diff --git a/.idea/copilot.data.migration.agent.xml b/.idea/copilot.data.migration.agent.xml index 6a251c8..57556ac 100644 --- a/.idea/copilot.data.migration.agent.xml +++ b/.idea/copilot.data.migration.agent.xml @@ -10,9 +10,11 @@ + @@ -57,6 +64,9 @@ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6e1b3d2..88cd9da 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -50,6 +50,8 @@ dependencies { implementation(libs.androidx.compose.ui.graphics) implementation(libs.androidx.compose.ui.tooling.preview) implementation(libs.androidx.compose.material3) + // Foundation (for verticalScroll, rememberScrollState, etc.) + implementation("androidx.compose.foundation:foundation:1.5.0") testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2e0d78f..5094eae 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -22,6 +22,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapDetailActivity.kt b/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapDetailActivity.kt new file mode 100644 index 0000000..fb77dff --- /dev/null +++ b/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapDetailActivity.kt @@ -0,0 +1,34 @@ +package id.ac.ubharajaya.panicbutton + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp + +class EvacuationMapDetailActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val which = intent.getStringExtra("which") ?: "selatan" + val resId = if (which == "selatan") R.drawable.lantai_1_selatan else R.drawable.lantai_1_utara + setContent { + MaterialTheme { + EvacuationMapDetailContent(resId) + } + } + } +} + +@Composable +fun EvacuationMapDetailContent(drawableResId: Int) { + Column(modifier = Modifier.fillMaxSize().padding(12.dp)) { + Text(text = "Detail Peta", modifier = Modifier.padding(bottom = 12.dp)) + Image(painter = painterResource(id = drawableResId), contentDescription = "Map Detail", modifier = Modifier.fillMaxWidth().heightIn(max = 800.dp)) + } +} diff --git a/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapsActivity.kt b/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapsActivity.kt new file mode 100644 index 0000000..6ba6bee --- /dev/null +++ b/app/src/main/java/id/ac/ubharajaya/panicbutton/EvacuationMapsActivity.kt @@ -0,0 +1,68 @@ +package id.ac.ubharajaya.panicbutton + +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +class EvacuationMapsActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + MaterialTheme { + EvacuationMapsContent(onSelect = { which -> + val intent = Intent(this, EvacuationMapDetailActivity::class.java) + intent.putExtra("which", which) + startActivity(intent) + }) + } + } + } +} + +@Composable +fun EvacuationMapsContent(onSelect: (String) -> Unit) { + Column(modifier = Modifier.fillMaxSize().padding(16.dp)) { + Text(text = "Peta Jalur Evakuasi", style = MaterialTheme.typography.titleLarge, modifier = Modifier.padding(bottom = 12.dp)) + + // Selatan + Card(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).clickable { onSelect("selatan") }, shape = RoundedCornerShape(12.dp), elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(12.dp)) { + Image(painter = painterResource(id = R.drawable.lantai_1_selatan), contentDescription = "Selatan", modifier = Modifier.size(120.dp)) + Spacer(modifier = Modifier.width(12.dp)) + Column { + Text(text = "Jalur Evakuasi Selatan", fontSize = 18.sp) + Spacer(modifier = Modifier.height(6.dp)) + Text(text = "Lihat detail jalur evakuasi lantai selatan.") + } + } + } + + // Utara + Card(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp).clickable { onSelect("utara") }, shape = RoundedCornerShape(12.dp), elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(12.dp)) { + Image(painter = painterResource(id = R.drawable.lantai_1_utara), contentDescription = "Utara", modifier = Modifier.size(120.dp)) + Spacer(modifier = Modifier.width(12.dp)) + Column { + Text(text = "Jalur Evakuasi Utara", fontSize = 18.sp) + Spacer(modifier = Modifier.height(6.dp)) + Text(text = "Lihat detail jalur evakuasi lantai utara.") + } + } + } + } +} 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 8eb8aad..61c2fba 100644 --- a/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt +++ b/app/src/main/java/id/ac/ubharajaya/panicbutton/MainActivity.kt @@ -1,51 +1,26 @@ package id.ac.ubharajaya.panicbutton +import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.viewModels -import androidx.compose.runtime.Composable -import androidx.navigation.NavType -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable -import androidx.navigation.compose.navArgument -import androidx.navigation.compose.rememberNavController -import id.ac.ubharajaya.panicbutton.ui.theme.PanicButtonTheme +import androidx.lifecycle.ViewModelProvider +import androidx.compose.material3.MaterialTheme class MainActivity : ComponentActivity() { - private val viewModel: MainViewModel by viewModels() + private lateinit var viewModel: MainViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { - PanicButtonTheme { - AppNav(viewModel = viewModel) - } - } - } -} + viewModel = ViewModelProvider(this).get(MainViewModel::class.java) -@Composable -fun AppNav(viewModel: MainViewModel) { - val navController = rememberNavController() - NavHost(navController = navController, startDestination = "main") { - composable("main") { - MainScreen(viewModel = viewModel, onOpenEvacMaps = { navController.navigate("evac_maps") }) + val openEvacMaps = { + startActivity(Intent(this, EvacuationMapsActivity::class.java)) } - composable("evac_maps") { - EvacuationMapsScreen(navController = navController, onBack = { navController.popBackStack() }) - } - composable( - route = "evac_map/{resName}", - arguments = listOf(navArgument("resName") { type = NavType.StringType }) - ) { backStackEntry -> - val resName = backStackEntry.arguments?.getString("resName") ?: "" - val resId = when (resName) { - "selatan" -> R.drawable.lantai_1_selatan - "utara" -> R.drawable.lantai_1_utara - else -> 0 + setContent { + MaterialTheme { + MainScreen(viewModel = viewModel, onOpenEvacMaps = openEvacMaps) } - EvacuationMapDetailScreen(drawableResId = resId, onBack = { navController.popBackStack() }) } } } diff --git a/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt b/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt index 3702785..13422c9 100644 --- a/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt +++ b/app/src/main/java/id/ac/ubharajaya/panicbutton/MainScreen.kt @@ -1,37 +1,37 @@ package id.ac.ubharajaya.panicbutton import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Warning import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material.icons.filled.KeyboardArrowUp -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Close import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.graphics.Brush +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import androidx.compose.ui.layout.IntrinsicSize +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.foundation.layout.IntrinsicSize @OptIn(ExperimentalMaterial3Api::class) @Composable fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit) { - Scaffold( modifier = Modifier.fillMaxSize(), bottomBar = { - // wrap PanicButton in a Box so we can center it over the bottom bar Box( modifier = Modifier .fillMaxWidth() @@ -41,73 +41,65 @@ fun MainScreen(viewModel: MainViewModel, onOpenEvacMaps: () -> Unit) { PanicButton(onClick = { viewModel.sendAlert() }, buttonSize = 130.dp, shadowSize = 160.dp) } } - ) { paddingValues -> - + ) { padding -> Column( modifier = Modifier .fillMaxSize() - .padding(paddingValues) + .padding(padding) .padding(20.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - - // Emergency instruction text (darurat) + // Emergency instruction Text( - text = "JANGAN PANIK, SEGERA EVAKUASI DIRI ANDA KE TITIK KUMPUL", + text = "JANGAN PANIK, SEGERA EVAKUASI DIRI ANDA KE TITIK KUMPUL!!", color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold), + style = MaterialTheme.typography.bodyLarge.copy( + fontWeight = FontWeight.Bold + ), + textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() - .padding(bottom = 12.dp), + .padding(bottom = 12.dp) + .background( + MaterialTheme.colorScheme.error.copy(alpha = 0.2f), + shape = RoundedCornerShape(8.dp) + ) + .border( + width = 1.dp, + color = MaterialTheme.colorScheme.error, + shape = RoundedCornerShape(8.dp) + ) + .padding(12.dp), maxLines = 2, ) - // Styled Emergency Condition Card EmergencyConditionCard(viewModel) - Spacer(Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(12.dp)) - // Button to open evacuation maps Button( onClick = onOpenEvacMaps, modifier = Modifier .fillMaxWidth(0.6f) .height(48.dp), - shape = RoundedCornerShape(12.dp), - colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary) + shape = RoundedCornerShape(12.dp) ) { - Text(text = "Lihat Peta Evakuasi", color = MaterialTheme.colorScheme.onPrimary) + Text(text = "Lihat Peta Evakuasi") } - // Tambahkan spacer di luar Card agar ada jarak dari bottom bar - Spacer(Modifier.height(20.dp)) + Spacer(modifier = Modifier.height(20.dp)) } - // Dialog Feedback + // Dialog val dialogMessage = viewModel.dialogMessage if (!dialogMessage.isNullOrBlank()) { AlertDialog( onDismissRequest = { viewModel.clearDialog() }, confirmButton = { - TextButton(onClick = { viewModel.clearDialog() }) { - Text("OK") - } + TextButton(onClick = { viewModel.clearDialog() }) { Text("OK") } }, - title = { - Text( - "Notifikasi", - style = MaterialTheme.typography.titleMedium.copy( - fontWeight = FontWeight.Bold - ) - ) - }, - text = { - Text( - dialogMessage, - style = MaterialTheme.typography.bodyMedium - ) - }, - containerColor = MaterialTheme.colorScheme.surface + title = { Text("🚨 Notifikasi", style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)) }, + text = { Text(dialogMessage ?: "") } ) } } @@ -231,8 +223,8 @@ fun EmergencyConditionCard(viewModel: MainViewModel) { ) // Show first 2 selected items as chips - selectedItems.take(2).forEach { option -> - SelectedChip(option = option, viewModel = viewModel) + selectedItems.take(2).forEach { opt -> + SelectedChip(option = opt, viewModel = viewModel) } if (selectedItems.size > 2) { @@ -250,7 +242,7 @@ fun EmergencyConditionCard(viewModel: MainViewModel) { expanded = expanded, onDismissRequest = { expanded = false }, modifier = Modifier - .width(IntrinsicSize.Max) + .fillMaxWidth() .background( color = MaterialTheme.colorScheme.surface, shape = RoundedCornerShape(16.dp) @@ -263,12 +255,12 @@ fun EmergencyConditionCard(viewModel: MainViewModel) { .heightIn(max = 280.dp) .verticalScroll(rememberScrollState()) ) { - viewModel.options.forEach { option -> + viewModel.options.forEach { opt -> EmergencyOptionItem( - option = option, - isChecked = viewModel.isChecked(option.label), + option = opt, + isChecked = viewModel.isChecked(opt.label), onCheckedChange = { checked -> - viewModel.setChecked(option.label, checked) + viewModel.setChecked(opt.label, checked) } ) }