feat(settings): add a settings option to disable AOD
disabled by default
This commit is contained in:
@@ -51,7 +51,10 @@ class MainActivity : ComponentActivity() {
|
||||
appContainer.appTimerRepository.colorScheme = colorScheme
|
||||
}
|
||||
|
||||
AppScreen(timerViewModel = timerViewModel, isAODEnabled = true)
|
||||
AppScreen(
|
||||
timerViewModel = timerViewModel,
|
||||
isAODEnabled = preferencesState.aodEnabled
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<String, Pair<Int, String>> = remember {
|
||||
val themeMap: Map<String, Pair<Int, Int>> = 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<String, String> = 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
|
||||
)
|
||||
|
||||
@@ -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<String, Pair<Int, String>>,
|
||||
themeMap: Map<String, Pair<Int, Int>>,
|
||||
reverseThemeMap: Map<String, String>,
|
||||
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<String, Pair<Int, String>> ->
|
||||
themeMap.entries.forEachIndexed { index: Int, pair: Map.Entry<String, Pair<Int, Int>> ->
|
||||
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
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
|
||||
@Composable
|
||||
fun ThemePickerListItem(
|
||||
theme: String,
|
||||
themeMap: Map<String, Pair<Int, String>>,
|
||||
themeMap: Map<String, Pair<Int, Int>>,
|
||||
reverseThemeMap: Map<String, String>,
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -95,6 +95,9 @@ class TimerViewModel(
|
||||
)
|
||||
).toUri()
|
||||
|
||||
preferenceRepository.getBooleanPreference("aod_enabled")
|
||||
?: preferenceRepository.saveBooleanPreference("aod_enabled", false)
|
||||
|
||||
_time.update { timerRepository.focusTime }
|
||||
cycles = 0
|
||||
startTime = 0L
|
||||
|
||||
16
app/src/main/res/drawable/aod.xml
Normal file
16
app/src/main/res/drawable/aod.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<!--
|
||||
~ Copyright (c) 2025 Nishant Mishra
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e3e3e3"
|
||||
android:pathData="M360,480h240q17,0 28.5,-11.5T640,440q0,-17 -11.5,-28.5T600,400L360,400q-17,0 -28.5,11.5T320,440q0,17 11.5,28.5T360,480ZM400,620h160q17,0 28.5,-11.5T600,580q0,-17 -11.5,-28.5T560,540L400,540q-17,0 -28.5,11.5T360,580q0,17 11.5,28.5T400,620ZM280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v124q18,7 29,22t11,34v80q0,19 -11,34t-29,22v404q0,33 -23.5,56.5T680,920L280,920Z" />
|
||||
</vector>
|
||||
@@ -1,60 +1,62 @@
|
||||
<resources>
|
||||
<string name="app_name">Tomato</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="focus">Focus</string>
|
||||
<string name="short_break">Short break</string>
|
||||
<string name="long_break">Long break</string>
|
||||
<string name="exit">Exit</string>
|
||||
<string name="skip">Skip</string>
|
||||
<string name="stop_alarm">Stop alarm</string>
|
||||
<string name="min_remaining_notification">%1$s min remaining</string>
|
||||
<string name="paused">Paused</string>
|
||||
<string name="completed">Completed</string>
|
||||
<string name="up_next_notification">Up next: %1$s (%2$s)</string>
|
||||
<string name="start_next">Start next</string>
|
||||
<string name="choose_color_scheme">Choose color scheme</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="color_scheme">Color scheme</string>
|
||||
<string name="dynamic">Dynamic</string>
|
||||
<string name="color">Color</string>
|
||||
<string name="system_default">System default</string>
|
||||
<string name="alarm">Alarm</string>
|
||||
<string name="light">Light</string>
|
||||
<string name="dark">Dark</string>
|
||||
<string name="choose_theme">Choose theme</string>
|
||||
<string name="productivity_analysis">Productivity analysis</string>
|
||||
<string name="productivity_analysis_desc">Focus durations at different times of the day</string>
|
||||
<string name="alarm_desc">Ring alarm when a timer completes</string>
|
||||
<string name="alarm_sound">Alarm sound</string>
|
||||
<string name="always_on_display">Always On Display</string>
|
||||
<string name="always_on_display_desc">Tap anywhere when viewing the timer to switch to AOD mode</string>
|
||||
<string name="app_name">Tomato</string>
|
||||
<string name="black_theme">Black theme</string>
|
||||
<string name="black_theme_desc">Use a pure black dark theme</string>
|
||||
<string name="alarm_desc">Ring alarm when a timer completes</string>
|
||||
<string name="vibrate">Vibrate</string>
|
||||
<string name="vibrate_desc">Vibrate when a timer completes</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="break_">Break</string>
|
||||
<string name="choose_color_scheme">Choose color scheme</string>
|
||||
<string name="choose_theme">Choose theme</string>
|
||||
<string name="color">Color</string>
|
||||
<string name="color_scheme">Color scheme</string>
|
||||
<string name="completed">Completed</string>
|
||||
<string name="dark">Dark</string>
|
||||
<string name="dynamic">Dynamic</string>
|
||||
<string name="exit">Exit</string>
|
||||
<string name="focus">Focus</string>
|
||||
<string name="focus_per_day_avg">focus per day (avg)</string>
|
||||
<string name="last_month">Last month</string>
|
||||
<string name="last_week">Last week</string>
|
||||
<string name="last_year">Last year</string>
|
||||
<string name="light">Light</string>
|
||||
<string name="long_break">Long break</string>
|
||||
<string name="min_remaining_notification">%1$s min remaining</string>
|
||||
<string name="monthly_productivity_analysis">Monthly productivity analysis</string>
|
||||
<string name="more">More</string>
|
||||
<string name="more_info">More info</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="paused">Paused</string>
|
||||
<string name="play">Play</string>
|
||||
<string name="pomodoro_info">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.</string>
|
||||
<string name="productivity_analysis">Productivity analysis</string>
|
||||
<string name="productivity_analysis_desc">Focus durations at different times of the day</string>
|
||||
<string name="restart">Restart</string>
|
||||
<string name="session_length">Session length</string>
|
||||
<string name="session_length_desc">Focus intervals in one session: %1$d</string>
|
||||
<string name="pomodoro_info">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.</string>
|
||||
<string name="stats">Stats</string>
|
||||
<string name="today">Today</string>
|
||||
<string name="break_">Break</string>
|
||||
<string name="last_week">Last week</string>
|
||||
<string name="focus_per_day_avg">focus per day (avg)</string>
|
||||
<string name="more_info">More info</string>
|
||||
<string name="weekly_productivity_analysis">Weekly productivity analysis</string>
|
||||
<string name="last_month">Last month</string>
|
||||
<string name="monthly_productivity_analysis">Monthly productivity analysis</string>
|
||||
<string name="stop_alarm_question">Stop Alarm?</string>
|
||||
<string name="stop_alarm_dialog_text">Current timer session is complete. Tap anywhere to stop the alarm.</string>
|
||||
<string name="timer_session_count">%1$d of %2$d</string>
|
||||
<string name="more">More</string>
|
||||
<string name="pause">Pause</string>
|
||||
<string name="play">Play</string>
|
||||
<string name="restart">Restart</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="short_break">Short break</string>
|
||||
<string name="skip">Skip</string>
|
||||
<string name="skip_to_next">Skip to next</string>
|
||||
<string name="up_next">Up next</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="start_next">Start next</string>
|
||||
<string name="stats">Stats</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="stop_alarm">Stop alarm</string>
|
||||
<string name="stop_alarm_dialog_text">Current timer session is complete. Tap anywhere to stop the alarm.</string>
|
||||
<string name="stop_alarm_question">Stop Alarm?</string>
|
||||
<string name="system_default">System default</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="timer">Timer</string>
|
||||
<string name="timer_progress">Timer progress</string>
|
||||
<string name="last_year">Last year</string>
|
||||
<string name="timer_session_count">%1$d of %2$d</string>
|
||||
<string name="today">Today</string>
|
||||
<string name="up_next">Up next</string>
|
||||
<string name="up_next_notification">Up next: %1$s (%2$s)</string>
|
||||
<string name="vibrate">Vibrate</string>
|
||||
<string name="vibrate_desc">Vibrate when a timer completes</string>
|
||||
<string name="weekly_productivity_analysis">Weekly productivity analysis</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user