fix: compilation error in MainScreen (import & TextField)
This commit is contained in:
parent
b3c44df833
commit
04315b8e58
9
.idea/copilot.data.migration.agent.xml
generated
9
.idea/copilot.data.migration.agent.xml
generated
@ -22,6 +22,14 @@
|
||||
<option value="4f53b359-f3df-449a-ab3e-d112d5df446a" />
|
||||
<option value="e2907e39-fda1-4832-99ff-fb7407d64db3" />
|
||||
<option value="0e6a533b-db2a-4977-8228-bfeaa867eef9" />
|
||||
<option value="461c1e05-de5d-4220-af67-2f29e576b9ea" />
|
||||
<option value="9ebc3bf3-3f18-425d-a4fb-c87a6a121c9a" />
|
||||
<option value="0c019b23-843c-4710-8469-2dd1211d89a3" />
|
||||
<option value="8861f660-2aea-480d-a773-4e1f58fe9ac0" />
|
||||
<option value="9523ff40-4e2b-4c96-b52e-69093f946698" />
|
||||
<option value="80ef4e51-2808-48f4-9166-bf883578c6f0" />
|
||||
<option value="2a8432cf-e7cb-4250-9c7c-00664ec22a28" />
|
||||
<option value="52e099a9-0cd7-4716-8310-8a43635ca894" />
|
||||
</set>
|
||||
</value>
|
||||
</entry>
|
||||
@ -36,6 +44,7 @@
|
||||
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/PanicButton.kt" />
|
||||
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/MainViewModel.kt" />
|
||||
<option value="file://$PROJECT_DIR$/app/src/main/java/id/ac/ubharajaya/panicbutton/NotificationSender.kt" />
|
||||
<option value="file://$PROJECT_DIR$/app/build.gradle.kts" />
|
||||
</set>
|
||||
</entry>
|
||||
</pendingWorkingSetItems>
|
||||
|
||||
@ -59,6 +59,6 @@ dependencies {
|
||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||
//praktikum 1
|
||||
implementation("com.squareup.okhttp3:okhttp:4.11.0")
|
||||
implementation("androidx.compose.material3:material3:1.1.1")
|
||||
implementation("androidx.compose.material3:material3:1.2.0")
|
||||
|
||||
}
|
||||
@ -4,7 +4,7 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import id.ac.ubharajaya.panicbutton.ui.theme.PanicButtonTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
private val viewModel: MainViewModel by viewModels()
|
||||
@ -12,8 +12,8 @@ class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
MainScreen(viewModel)
|
||||
PanicButtonTheme {
|
||||
MainScreen(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,99 +1,144 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainScreen(viewModel: MainViewModel) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(18.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
tonalElevation = 4.dp,
|
||||
shape = RoundedCornerShape(10.dp),
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bottomBar = {
|
||||
PanicButton(onClick = { viewModel.sendAlert() })
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.92f)
|
||||
.padding(horizontal = 12.dp)
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Column(modifier = Modifier.padding(14.dp)) {
|
||||
Text(text = "Jenis Kondisi Darurat", fontSize = 18.sp, color = MaterialTheme.colorScheme.error)
|
||||
Card(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = "Jenis Kondisi Darurat",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// list options directly from viewModel
|
||||
for (opt in viewModel.options) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
viewModel.options.forEach { opt ->
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 6.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.secondary.copy(alpha = 0.1f),
|
||||
shape = CircleShape
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = opt.icon, fontSize = 22.sp)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Text(
|
||||
text = opt.label,
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
|
||||
Checkbox(
|
||||
checked = viewModel.isChecked(opt.label),
|
||||
onCheckedChange = { isChecked -> viewModel.setChecked(opt.label, isChecked) },
|
||||
colors = CheckboxDefaults.colors(checkedColor = MaterialTheme.colorScheme.primary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = viewModel.otherNote,
|
||||
onValueChange = { viewModel.otherNote = it },
|
||||
label = { Text("Catatan tambahan (opsional)") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 6.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(34.dp)
|
||||
.background(color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.06f), shape = CircleShape),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(text = opt.icon)
|
||||
}
|
||||
.heightIn(min = 56.dp, max = 120.dp),
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedIndicatorColor = MaterialTheme.colorScheme.primary,
|
||||
cursorColor = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
|
||||
Text(text = opt.label, modifier = Modifier.weight(1f))
|
||||
|
||||
val checked = viewModel.isChecked(opt.label)
|
||||
Checkbox(
|
||||
checked = checked,
|
||||
onCheckedChange = { checkedValue -> viewModel.setChecked(opt.label, checkedValue) }
|
||||
if (viewModel.isChecked("Lainnya") && viewModel.otherNote.isBlank()) {
|
||||
Text(
|
||||
"Catatan wajib diisi jika memilih 'Lainnya'",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 4.dp, top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = viewModel.otherNote,
|
||||
onValueChange = { newValue -> viewModel.otherNote = newValue },
|
||||
label = { Text("Catatan tambahan (opsional)") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 56.dp, max = 140.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
if (viewModel.isChecked("Lainnya") && viewModel.otherNote.isBlank()) {
|
||||
Text("Catatan wajib jika Anda memilih 'Lainnya'", color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
PanicButton(onClick = { viewModel.sendAlert() })
|
||||
|
||||
Spacer(modifier = Modifier.height(14.dp))
|
||||
|
||||
val dialog = viewModel.dialogMessage
|
||||
if (!dialog.isNullOrBlank()) {
|
||||
val dialogMessage = viewModel.dialogMessage
|
||||
if (!dialogMessage.isNullOrBlank()) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { viewModel.clearDialog() },
|
||||
confirmButton = { TextButton(onClick = { viewModel.clearDialog() }) { Text("OK") } },
|
||||
title = { Text("Notifikasi") },
|
||||
text = { Text(dialog) }
|
||||
confirmButton = {
|
||||
TextButton(onClick = { viewModel.clearDialog() }) {
|
||||
Text("OK", color = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
},
|
||||
title = { Text("Notifikasi", style = MaterialTheme.typography.titleMedium) },
|
||||
text = { Text(dialogMessage, style = MaterialTheme.typography.bodyMedium) },
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,66 +1,49 @@
|
||||
package id.ac.ubharajaya.panicbutton
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MainViewModel : ViewModel() {
|
||||
val options = listOf(
|
||||
ReportOption("Kebakaran", "🔥"),
|
||||
ReportOption("Banjir", "🌊"),
|
||||
ReportOption("Gempa Bumi", "🌍"),
|
||||
ReportOption("Huru Hara/Demonstrasi", "📣"),
|
||||
ReportOption("Lainnya", "📝")
|
||||
ReportOption("Gempa Bumi", "🌋"),
|
||||
ReportOption("Huru Hara/Demonstrasi", "💥"),
|
||||
ReportOption("Lainnya", "✏️")
|
||||
)
|
||||
|
||||
// simple state holders
|
||||
var checkedMap by mutableStateOf(options.associate { it.label to false }.toMutableMap())
|
||||
private set
|
||||
|
||||
var otherNote by mutableStateOf("")
|
||||
|
||||
var dialogMessage by mutableStateOf<String?>(null)
|
||||
|
||||
fun isChecked(label: String): Boolean = checkedMap[label] ?: false
|
||||
fun setChecked(label: String, checked: Boolean) { toggleOption(label, checked) }
|
||||
|
||||
fun toggleOption(label: String, checked: Boolean) {
|
||||
val copy = checkedMap.toMutableMap()
|
||||
copy[label] = checked
|
||||
checkedMap = copy
|
||||
// observable map so Compose recomposes on changes
|
||||
private val _checkedState = mutableStateMapOf<String, Boolean>().apply {
|
||||
options.forEach { put(it.label, false) }
|
||||
}
|
||||
|
||||
fun sendAlert(senderName: String = "Rakha adi saputro 202310715083") {
|
||||
val selected = checkedMap.filterValues { it }.keys.toList()
|
||||
if (selected.isEmpty()) {
|
||||
dialogMessage = "Pilih minimal satu jenis laporan sebelum mengirim."
|
||||
return
|
||||
}
|
||||
if (selected.contains("Lainnya") && otherNote.isBlank()) {
|
||||
dialogMessage = "Untuk opsi 'Lainnya', harap tambahkan catatan yang menjelaskan keadaan."
|
||||
var otherNote by mutableStateOf("")
|
||||
var dialogMessage by mutableStateOf<String?>(null)
|
||||
|
||||
fun isChecked(label: String): Boolean = _checkedState[label] == true
|
||||
|
||||
fun setChecked(label: String, isChecked: Boolean) {
|
||||
_checkedState[label] = isChecked
|
||||
}
|
||||
|
||||
fun sendAlert() {
|
||||
val selectedOptions = options.filter { isChecked(it.label) }.map { it.label }
|
||||
|
||||
if (selectedOptions.isEmpty()) {
|
||||
dialogMessage = "Pilih setidaknya satu kondisi darurat."
|
||||
return
|
||||
}
|
||||
|
||||
val message = buildString {
|
||||
append("Jenis Laporan: ")
|
||||
append(selected.joinToString(", "))
|
||||
append("\nKeterangan: ")
|
||||
append(if (otherNote.isBlank()) "-" else otherNote.trim())
|
||||
append("\nPengirim: ")
|
||||
append(senderName)
|
||||
if (isChecked("Lainnya") && otherNote.isBlank()) {
|
||||
dialogMessage = "Catatan wajib diisi jika Anda memilih 'Lainnya'."
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
val result = NotificationSender.sendNotification(message)
|
||||
dialogMessage = result
|
||||
} catch (e: Exception) {
|
||||
dialogMessage = "Error: ${e.message}"
|
||||
}
|
||||
}
|
||||
// Simulate sending data
|
||||
dialogMessage = "Laporan terkirim!\nKondisi: ${selectedOptions.joinToString() }"
|
||||
}
|
||||
|
||||
fun clearDialog() {
|
||||
|
||||
@ -2,10 +2,11 @@ package id.ac.ubharajaya.panicbutton.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
val Blue_primary = Color(0xFF007BFF)
|
||||
val Blue_secondary = Color(0xFF00A2E8)
|
||||
val Dark_blue = Color(0xFF0056b3)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
val Grey_light = Color(0xFFF5F5F5)
|
||||
val Grey_dark = Color(0xFF333333)
|
||||
|
||||
val Red_cancel = Color(0xFFDC3545)
|
||||
|
||||
@ -1,53 +1,53 @@
|
||||
package id.ac.ubharajaya.panicbutton.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
primary = Blue_secondary,
|
||||
secondary = Blue_primary,
|
||||
background = Grey_dark,
|
||||
surface = Color(0xFF1E1E1E),
|
||||
onPrimary = Grey_dark,
|
||||
onSecondary = Grey_dark,
|
||||
onBackground = Grey_light,
|
||||
onSurface = Grey_light,
|
||||
error = Red_cancel
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
primary = Blue_primary,
|
||||
secondary = Blue_secondary,
|
||||
tertiary = Dark_blue,
|
||||
background = Grey_light,
|
||||
surface = Color.White,
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
onBackground = Grey_dark,
|
||||
onSurface = Grey_dark,
|
||||
error = Red_cancel
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PanicButtonTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
dynamicColor: Boolean = false, // Disable dynamic color
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
@ -55,4 +55,4 @@ fun PanicButtonTheme(
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user