From e4ce89aca9220b9054b98d6c3984edf69d52f132 Mon Sep 17 00:00:00 2001 From: timklge <2026103+timklge@users.noreply.github.com> Date: Fri, 7 Feb 2025 21:04:27 +0100 Subject: [PATCH] Replace trigger dropdown with fullscreen dialog (#26) * fix #24: Replace trigger dropdown with fullscreen dialog * Update button heights --- .../de/timklge/karooreminder/Dropdown.kt | 60 ----------- .../karooreminder/screens/DetailScreen.kt | 100 ++++++++++++------ app/src/main/res/drawable/volume.png | Bin 0 -> 11400 bytes 3 files changed, 65 insertions(+), 95 deletions(-) delete mode 100644 app/src/main/kotlin/de/timklge/karooreminder/Dropdown.kt create mode 100644 app/src/main/res/drawable/volume.png diff --git a/app/src/main/kotlin/de/timklge/karooreminder/Dropdown.kt b/app/src/main/kotlin/de/timklge/karooreminder/Dropdown.kt deleted file mode 100644 index 1880680..0000000 --- a/app/src/main/kotlin/de/timklge/karooreminder/Dropdown.kt +++ /dev/null @@ -1,60 +0,0 @@ -package de.timklge.karooreminder - -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExposedDropdownMenuBox -import androidx.compose.material3.ExposedDropdownMenuDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.MenuAnchorType -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier - - -data class DropdownOption(val id: String, val name: String) - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun Dropdown(label: String, options: List, selected: DropdownOption, onSelect: (selectedOption: DropdownOption) -> Unit) { - var expanded by remember { mutableStateOf(false) } - - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = it }, - ) { - OutlinedTextField( - readOnly = true, - value = selected.name, - onValueChange = { }, - modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryEditable, true).fillMaxWidth(), - label = { Text(label) }, - trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - expanded = expanded - ) - }, - colors = ExposedDropdownMenuDefaults.textFieldColors() - ) - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - ) { - options.forEach { option -> - DropdownMenuItem( - text = { Text(option.name, style = MaterialTheme.typography.bodyLarge) }, - onClick = { - expanded = false - onSelect(option) - }, - contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt b/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt index 664c9f3..4b2af7c 100644 --- a/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape @@ -45,6 +46,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -57,8 +59,6 @@ import com.maxkeppeler.sheets.color.models.ColorSelection import com.maxkeppeler.sheets.color.models.ColorSelectionMode import com.maxkeppeler.sheets.color.models.MultipleColors import com.maxkeppeler.sheets.color.models.SingleColor -import de.timklge.karooreminder.Dropdown -import de.timklge.karooreminder.DropdownOption import de.timklge.karooreminder.R import de.timklge.karooreminder.ReminderTrigger import de.timklge.karooreminder.streamUserProfile @@ -83,6 +83,7 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi var autoDismiss by remember { mutableStateOf(reminder.isAutoDismiss) } var deleteDialogVisible by remember { mutableStateOf(false) } var toneDialogVisible by remember { mutableStateOf(false) } + var triggerDialogVisible by remember { mutableStateOf(false) } var selectedTone by remember { mutableStateOf(reminder.tone) } var autoDismissSeconds by remember { mutableStateOf(reminder.autoDismissSeconds.toString()) } var selectedTrigger by remember { mutableStateOf(reminder.trigger) } @@ -108,31 +109,15 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi OutlinedTextField(value = text, onValueChange = { text = it }, label = { Text("Text") }, modifier = Modifier.fillMaxWidth(), singleLine = true) - apply { - val dropdownOptions = ReminderTrigger.entries.toList().map { unit -> DropdownOption(unit.id, unit.label) } - val dropdownInitialSelection by remember(selectedTrigger) { - mutableStateOf(dropdownOptions.find { option -> option.id == selectedTrigger.id }!!) - } - Dropdown(label = "Trigger", options = dropdownOptions, selected = dropdownInitialSelection) { selectedOption -> - val previousTrigger = selectedTrigger - selectedTrigger = ReminderTrigger.entries.find { entry -> entry.id == selectedOption.id }!! - - if (selectedTrigger != previousTrigger) { - duration = when (selectedTrigger) { - ReminderTrigger.ELAPSED_TIME -> 30.toString() - ReminderTrigger.DISTANCE -> 10.toString() - ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED -> 160.toString() - ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED -> 200.toString() - ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED -> 60.toString() - ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED -> 100.toString() - ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED -> 40.toString() - ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED -> 20.toString() - ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED -> 120.toString() - ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED -> 60.toString() - ReminderTrigger.ENERGY_OUTPUT -> 200.toString() - } - } - } + FilledTonalButton(modifier = Modifier + .fillMaxWidth() + .height(60.dp), + onClick = { + triggerDialogVisible = true + }) { + Icon(Icons.Default.Build, contentDescription = "Change Trigger", modifier = Modifier.size(20.dp)) + Spacer(modifier = Modifier.width(5.dp)) + Text("Trigger: ${selectedTrigger.label}") } OutlinedTextField(value = duration, modifier = Modifier.fillMaxWidth(), @@ -187,19 +172,20 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi ) FilledTonalButton(modifier = Modifier - .fillMaxWidth(), + .fillMaxWidth() + .height(60.dp), onClick = { colorDialogState.show() }) { Surface(shape = CircleShape, color = Color(ContextCompat.getColor(ctx, selectedColor?.colorRes ?: R.color.hRed)), modifier = Modifier - .height(40.dp) + .height(30.dp) .shadow(5.dp, CircleShape) - .width(40.dp), content = {}) + .width(30.dp), content = {}) Spacer(modifier = Modifier.width(5.dp)) - Text("Change Color") + Text("Color") } FilledTonalButton(modifier = Modifier @@ -208,9 +194,9 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi onClick = { toneDialogVisible = true }) { - Icon(Icons.Default.Build, contentDescription = "Change Tone") + Icon(painterResource(R.drawable.volume), contentDescription = "Tone", modifier = Modifier.size(20.dp)) Spacer(modifier = Modifier.width(5.dp)) - Text("Change Tone") + Text("Tone: ${selectedTone.displayName}") } Row(verticalAlignment = Alignment.CenterVertically) { @@ -280,6 +266,50 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi title = { Text("Delete reminder") }, text = { Text("Really delete this reminder?") }) } + if (triggerDialogVisible){ + Dialog(onDismissRequest = { triggerDialogVisible = false }) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(10.dp), + shape = RoundedCornerShape(10.dp), + ) { + Column(modifier = Modifier + .padding(5.dp) + .verticalScroll(rememberScrollState()) + .fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) { + + ReminderTrigger.entries.forEach { trigger -> + Row(modifier = Modifier + .fillMaxWidth() + .clickable { + selectedTrigger = trigger + triggerDialogVisible = false + }, verticalAlignment = Alignment.CenterVertically) { + RadioButton(selected = selectedTrigger == trigger, onClick = { + selectedTrigger = trigger + triggerDialogVisible = false + }) + + Column(modifier = Modifier.padding(start = 10.dp)) { + Text( + text = trigger.label, + ) + + if (trigger == ReminderTrigger.ENERGY_OUTPUT){ + Text( + text = "Powermeter required", + color = Color.Gray, + ) + } + } + } + } + } + } + } + } + if (toneDialogVisible){ Dialog(onDismissRequest = { toneDialogVisible = false }) { var dialogSelectedTone by remember { mutableStateOf(selectedTone) } @@ -287,8 +317,8 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi Card( modifier = Modifier .fillMaxWidth() - .padding(16.dp), - shape = RoundedCornerShape(16.dp), + .padding(10.dp), + shape = RoundedCornerShape(10.dp), ) { Column(modifier = Modifier .padding(5.dp) diff --git a/app/src/main/res/drawable/volume.png b/app/src/main/res/drawable/volume.png new file mode 100644 index 0000000000000000000000000000000000000000..88c783b5bb0d62daa124c0fb4dc85dcaa26cb950 GIT binary patch literal 11400 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