Feat(UI): Style panic button

This commit is contained in:
Rakha adi 2025-11-19 20:46:01 +07:00
parent 9d34345d24
commit ba36f0447b

View File

@ -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 {