feat(ui): add checklist report types and 3D panic button; include notes/validation and formatted notification
This commit is contained in:
parent
ba36f0447b
commit
97d37a43ad
29
.idea/copilot.data.migration.agent.xml
generated
Normal file
29
.idea/copilot.data.migration.agent.xml
generated
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AgentMigrationStateService">
|
||||||
|
<option name="pendingSessionIds">
|
||||||
|
<option value="bf5061b4-a1e9-4600-a273-ea8e51b2ce89" />
|
||||||
|
</option>
|
||||||
|
<option name="pendingTurns">
|
||||||
|
<map>
|
||||||
|
<entry key="bf5061b4-a1e9-4600-a273-ea8e51b2ce89">
|
||||||
|
<value>
|
||||||
|
<set>
|
||||||
|
<option value="6dffb37e-ecb0-4c89-8cbe-e38ed347c397" />
|
||||||
|
<option value="f607f271-4885-4c4e-91da-e2caf2abd67a" />
|
||||||
|
<option value="39a0d9c4-a68f-4050-bb14-eda17dc695db" />
|
||||||
|
<option value="94d90579-6a6d-44a9-8b93-de694c0c38ef" />
|
||||||
|
</set>
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
<pendingWorkingSetItems>
|
||||||
|
<entry key="bf5061b4-a1e9-4600-a273-ea8e51b2ce89">
|
||||||
|
<set>
|
||||||
|
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt" />
|
||||||
|
</set>
|
||||||
|
</entry>
|
||||||
|
</pendingWorkingSetItems>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -4,13 +4,14 @@ 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.tween
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.selection.toggleable
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@ -45,6 +46,11 @@ fun MyApp() {
|
|||||||
// dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil
|
// dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil
|
||||||
var dialogMessage by remember { mutableStateOf<String?>(null) }
|
var dialogMessage by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
// Report options (checkbox-style)
|
||||||
|
val reportOptions = listOf("Kebakaran", "Banjir", "Gempa Bumi", "Huru Hara/Demostrasi", "Lainnya")
|
||||||
|
val checkedMap = remember { mutableStateMapOf<String, Boolean>().apply { reportOptions.forEach { put(it, false) } } }
|
||||||
|
var otherNote by remember { mutableStateOf("") }
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -53,76 +59,157 @@ fun MyApp() {
|
|||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
// tombol panic
|
// Checklist area similar to the wireframe
|
||||||
PanicButton(onClick = {
|
Surface(
|
||||||
// Kirim HTTP request saat tombol ditekan
|
tonalElevation = 2.dp,
|
||||||
sendNotification { response ->
|
shape = RoundedCornerShape(8.dp),
|
||||||
// tampilkan dialog ketika ada respon
|
modifier = Modifier
|
||||||
dialogMessage = response
|
.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
|
// Each option as a row with checkbox
|
||||||
if (dialogMessage != null) {
|
reportOptions.forEach { option ->
|
||||||
Dialog(onDismissRequest = { dialogMessage = null }) {
|
Row(
|
||||||
Box(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
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
|
modifier = Modifier
|
||||||
.padding(0.dp)
|
.fillMaxWidth()
|
||||||
.widthIn(min = 280.dp),
|
.padding(vertical = 4.dp)
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
.toggleable(
|
||||||
) {
|
value = checkedMap[option] ?: false,
|
||||||
// header
|
onValueChange = { checked -> checkedMap[option] = checked }
|
||||||
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
|
|
||||||
)
|
)
|
||||||
}
|
) {
|
||||||
|
Checkbox(
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
checked = checkedMap[option] ?: false,
|
||||||
|
onCheckedChange = { checked -> checkedMap[option] = checked }
|
||||||
// message
|
|
||||||
Text(
|
|
||||||
text = dialogMessage ?: "",
|
|
||||||
color = Color.Black,
|
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
|
||||||
)
|
)
|
||||||
|
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
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
Box(
|
|
||||||
|
// 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
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(0.dp)
|
||||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
.widthIn(min = 280.dp),
|
||||||
contentAlignment = Alignment.Center
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Button(
|
// header
|
||||||
onClick = { dialogMessage = null },
|
Box(
|
||||||
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB71C1C))
|
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
|
// Fungsi untuk mengirimkan HTTP request (mengikuti format pesan yang lebih rapi)
|
||||||
fun sendNotification(onResult: (String) -> Unit) {
|
fun sendNotification(message: String, onResult: (String) -> Unit) {
|
||||||
val client = OkHttpClient()
|
val client = OkHttpClient()
|
||||||
val url = "https://ntfy.ubharajaya.ac.id/panic-button" // Ganti <your-topic> dengan topik Anda
|
val url = "https://ntfy.ubharajaya.ac.id/panic-button" // Ganti <your-topic> dengan topik Anda
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val requestBody = "Notifikasi dari Satrio Putra Wardani 202310715307".toRequestBody(
|
val requestBody = message.toRequestBody(
|
||||||
"text/plain".toMediaType()
|
"text/plain".toMediaType()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user