diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml new file mode 100644 index 0000000..40c729a --- /dev/null +++ b/.idea/copilot.data.migration.agent.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt b/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt index 12835ec..6256157 100644 --- a/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt +++ b/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt @@ -4,13 +4,14 @@ import android.os.Bundle import androidx.activity.ComponentActivity 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.animation.core.animateFloatAsState 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.selection.toggleable import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* @@ -45,6 +46,11 @@ fun MyApp() { // dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil var dialogMessage by remember { mutableStateOf(null) } + // Report options (checkbox-style) + val reportOptions = listOf("Kebakaran", "Banjir", "Gempa Bumi", "Huru Hara/Demostrasi", "Lainnya") + val checkedMap = remember { mutableStateMapOf().apply { reportOptions.forEach { put(it, false) } } } + var otherNote by remember { mutableStateOf("") } + // UI Column( modifier = Modifier @@ -53,76 +59,157 @@ fun MyApp() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - // tombol panic - PanicButton(onClick = { - // Kirim HTTP request saat tombol ditekan - sendNotification { response -> - // tampilkan dialog ketika ada respon - dialogMessage = response - } - }) - } + // Checklist area similar to the wireframe + Surface( + tonalElevation = 2.dp, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 24.dp) + ) { + Column(modifier = Modifier.padding(12.dp)) { + Text(text = "Terjadi Kondisi Darurat", fontWeight = FontWeight.SemiBold) + Spacer(modifier = Modifier.height(8.dp)) - // 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( + // Each option as a row with checkbox + reportOptions.forEach { option -> + Row( + verticalAlignment = Alignment.CenterVertically, 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 + .fillMaxWidth() + .padding(vertical = 4.dp) + .toggleable( + value = checkedMap[option] ?: false, + onValueChange = { checked -> checkedMap[option] = checked } ) - } - - Spacer(modifier = Modifier.height(12.dp)) - - // message - Text( - text = dialogMessage ?: "", - color = Color.Black, - modifier = Modifier.padding(horizontal = 16.dp) + ) { + Checkbox( + checked = checkedMap[option] ?: false, + onCheckedChange = { checked -> checkedMap[option] = checked } ) + Spacer(modifier = Modifier.width(8.dp)) + Text(text = option) + } + } - Spacer(modifier = Modifier.height(20.dp)) + // Additional notes field — show always (wireframe shows a note field) + Spacer(modifier = Modifier.height(8.dp)) + OutlinedTextField( + value = otherNote, + onValueChange = { otherNote = it }, + label = { Text("Catatan tambahan (opsional)") }, + placeholder = { Text("Catatan tambahan ...") }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + ) - // action - Box( + Spacer(modifier = Modifier.height(8.dp)) + + // small hint when Lainnya is checked but note empty + if ((checkedMap["Lainnya"] == true) && otherNote.isBlank()) { + Text( + text = "Catatan wajib jika Anda memilih 'Lainnya'", + color = Color(0xFFB71C1C), + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(top = 6.dp) + ) + } + } + } + + Spacer(modifier = Modifier.height(18.dp)) + + // Keep the existing nice PanicButton unchanged + PanicButton(onClick = { + // Validation: at least one option selected + val selected = reportOptions.filter { checkedMap[it] == true } + if (selected.isEmpty()) { + dialogMessage = "Pilih minimal satu jenis laporan sebelum mengirim." + return@PanicButton + } + if (selected.contains("Lainnya") && otherNote.isBlank()) { + dialogMessage = "Untuk opsi 'Lainnya', harap tambahkan catatan yang menjelaskan keadaan." + return@PanicButton + } + + // build message: list selected options and the note + val message = buildString { + append("Jenis Laporan: ") + append(selected.joinToString(", ")) + append("\n") + append("Keterangan: ") + if (selected.contains("Lainnya")) append(otherNote.trim()) else append("-") + append("\n") + append("Pengirim: Satrio Putra Wardani 202310715307") + } + + sendNotification(message) { response -> dialogMessage = response } + }) + + Spacer(modifier = Modifier.height(16.dp)) + + // dialog hasil + 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 - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 12.dp), - contentAlignment = Alignment.Center + .padding(0.dp) + .widthIn(min = 280.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - Button( - onClick = { dialogMessage = null }, - colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB71C1C)) + // header + Box( + modifier = Modifier + .fillMaxWidth() + .background(color = Color(0xFFB71C1C)) + .padding(vertical = 12.dp), + contentAlignment = Alignment.Center ) { - Text(text = "OK", color = Color.White) + 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) + } } } } @@ -206,34 +293,18 @@ fun PanicButton( ) } } - - 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) - ) - } } } -// Fungsi untuk mengirimkan HTTP request -fun sendNotification(onResult: (String) -> Unit) { +// Fungsi untuk mengirimkan HTTP request (mengikuti format pesan yang lebih rapi) +fun sendNotification(message: String, onResult: (String) -> Unit) { val client = OkHttpClient() val url = "https://ntfy.ubharajaya.ac.id/panic-button" // Ganti dengan topik Anda - val requestBody = "Notifikasi dari Satrio Putra Wardani 202310715307".toRequestBody( + val requestBody = message.toRequestBody( "text/plain".toMediaType() )