From f0a16b859447d140895e1945e9c22c4b0a6b78a7 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sun, 24 Mar 2024 17:56:40 +0530 Subject: [PATCH] Fix: Alarm Settings layout --- .../com/bnyro/clock/ui/components/AlarmRow.kt | 11 - .../clock/ui/components/AlarmSettingsSheet.kt | 292 ++++++++++-------- .../clock/ui/components/AlarmTimePicker.kt | 4 +- .../com/bnyro/clock/ui/model/AlarmModel.kt | 15 + 4 files changed, 174 insertions(+), 148 deletions(-) diff --git a/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt b/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt index c125b307..ebe999fa 100644 --- a/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt +++ b/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt @@ -141,17 +141,6 @@ fun AlarmRow(alarm: Alarm, alarmModel: AlarmModel) { onCheckedChange = { newValue -> alarm.enabled = newValue isEnabled = newValue - - if (isEnabled) { - val millisRemainingForAlarm = (AlarmHelper.getAlarmTime(alarm) - System.currentTimeMillis()) - val formattedDuration = TimeHelper.durationToFormatted(context, millisRemainingForAlarm.milliseconds) - Toast.makeText( - context, - context.resources.getString(R.string.alarm_will_play, formattedDuration), - Toast.LENGTH_SHORT - ).show() - } - alarmModel.updateAlarm(context, alarm) } ) diff --git a/app/src/main/java/com/bnyro/clock/ui/components/AlarmSettingsSheet.kt b/app/src/main/java/com/bnyro/clock/ui/components/AlarmSettingsSheet.kt index e71ded89..8af6f151 100644 --- a/app/src/main/java/com/bnyro/clock/ui/components/AlarmSettingsSheet.kt +++ b/app/src/main/java/com/bnyro/clock/ui/components/AlarmSettingsSheet.kt @@ -12,7 +12,9 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Label import androidx.compose.material.icons.rounded.Alarm @@ -24,9 +26,12 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -37,9 +42,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.compose.ui.window.DialogWindowProvider import com.bnyro.clock.R import com.bnyro.clock.obj.Alarm import com.bnyro.clock.ui.common.SwitchItem @@ -53,7 +62,6 @@ import com.bnyro.clock.util.TimeHelper @Composable fun AlarmSettingsSheet(onDismissRequest: () -> Unit, currentAlarm: Alarm, onSave: (Alarm) -> Unit) { val context = LocalContext.current - val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) var showRingtoneDialog by remember { mutableStateOf(false) } var showSnoozeDialog by remember { mutableStateOf(false) } @@ -72,154 +80,168 @@ fun AlarmSettingsSheet(onDismissRequest: () -> Unit, currentAlarm: Alarm, onSave val initialTime = remember { TimeHelper.millisToTime(currentAlarm.time) } var hours = remember { initialTime.hours } var minutes = remember { initialTime.minutes } - ModalBottomSheet(onDismissRequest, sheetState = sheetState) { - Column( - Modifier - .fillMaxSize() - .padding(horizontal = 8.dp, vertical = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - AlarmTimePicker( - hours, - minutes, - onHoursChanged = { - hours = it - }, - onMinutesChanged = { minutes = it } - ) - Column { - SwitchItem( - title = stringResource(R.string.repeat), - isChecked = repeat, - onClick = { newValue -> - repeat = newValue + Dialog( + onDismissRequest, + properties = DialogProperties( + usePlatformDefaultWidth = false, + dismissOnClickOutside = false + ) + ) { + val window = (LocalView.current.parent as DialogWindowProvider).window + SideEffect { + window.setDimAmount(0f) + } + val scrollState = rememberScrollState() + Surface { + Column( + Modifier + .fillMaxSize() + .padding(horizontal = 8.dp, vertical = 16.dp) + .verticalScroll(scrollState), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceEvenly + ) { + AlarmTimePicker( + hours, + minutes, + onHoursChanged = { + hours = it }, - icon = Icons.Rounded.EventRepeat + onMinutesChanged = { minutes = it } ) - AnimatedVisibility(visible = repeat) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(16.dp), - horizontalArrangement = Arrangement.SpaceBetween - ) { - val daysOfWeek = remember { - AlarmHelper.getDaysOfWeekByLocale(context) - } + Column { + SwitchItem( + title = stringResource(R.string.repeat), + isChecked = repeat, + onClick = { newValue -> + repeat = newValue + }, + icon = Icons.Rounded.EventRepeat + ) + AnimatedVisibility(visible = repeat) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + val daysOfWeek = remember { + AlarmHelper.getDaysOfWeekByLocale(context) + } - daysOfWeek.forEach { (day, index) -> - val enabled = chosenDays.contains(index) - Box( - modifier = Modifier - .size(30.dp) - .background( - if (enabled) MaterialTheme.colorScheme.primary else Color.Transparent, - CircleShape + daysOfWeek.forEach { (day, index) -> + val enabled = chosenDays.contains(index) + Box( + modifier = Modifier + .size(30.dp) + .background( + if (enabled) MaterialTheme.colorScheme.primary else Color.Transparent, + CircleShape + ) + .clip(CircleShape) + .border( + if (enabled) 0.dp else 1.dp, + MaterialTheme.colorScheme.primary, + CircleShape + ) + .clickable { + if (enabled) { + if (chosenDays.size > 1) chosenDays.remove(index) + } else { + chosenDays.add( + index + ) + } + }, + contentAlignment = Alignment.Center + ) { + Text( + text = day, + color = if (enabled) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onPrimaryContainer ) - .clip(CircleShape) - .border( - if (enabled) 0.dp else 1.dp, - MaterialTheme.colorScheme.primary, - CircleShape - ) - .clickable { - if (enabled) { - if (chosenDays.size > 1) chosenDays.remove(index) - } else { - chosenDays.add( - index - ) - } - }, - contentAlignment = Alignment.Center - ) { - Text( - text = day, - color = if (enabled) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onPrimaryContainer - ) + } } } } - } - Row( - modifier = Modifier.padding(8.dp, 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - OutlinedTextField( - modifier = Modifier.fillMaxWidth(), - value = label, - onValueChange = { - label = it + Row( + modifier = Modifier.padding(8.dp, 16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = label, + onValueChange = { + label = it + }, + label = { + Text(text = stringResource(id = R.string.alarm_name)) + }, + singleLine = true, + leadingIcon = { + Icon(imageVector = Icons.Outlined.Label, contentDescription = null) + } + ) + } + SwitchWithDivider( + title = stringResource(R.string.sound), + description = soundName ?: stringResource(R.string.default_sound), + isChecked = soundEnabled, + icon = Icons.Rounded.Alarm, + onClick = { + showRingtoneDialog = true }, - label = { - Text(text = stringResource(id = R.string.alarm_name)) + onChecked = { + soundEnabled = it + } + ) + SwitchItem( + title = stringResource(R.string.vibrate), + isChecked = vibrationEnabled, + onClick = { newValue -> + vibrationEnabled = newValue + }, + icon = Icons.Rounded.Vibration + ) + SwitchWithDivider( + title = stringResource(R.string.snooze), + description = with(snoozeMinutes) { + pluralStringResource( + id = R.plurals.minutes, + count = this, + this + ) + }, + isChecked = snoozeEnabled, + icon = Icons.Rounded.Snooze, + onClick = { + showSnoozeDialog = true }, - singleLine = true, - leadingIcon = { - Icon(imageVector = Icons.Outlined.Label, contentDescription = null) + onChecked = { + snoozeEnabled = it } ) } - SwitchWithDivider( - title = stringResource(R.string.sound), - description = soundName ?: stringResource(R.string.default_sound), - isChecked = soundEnabled, - icon = Icons.Rounded.Alarm, - onClick = { - showRingtoneDialog = true - }, - onChecked = { - soundEnabled = it + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { + DialogButton(label = android.R.string.cancel) { + onDismissRequest.invoke() } - ) - SwitchItem( - title = stringResource(R.string.vibrate), - isChecked = vibrationEnabled, - onClick = { newValue -> - vibrationEnabled = newValue - }, - icon = Icons.Rounded.Vibration - ) - SwitchWithDivider( - title = stringResource(R.string.snooze), - description = with(snoozeMinutes) { - pluralStringResource( - id = R.plurals.minutes, - count = this, - this - ) - }, - isChecked = snoozeEnabled, - icon = Icons.Rounded.Snooze, - onClick = { - showSnoozeDialog = true - }, - onChecked = { - snoozeEnabled = it + DialogButton(label = android.R.string.ok) { + val alarm = + currentAlarm.copy( + time = (hours * 60 + minutes) * 60 * 1000L, + label = label.takeIf { l -> l.isNotBlank() }, + days = chosenDays.sorted(), + vibrate = vibrationEnabled, + soundName = soundName, + soundUri = soundUri, + repeat = repeat, + snoozeEnabled = snoozeEnabled, + snoozeMinutes = snoozeMinutes, + soundEnabled = soundEnabled + ) + onSave(alarm) + onDismissRequest.invoke() } - ) - } - Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly) { - DialogButton(label = android.R.string.cancel) { - onDismissRequest.invoke() - } - DialogButton(label = android.R.string.ok) { - val alarm = - currentAlarm.copy( - time = (hours * 60 + minutes) * 60 * 1000L, - label = label.takeIf { l -> l.isNotBlank() }, - days = chosenDays.sorted(), - vibrate = vibrationEnabled, - soundName = soundName, - soundUri = soundUri, - repeat = repeat, - snoozeEnabled = snoozeEnabled, - snoozeMinutes = snoozeMinutes, - soundEnabled = soundEnabled - ) - onSave(alarm) - onDismissRequest.invoke() } } } diff --git a/app/src/main/java/com/bnyro/clock/ui/components/AlarmTimePicker.kt b/app/src/main/java/com/bnyro/clock/ui/components/AlarmTimePicker.kt index 68cacb7e..d31ed657 100644 --- a/app/src/main/java/com/bnyro/clock/ui/components/AlarmTimePicker.kt +++ b/app/src/main/java/com/bnyro/clock/ui/components/AlarmTimePicker.kt @@ -29,7 +29,7 @@ import androidx.compose.ui.unit.dp * @param onMinutesChanged New minutes value */ @Composable -fun ColumnScope.AlarmTimePicker( +fun AlarmTimePicker( initialHours: Int, initialMinutes: Int, onHoursChanged: (Int) -> Unit, @@ -41,7 +41,7 @@ fun ColumnScope.AlarmTimePicker( if (initialHours >= 12) Meridiem.PM else Meridiem.AM } Box( - modifier = Modifier.fillMaxWidth().weight(1f), + modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center ) { Row { diff --git a/app/src/main/java/com/bnyro/clock/ui/model/AlarmModel.kt b/app/src/main/java/com/bnyro/clock/ui/model/AlarmModel.kt index 1a1aa8ac..8641e45b 100644 --- a/app/src/main/java/com/bnyro/clock/ui/model/AlarmModel.kt +++ b/app/src/main/java/com/bnyro/clock/ui/model/AlarmModel.kt @@ -1,15 +1,18 @@ package com.bnyro.clock.ui.model import android.content.Context +import android.widget.Toast import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.bnyro.clock.R import com.bnyro.clock.db.DatabaseHolder import com.bnyro.clock.obj.Alarm import com.bnyro.clock.obj.AlarmFilters import com.bnyro.clock.util.AlarmHelper +import com.bnyro.clock.util.TimeHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -19,6 +22,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import java.util.Collections +import kotlin.time.Duration.Companion.milliseconds class AlarmModel : ViewModel() { var selectedAlarm: Alarm? by mutableStateOf(null) @@ -47,6 +51,17 @@ class AlarmModel : ViewModel() { } fun updateAlarm(context: Context, alarm: Alarm) { + if (alarm.enabled) { + val millisRemainingForAlarm = + (AlarmHelper.getAlarmTime(alarm) - System.currentTimeMillis()) + val formattedDuration = + TimeHelper.durationToFormatted(context, millisRemainingForAlarm.milliseconds) + Toast.makeText( + context, + context.resources.getString(R.string.alarm_will_play, formattedDuration), + Toast.LENGTH_SHORT + ).show() + } AlarmHelper.enqueue(context, alarm) viewModelScope.launch(Dispatchers.IO) { DatabaseHolder.instance.alarmsDao().update(alarm)