Feat(UI): Style panic button
This commit is contained in:
parent
9d34345d24
commit
ba36f0447b
@ -3,17 +3,32 @@ package id.ac.ubharajaya.panicbutton
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.scale
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -27,9 +42,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MyApp() {
|
fun MyApp() {
|
||||||
// State untuk menampilkan hasil request
|
// dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil
|
||||||
var message by remember { mutableStateOf("Klik tombol untuk mengirim notifikasi") }
|
var dialogMessage by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
Column(
|
Column(
|
||||||
@ -39,14 +53,174 @@ fun MyApp() {
|
|||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(text = message, Modifier.padding(bottom = 16.dp))
|
// tombol panic
|
||||||
Button(onClick = {
|
PanicButton(onClick = {
|
||||||
// Kirim HTTP request saat tombol ditekan
|
// Kirim HTTP request saat tombol ditekan
|
||||||
sendNotification { response ->
|
sendNotification { response ->
|
||||||
message = response
|
// tampilkan dialog ketika ada respon
|
||||||
|
dialogMessage = response
|
||||||
}
|
}
|
||||||
}) {
|
})
|
||||||
Text(text = "Kirim Alert")
|
}
|
||||||
|
|
||||||
|
// Custom Dialog untuk menampilkan status kirim notifikasi — styled sesuai tema panic
|
||||||
|
if (dialogMessage != null) {
|
||||||
|
Dialog(onDismissRequest = { dialogMessage = null }) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(24.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(12.dp),
|
||||||
|
color = Color.White,
|
||||||
|
tonalElevation = 8.dp,
|
||||||
|
modifier = Modifier.wrapContentWidth()
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(0.dp)
|
||||||
|
.widthIn(min = 280.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
// header
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(color = Color(0xFFB71C1C))
|
||||||
|
.padding(vertical = 12.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Notifikasi",
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontSize = 18.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
// message
|
||||||
|
Text(
|
||||||
|
text = dialogMessage ?: "",
|
||||||
|
color = Color.Black,
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
|
||||||
|
// action
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = { dialogMessage = null },
|
||||||
|
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB71C1C))
|
||||||
|
) {
|
||||||
|
Text(text = "OK", color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// New: 3D-styled PanicButton with press animation and glossy highlight
|
||||||
|
@Composable
|
||||||
|
fun PanicButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
// theme colors
|
||||||
|
val panicColor = Color(0xFFB71C1C)
|
||||||
|
val darkShade = Color(0xFF7F0F0F)
|
||||||
|
val lightAccent = Color(0xFFFF8A80)
|
||||||
|
|
||||||
|
// interaction for press-state
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val isPressed by interactionSource.collectIsPressedAsState()
|
||||||
|
|
||||||
|
// animations: scale down and lower elevation when pressed
|
||||||
|
val scaleAnim by animateFloatAsState(targetValue = if (isPressed) 0.96f else 1f, animationSpec = tween(120))
|
||||||
|
val elevationAnim by animateDpAsState(targetValue = if (isPressed) 6.dp else 18.dp, animationSpec = tween(120))
|
||||||
|
|
||||||
|
// vertical gradient to simulate 3D lighting
|
||||||
|
val gradient = Brush.verticalGradient(listOf(lightAccent, panicColor, darkShade))
|
||||||
|
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Box(contentAlignment = Alignment.Center) {
|
||||||
|
// soft outer shadow (ground shadow)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(210.dp)
|
||||||
|
.shadow(elevation = elevationAnim, shape = CircleShape)
|
||||||
|
.background(color = Color.Black.copy(alpha = 0.12f), shape = CircleShape)
|
||||||
|
)
|
||||||
|
|
||||||
|
// main 3D button
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(170.dp)
|
||||||
|
.scale(scaleAnim)
|
||||||
|
.shadow(elevation = elevationAnim, shape = CircleShape)
|
||||||
|
.background(brush = gradient, shape = CircleShape)
|
||||||
|
.clickable(indication = null, interactionSource = interactionSource) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
// glossy highlight (small white circle offset to top-left)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(70.dp)
|
||||||
|
.offset(x = (-24).dp, y = (-28).dp)
|
||||||
|
.background(color = Color.White.copy(alpha = 0.16f), shape = CircleShape)
|
||||||
|
)
|
||||||
|
|
||||||
|
// central exclamation icon
|
||||||
|
Text(
|
||||||
|
text = "!",
|
||||||
|
color = Color.White,
|
||||||
|
fontSize = 72.sp,
|
||||||
|
fontWeight = FontWeight.ExtraBold
|
||||||
|
)
|
||||||
|
|
||||||
|
// subtle inner rim to enhance 3D edge
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.padding(6.dp)
|
||||||
|
.background(color = Color.Transparent, shape = CircleShape)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(14.dp))
|
||||||
|
|
||||||
|
// label with 3D-like chip
|
||||||
|
Surface(
|
||||||
|
color = panicColor.copy(alpha = 0.10f),
|
||||||
|
shape = RoundedCornerShape(20.dp),
|
||||||
|
tonalElevation = 0.dp
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "PANIC",
|
||||||
|
color = panicColor,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 20.dp, vertical = 8.dp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,10 +233,8 @@ fun sendNotification(onResult: (String) -> Unit) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
val requestBody = "Notifikasi dari Satrio Putra Wardani 202310715307".toRequestBody(
|
||||||
val requestBody = RequestBody.create(
|
"text/plain".toMediaType()
|
||||||
"text/plain".toMediaType(), // Mengirim plain text
|
|
||||||
"Notifikasi dari Satrio Putra Wardani 202310715307" // Pesan yang akan tampil
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -76,6 +248,7 @@ fun sendNotification(onResult: (String) -> Unit) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Eksekusi request di thread terpisah
|
// Eksekusi request di thread terpisah
|
||||||
Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user