From 4293f0d5f13e030ff97fe226a56ad5909ef40b5a Mon Sep 17 00:00:00 2001 From: Nishant Mishra Date: Tue, 21 Oct 2025 11:41:38 +0530 Subject: [PATCH] feat(settings): add a settings option to disable AOD disabled by default --- .../java/org/nsh07/pomodoro/MainActivity.kt | 5 +- .../org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt | 4 +- .../ui/settingsScreen/SettingsScreen.kt | 61 +++++++---- .../pomodoro/ui/settingsScreen/ThemeDialog.kt | 22 ++-- .../ui/settingsScreen/ThemePickerListItem.kt | 4 +- .../viewModel/PreferencesState.kt | 3 +- .../viewModel/SettingsViewModel.kt | 14 ++- .../timerScreen/viewModel/TimerViewModel.kt | 3 + app/src/main/res/drawable/aod.xml | 16 +++ app/src/main/res/values/strings.xml | 100 +++++++++--------- 10 files changed, 148 insertions(+), 84 deletions(-) create mode 100644 app/src/main/res/drawable/aod.xml diff --git a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt index 9a40b41..a98932a 100644 --- a/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt +++ b/app/src/main/java/org/nsh07/pomodoro/MainActivity.kt @@ -51,7 +51,10 @@ class MainActivity : ComponentActivity() { appContainer.appTimerRepository.colorScheme = colorScheme } - AppScreen(timerViewModel = timerViewModel, isAODEnabled = true) + AppScreen( + timerViewModel = timerViewModel, + isAODEnabled = preferencesState.aodEnabled + ) } } } diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt b/app/src/main/java/org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt index ce78c12..8225cc2 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt @@ -177,8 +177,8 @@ fun SharedTransitionScope.AlwaysOnDisplay( } } - val x by animateIntAsState(randomX) - val y by animateIntAsState(randomY) + val x by animateIntAsState(randomX, motionScheme.slowSpatialSpec()) + val y by animateIntAsState(randomY, motionScheme.slowSpatialSpec()) Box( modifier = modifier diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt index 67b00a0..b2bca48 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt @@ -15,6 +15,7 @@ import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.DrawableRes +import androidx.annotation.StringRes import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -143,6 +144,7 @@ fun SettingsScreenRoot( onAlarmEnabledChange = viewModel::saveAlarmEnabled, onVibrateEnabledChange = viewModel::saveVibrateEnabled, onBlackThemeChange = viewModel::saveBlackTheme, + onAodEnabledChange = viewModel::saveAodEnabled, onAlarmSoundChanged = { viewModel.saveAlarmSound(it) Intent(context, TimerService::class.java).apply { @@ -170,6 +172,7 @@ private fun SettingsScreen( onAlarmEnabledChange: (Boolean) -> Unit, onVibrateEnabledChange: (Boolean) -> Unit, onBlackThemeChange: (Boolean) -> Unit, + onAodEnabledChange: (Boolean) -> Unit, onAlarmSoundChanged: (Uri?) -> Unit, onThemeChange: (String) -> Unit, onColorSchemeChange: (Color) -> Unit, @@ -181,14 +184,14 @@ private fun SettingsScreen( checkedIconColor = colorScheme.primary, ) - val themeMap: Map> = remember { + val themeMap: Map> = remember { mapOf( "auto" to Pair( R.drawable.brightness_auto, - context.getString(R.string.system_default) + R.string.system_default ), - "light" to Pair(R.drawable.light_mode, context.getString(R.string.light)), - "dark" to Pair(R.drawable.dark_mode, context.getString(R.string.dark)) + "light" to Pair(R.drawable.light_mode, R.string.light), + "dark" to Pair(R.drawable.dark_mode, R.string.dark) ) } val reverseThemeMap: Map = remember { @@ -232,27 +235,39 @@ private fun SettingsScreen( putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, alarmSound.toUri()) } - val switchItems = remember(preferencesState.blackTheme, alarmEnabled, vibrateEnabled) { + val switchItems = remember( + preferencesState.blackTheme, + preferencesState.aodEnabled, + alarmEnabled, + vibrateEnabled + ) { listOf( SettingsSwitchItem( checked = preferencesState.blackTheme, icon = R.drawable.contrast, - label = context.getString(R.string.black_theme), - description = context.getString(R.string.black_theme_desc), + label = R.string.black_theme, + description = R.string.black_theme_desc, onClick = onBlackThemeChange ), + SettingsSwitchItem( + checked = preferencesState.aodEnabled, + icon = R.drawable.aod, + label = R.string.always_on_display, + description = R.string.always_on_display_desc, + onClick = onAodEnabledChange + ), SettingsSwitchItem( checked = alarmEnabled, icon = R.drawable.alarm_on, - label = context.getString(R.string.alarm), - description = context.getString(R.string.alarm_desc), + label = R.string.alarm, + description = R.string.alarm_desc, onClick = onAlarmEnabledChange ), SettingsSwitchItem( checked = vibrateEnabled, icon = R.drawable.mobile_vibrate, - label = context.getString(R.string.vibrate), - description = context.getString(R.string.vibrate_desc), + label = R.string.vibrate, + description = R.string.vibrate_desc, onClick = onVibrateEnabledChange ) ) @@ -404,14 +419,13 @@ private fun SettingsScreen( .clip(middleListItemShape) ) } - item { - val item = switchItems[0] + itemsIndexed(switchItems.take(2)) { index, item -> ListItem( leadingContent = { Icon(painterResource(item.icon), contentDescription = null) }, - headlineContent = { Text(item.label) }, - supportingContent = { Text(item.description) }, + headlineContent = { Text(stringResource(item.label)) }, + supportingContent = { Text(stringResource(item.description)) }, trailingContent = { Switch( checked = item.checked, @@ -435,7 +449,9 @@ private fun SettingsScreen( ) }, colors = listItemColors, - modifier = Modifier.clip(bottomListItemShape) + modifier = Modifier + .padding(top = if (index != 0) 16.dp else 0.dp) + .clip(if (index == 0) bottomListItemShape else cardShape) ) } @@ -454,13 +470,13 @@ private fun SettingsScreen( .clickable(onClick = { ringtonePickerLauncher.launch(intent) }) ) } - itemsIndexed(switchItems.drop(1)) { index, item -> + itemsIndexed(switchItems.drop(2)) { index, item -> ListItem( leadingContent = { Icon(painterResource(item.icon), contentDescription = null) }, - headlineContent = { Text(item.label) }, - supportingContent = { Text(item.description) }, + headlineContent = { Text(stringResource(item.label)) }, + supportingContent = { Text(stringResource(item.description)) }, trailingContent = { Switch( checked = item.checked, @@ -487,7 +503,7 @@ private fun SettingsScreen( modifier = Modifier .clip( when (index) { - switchItems.lastIndex - 1 -> bottomListItemShape + switchItems.lastIndex - 2 -> bottomListItemShape else -> middleListItemShape } ) @@ -546,6 +562,7 @@ fun SettingsScreenPreview() { onAlarmEnabledChange = {}, onVibrateEnabledChange = {}, onBlackThemeChange = {}, + onAodEnabledChange = {}, onAlarmSoundChanged = {}, onThemeChange = {}, onColorSchemeChange = {}, @@ -557,7 +574,7 @@ fun SettingsScreenPreview() { data class SettingsSwitchItem( val checked: Boolean, @param:DrawableRes val icon: Int, - val label: String, - val description: String, + @param:StringRes val label: Int, + @param:StringRes val description: Int, val onClick: (Boolean) -> Unit ) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemeDialog.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemeDialog.kt index 67f4d7a..1de30cd 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemeDialog.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemeDialog.kt @@ -31,11 +31,12 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role @@ -50,14 +51,16 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun ThemeDialog( - themeMap: Map>, + themeMap: Map>, reverseThemeMap: Map, theme: String, setShowThemeDialog: (Boolean) -> Unit, onThemeChange: (String) -> Unit ) { val selectedOption = - remember { mutableStateOf(themeMap[theme]!!.second) } + remember { mutableIntStateOf(themeMap[theme]!!.second) } + + val context = LocalContext.current BasicAlertDialog( onDismissRequest = { setShowThemeDialog(false) } @@ -80,7 +83,7 @@ fun ThemeDialog( verticalArrangement = Arrangement.spacedBy(2.dp), modifier = Modifier.selectableGroup() ) { - themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry> -> + themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry> -> val text = pair.value.second val selected = text == selectedOption.value @@ -94,7 +97,10 @@ fun ThemeDialog( } }, headlineContent = { - Text(text = text, style = MaterialTheme.typography.bodyLarge) + Text( + text = stringResource(text), + style = MaterialTheme.typography.bodyLarge + ) }, colors = if (!selected) listItemColors else selectedListItemColors, modifier = Modifier @@ -110,7 +116,11 @@ fun ThemeDialog( selected = (text == selectedOption.value), onClick = { selectedOption.value = text - onThemeChange(reverseThemeMap[selectedOption.value]!!) + onThemeChange( + reverseThemeMap[context.getString( + selectedOption.intValue + )]!! + ) }, role = Role.RadioButton ) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt index 2bee9c5..0c66f00 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/ThemePickerListItem.kt @@ -25,7 +25,7 @@ import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors @Composable fun ThemePickerListItem( theme: String, - themeMap: Map>, + themeMap: Map>, reverseThemeMap: Map, items: Int, index: Int, @@ -53,7 +53,7 @@ fun ThemePickerListItem( }, headlineContent = { Text(stringResource(R.string.theme)) }, supportingContent = { - Text(themeMap[theme]!!.second) + Text(stringResource(themeMap[theme]!!.second)) }, colors = listItemColors, items = items, diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/PreferencesState.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/PreferencesState.kt index 0ae4849..7231852 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/PreferencesState.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/PreferencesState.kt @@ -14,5 +14,6 @@ import androidx.compose.ui.graphics.Color data class PreferencesState( val theme: String = "auto", val colorScheme: String = Color.White.toString(), - val blackTheme: Boolean = false + val blackTheme: Boolean = false, + val aodEnabled: Boolean = false ) diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt index 5f604c1..454bf82 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/viewModel/SettingsViewModel.kt @@ -80,12 +80,15 @@ class SettingsViewModel( ?: preferenceRepository.saveStringPreference("color_scheme", Color.White.toString()) val blackTheme = preferenceRepository.getBooleanPreference("black_theme") ?: preferenceRepository.saveBooleanPreference("black_theme", false) + val aodEnabled = preferenceRepository.getBooleanPreference("aod_enabled") + ?: preferenceRepository.saveBooleanPreference("aod_enabled", false) _preferencesState.update { currentState -> currentState.copy( theme = theme, colorScheme = colorScheme, - blackTheme = blackTheme + blackTheme = blackTheme, + aodEnabled = aodEnabled ) } } @@ -196,6 +199,15 @@ class SettingsViewModel( } } + fun saveAodEnabled(aodEnabled: Boolean) { + viewModelScope.launch { + _preferencesState.update { currentState -> + currentState.copy(aodEnabled = aodEnabled) + } + preferenceRepository.saveBooleanPreference("aod_enabled", aodEnabled) + } + } + companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { diff --git a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt index 2202d42..53c23b5 100644 --- a/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt +++ b/app/src/main/java/org/nsh07/pomodoro/ui/timerScreen/viewModel/TimerViewModel.kt @@ -95,6 +95,9 @@ class TimerViewModel( ) ).toUri() + preferenceRepository.getBooleanPreference("aod_enabled") + ?: preferenceRepository.saveBooleanPreference("aod_enabled", false) + _time.update { timerRepository.focusTime } cycles = 0 startTime = 0L diff --git a/app/src/main/res/drawable/aod.xml b/app/src/main/res/drawable/aod.xml new file mode 100644 index 0000000..ab821c8 --- /dev/null +++ b/app/src/main/res/drawable/aod.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 067cff5..c185a08 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,60 +1,62 @@ - Tomato - Start - Stop - Focus - Short break - Long break - Exit - Skip - Stop alarm - %1$s min remaining - Paused - Completed - Up next: %1$s (%2$s) - Start next - Choose color scheme - OK - Color scheme - Dynamic - Color - System default Alarm - Light - Dark - Choose theme - Productivity analysis - Focus durations at different times of the day + Ring alarm when a timer completes Alarm sound + Always On Display + Tap anywhere when viewing the timer to switch to AOD mode + Tomato Black theme Use a pure black dark theme - Ring alarm when a timer completes - Vibrate - Vibrate when a timer completes - Theme - Settings + Break + Choose color scheme + Choose theme + Color + Color scheme + Completed + Dark + Dynamic + Exit + Focus + focus per day (avg) + Last month + Last week + Last year + Light + Long break + %1$s min remaining + Monthly productivity analysis + More + More info + OK + Pause + Paused + Play + A \"session\" is a sequence of pomodoro intervals that contain focus intervals, short break intervals, and a long break interval. The last break of a session is always a long break. + Productivity analysis + Focus durations at different times of the day + Restart Session length Focus intervals in one session: %1$d - A \"session\" is a sequence of pomodoro intervals that contain focus intervals, short break intervals, and a long break interval. The last break of a session is always a long break. - Stats - Today - Break - Last week - focus per day (avg) - More info - Weekly productivity analysis - Last month - Monthly productivity analysis - Stop Alarm? - Current timer session is complete. Tap anywhere to stop the alarm. - %1$d of %2$d - More - Pause - Play - Restart + Settings + Short break + Skip Skip to next - Up next + Start + Start next + Stats + Stop + Stop alarm + Current timer session is complete. Tap anywhere to stop the alarm. + Stop Alarm? + System default + Theme Timer Timer progress - Last year + %1$d of %2$d + Today + Up next + Up next: %1$s (%2$s) + Vibrate + Vibrate when a timer completes + Weekly productivity analysis \ No newline at end of file