feat(settings): implement a settings option to enable auto-dnd
This commit is contained in:
@@ -1,8 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Nishant Mishra
|
* Copyright (c) 2025 Nishant Mishra
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* This file is part of Tomato - a minimalist pomodoro timer for Android.
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
*
|
||||||
|
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
* General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Tomato.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.nsh07.pomodoro.data
|
package org.nsh07.pomodoro.data
|
||||||
@@ -27,6 +37,7 @@ interface TimerRepository {
|
|||||||
|
|
||||||
var alarmEnabled: Boolean
|
var alarmEnabled: Boolean
|
||||||
var vibrateEnabled: Boolean
|
var vibrateEnabled: Boolean
|
||||||
|
var dndEnabled: Boolean
|
||||||
|
|
||||||
var colorScheme: ColorScheme
|
var colorScheme: ColorScheme
|
||||||
|
|
||||||
@@ -46,6 +57,7 @@ class AppTimerRepository : TimerRepository {
|
|||||||
override var timerFrequency: Float = 10f
|
override var timerFrequency: Float = 10f
|
||||||
override var alarmEnabled = true
|
override var alarmEnabled = true
|
||||||
override var vibrateEnabled = true
|
override var vibrateEnabled = true
|
||||||
|
override var dndEnabled: Boolean = false
|
||||||
override var colorScheme = lightColorScheme()
|
override var colorScheme = lightColorScheme()
|
||||||
override var alarmSoundUri: Uri? =
|
override var alarmSoundUri: Uri? =
|
||||||
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Actions.SKIP.toString() -> skipTimer(true)
|
Actions.SKIP.toString() -> skipScope.launch { skipTimer(true) }
|
||||||
|
|
||||||
Actions.STOP_ALARM.toString() -> stopAlarm()
|
Actions.STOP_ALARM.toString() -> stopAlarm()
|
||||||
|
|
||||||
@@ -329,50 +329,48 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun skipTimer(fromButton: Boolean = false) {
|
private suspend fun skipTimer(fromButton: Boolean = false) {
|
||||||
updateProgressSegments()
|
updateProgressSegments()
|
||||||
skipScope.launch {
|
saveTimeToDb()
|
||||||
saveTimeToDb()
|
updateProgressSegments()
|
||||||
updateProgressSegments()
|
showTimerNotification(0, paused = true, complete = !fromButton)
|
||||||
showTimerNotification(0, paused = true, complete = !fromButton)
|
startTime = 0L
|
||||||
startTime = 0L
|
pauseTime = 0L
|
||||||
pauseTime = 0L
|
pauseDuration = 0L
|
||||||
pauseDuration = 0L
|
|
||||||
|
|
||||||
cycles = (cycles + 1) % (timerRepository.sessionLength * 2)
|
cycles = (cycles + 1) % (timerRepository.sessionLength * 2)
|
||||||
|
|
||||||
if (cycles % 2 == 0) {
|
if (cycles % 2 == 0) {
|
||||||
if (timerState.value.timerRunning) setDoNotDisturb(true)
|
if (timerState.value.timerRunning) setDoNotDisturb(true)
|
||||||
time = timerRepository.focusTime
|
time = timerRepository.focusTime
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
timerMode = TimerMode.FOCUS,
|
timerMode = TimerMode.FOCUS,
|
||||||
timeStr = millisecondsToStr(time),
|
timeStr = millisecondsToStr(time),
|
||||||
totalTime = time,
|
totalTime = time,
|
||||||
nextTimerMode = if (cycles == (timerRepository.sessionLength - 1) * 2) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
nextTimerMode = if (cycles == (timerRepository.sessionLength - 1) * 2) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
||||||
nextTimeStr = if (cycles == (timerRepository.sessionLength - 1) * 2) millisecondsToStr(
|
nextTimeStr = if (cycles == (timerRepository.sessionLength - 1) * 2) millisecondsToStr(
|
||||||
timerRepository.longBreakTime
|
timerRepository.longBreakTime
|
||||||
) else millisecondsToStr(
|
) else millisecondsToStr(
|
||||||
timerRepository.shortBreakTime
|
timerRepository.shortBreakTime
|
||||||
),
|
),
|
||||||
currentFocusCount = cycles / 2 + 1,
|
currentFocusCount = cycles / 2 + 1,
|
||||||
totalFocusCount = timerRepository.sessionLength
|
totalFocusCount = timerRepository.sessionLength
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (timerState.value.timerRunning) setDoNotDisturb(false)
|
if (timerState.value.timerRunning) setDoNotDisturb(false)
|
||||||
val long = cycles == (timerRepository.sessionLength * 2) - 1
|
val long = cycles == (timerRepository.sessionLength * 2) - 1
|
||||||
time = if (long) timerRepository.longBreakTime else timerRepository.shortBreakTime
|
time = if (long) timerRepository.longBreakTime else timerRepository.shortBreakTime
|
||||||
|
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
timerMode = if (long) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
timerMode = if (long) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
||||||
timeStr = millisecondsToStr(time),
|
timeStr = millisecondsToStr(time),
|
||||||
totalTime = time,
|
totalTime = time,
|
||||||
nextTimerMode = TimerMode.FOCUS,
|
nextTimerMode = TimerMode.FOCUS,
|
||||||
nextTimeStr = millisecondsToStr(timerRepository.focusTime)
|
nextTimeStr = millisecondsToStr(timerRepository.focusTime)
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -449,7 +447,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setDoNotDisturb(doNotDisturb: Boolean) {
|
private fun setDoNotDisturb(doNotDisturb: Boolean) {
|
||||||
if (notificationManagerService.isNotificationPolicyAccessGranted()) {
|
if (timerRepository.dndEnabled && notificationManagerService.isNotificationPolicyAccessGranted()) {
|
||||||
if (doNotDisturb) {
|
if (doNotDisturb) {
|
||||||
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS)
|
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS)
|
||||||
} else notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
} else notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ fun SettingsScreenRoot(
|
|||||||
|
|
||||||
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
|
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
|
||||||
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
|
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
|
||||||
|
val dndEnabled by viewModel.dndEnabled.collectAsStateWithLifecycle(false)
|
||||||
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound)
|
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound)
|
||||||
|
|
||||||
val preferencesState by viewModel.preferencesState.collectAsStateWithLifecycle()
|
val preferencesState by viewModel.preferencesState.collectAsStateWithLifecycle()
|
||||||
@@ -126,11 +127,13 @@ fun SettingsScreenRoot(
|
|||||||
sessionsSliderState = sessionsSliderState,
|
sessionsSliderState = sessionsSliderState,
|
||||||
alarmEnabled = alarmEnabled,
|
alarmEnabled = alarmEnabled,
|
||||||
vibrateEnabled = vibrateEnabled,
|
vibrateEnabled = vibrateEnabled,
|
||||||
|
dndEnabled = dndEnabled,
|
||||||
alarmSound = alarmSound,
|
alarmSound = alarmSound,
|
||||||
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
|
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
|
||||||
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
|
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
|
||||||
onBlackThemeChange = viewModel::saveBlackTheme,
|
onBlackThemeChange = viewModel::saveBlackTheme,
|
||||||
onAodEnabledChange = viewModel::saveAodEnabled,
|
onAodEnabledChange = viewModel::saveAodEnabled,
|
||||||
|
onDndEnabledChange = viewModel::saveDndEnabled,
|
||||||
onAlarmSoundChanged = {
|
onAlarmSoundChanged = {
|
||||||
viewModel.saveAlarmSound(it)
|
viewModel.saveAlarmSound(it)
|
||||||
Intent(context, TimerService::class.java).apply {
|
Intent(context, TimerService::class.java).apply {
|
||||||
@@ -155,11 +158,13 @@ private fun SettingsScreen(
|
|||||||
sessionsSliderState: SliderState,
|
sessionsSliderState: SliderState,
|
||||||
alarmEnabled: Boolean,
|
alarmEnabled: Boolean,
|
||||||
vibrateEnabled: Boolean,
|
vibrateEnabled: Boolean,
|
||||||
|
dndEnabled: Boolean,
|
||||||
alarmSound: String,
|
alarmSound: String,
|
||||||
onAlarmEnabledChange: (Boolean) -> Unit,
|
onAlarmEnabledChange: (Boolean) -> Unit,
|
||||||
onVibrateEnabledChange: (Boolean) -> Unit,
|
onVibrateEnabledChange: (Boolean) -> Unit,
|
||||||
onBlackThemeChange: (Boolean) -> Unit,
|
onBlackThemeChange: (Boolean) -> Unit,
|
||||||
onAodEnabledChange: (Boolean) -> Unit,
|
onAodEnabledChange: (Boolean) -> Unit,
|
||||||
|
onDndEnabledChange: (Boolean) -> Unit,
|
||||||
onAlarmSoundChanged: (Uri?) -> Unit,
|
onAlarmSoundChanged: (Uri?) -> Unit,
|
||||||
onThemeChange: (String) -> Unit,
|
onThemeChange: (String) -> Unit,
|
||||||
onColorSchemeChange: (Color) -> Unit,
|
onColorSchemeChange: (Color) -> Unit,
|
||||||
@@ -270,11 +275,13 @@ private fun SettingsScreen(
|
|||||||
entry<Screen.Settings.Timer> {
|
entry<Screen.Settings.Timer> {
|
||||||
TimerSettings(
|
TimerSettings(
|
||||||
aodEnabled = preferencesState.aodEnabled,
|
aodEnabled = preferencesState.aodEnabled,
|
||||||
|
dndEnabled = dndEnabled,
|
||||||
focusTimeInputFieldState = focusTimeInputFieldState,
|
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||||
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||||
sessionsSliderState = sessionsSliderState,
|
sessionsSliderState = sessionsSliderState,
|
||||||
onAodEnabledChange = onAodEnabledChange,
|
onAodEnabledChange = onAodEnabledChange,
|
||||||
|
onDndEnabledChange = onDndEnabledChange,
|
||||||
onBack = backStack::removeLastOrNull
|
onBack = backStack::removeLastOrNull
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,11 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen.screens
|
package org.nsh07.pomodoro.ui.settingsScreen.screens
|
||||||
|
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.provider.Settings
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.horizontalScroll
|
import androidx.compose.foundation.horizontalScroll
|
||||||
@@ -31,6 +36,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.input.TextFieldState
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
@@ -51,6 +57,7 @@ import androidx.compose.material3.SwitchDefaults
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -59,6 +66,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
@@ -76,19 +84,58 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.cardShape
|
|||||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.middleListItemShape
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun TimerSettings(
|
fun TimerSettings(
|
||||||
aodEnabled: Boolean,
|
aodEnabled: Boolean,
|
||||||
|
dndEnabled: Boolean,
|
||||||
focusTimeInputFieldState: TextFieldState,
|
focusTimeInputFieldState: TextFieldState,
|
||||||
shortBreakTimeInputFieldState: TextFieldState,
|
shortBreakTimeInputFieldState: TextFieldState,
|
||||||
longBreakTimeInputFieldState: TextFieldState,
|
longBreakTimeInputFieldState: TextFieldState,
|
||||||
sessionsSliderState: SliderState,
|
sessionsSliderState: SliderState,
|
||||||
onAodEnabledChange: (Boolean) -> Unit,
|
onAodEnabledChange: (Boolean) -> Unit,
|
||||||
|
onDndEnabledChange: (Boolean) -> Unit,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
|
val context = LocalContext.current
|
||||||
|
val appName = stringResource(R.string.app_name)
|
||||||
|
val notificationManagerService =
|
||||||
|
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
if (!notificationManagerService.isNotificationPolicyAccessGranted())
|
||||||
|
onDndEnabledChange(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
val switchItems = listOf(
|
||||||
|
SettingsSwitchItem(
|
||||||
|
checked = dndEnabled,
|
||||||
|
icon = R.drawable.dnd,
|
||||||
|
label = R.string.dnd,
|
||||||
|
description = R.string.dnd_desc,
|
||||||
|
onClick = {
|
||||||
|
if (it && !notificationManagerService.isNotificationPolicyAccessGranted()) {
|
||||||
|
val intent = Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS)
|
||||||
|
Toast.makeText(context, "Enable permission for \"$appName\"", Toast.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
context.startActivity(intent)
|
||||||
|
} else if (!it && notificationManagerService.isNotificationPolicyAccessGranted()) {
|
||||||
|
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
|
||||||
|
}
|
||||||
|
onDndEnabledChange(it)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
SettingsSwitchItem(
|
||||||
|
checked = aodEnabled,
|
||||||
|
icon = R.drawable.aod,
|
||||||
|
label = R.string.always_on_display,
|
||||||
|
description = R.string.always_on_display_desc,
|
||||||
|
onClick = onAodEnabledChange
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
Column(modifier.nestedScroll(scrollBehavior.nestedScrollConnection)) {
|
||||||
LargeFlexibleTopAppBar(
|
LargeFlexibleTopAppBar(
|
||||||
@@ -213,14 +260,7 @@ fun TimerSettings(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
item { Spacer(Modifier.height(12.dp)) }
|
item { Spacer(Modifier.height(12.dp)) }
|
||||||
item {
|
itemsIndexed(switchItems) { index, item ->
|
||||||
val item = SettingsSwitchItem(
|
|
||||||
checked = aodEnabled,
|
|
||||||
icon = R.drawable.aod,
|
|
||||||
label = R.string.always_on_display,
|
|
||||||
description = R.string.always_on_display_desc,
|
|
||||||
onClick = onAodEnabledChange
|
|
||||||
)
|
|
||||||
ListItem(
|
ListItem(
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
@@ -254,7 +294,13 @@ fun TimerSettings(
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
colors = listItemColors,
|
colors = listItemColors,
|
||||||
modifier = Modifier.clip(cardShape)
|
modifier = Modifier.clip(
|
||||||
|
when (index) {
|
||||||
|
0 -> topListItemShape
|
||||||
|
switchItems.size - 1 -> bottomListItemShape
|
||||||
|
else -> middleListItemShape
|
||||||
|
}
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +357,9 @@ private fun TimerSettingsPreview() {
|
|||||||
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||||
sessionsSliderState = sessionsSliderState,
|
sessionsSliderState = sessionsSliderState,
|
||||||
aodEnabled = true,
|
aodEnabled = true,
|
||||||
|
dndEnabled = false,
|
||||||
onBack = {},
|
onBack = {},
|
||||||
onAodEnabledChange = {}
|
onAodEnabledChange = {},
|
||||||
|
onDndEnabledChange = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -85,6 +85,8 @@ class SettingsViewModel(
|
|||||||
preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged()
|
preferenceRepository.getBooleanPreferenceFlow("alarm_enabled").distinctUntilChanged()
|
||||||
val vibrateEnabled =
|
val vibrateEnabled =
|
||||||
preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged()
|
preferenceRepository.getBooleanPreferenceFlow("vibrate_enabled").distinctUntilChanged()
|
||||||
|
val dndEnabled =
|
||||||
|
preferenceRepository.getBooleanPreferenceFlow("dnd_enabled").distinctUntilChanged()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -179,6 +181,13 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun saveDndEnabled(enabled: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
timerRepository.dndEnabled = enabled
|
||||||
|
preferenceRepository.saveBooleanPreference("dnd_enabled", enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun saveAlarmSound(uri: Uri?) {
|
fun saveAlarmSound(uri: Uri?) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
timerRepository.alarmSoundUri = uri
|
timerRepository.alarmSoundUri = uri
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2025 Nishant Mishra
|
* Copyright (c) 2025 Nishant Mishra
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* This file is part of Tomato - a minimalist pomodoro timer for Android.
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
*
|
||||||
|
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
* General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
* Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with Tomato.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.nsh07.pomodoro.ui.timerScreen.viewModel
|
package org.nsh07.pomodoro.ui.timerScreen.viewModel
|
||||||
@@ -85,6 +95,9 @@ class TimerViewModel(
|
|||||||
timerRepository.vibrateEnabled =
|
timerRepository.vibrateEnabled =
|
||||||
preferenceRepository.getBooleanPreference("vibrate_enabled")
|
preferenceRepository.getBooleanPreference("vibrate_enabled")
|
||||||
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
|
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
|
||||||
|
timerRepository.dndEnabled =
|
||||||
|
preferenceRepository.getBooleanPreference("dnd_enabled")
|
||||||
|
?: preferenceRepository.saveBooleanPreference("dnd_enabled", false)
|
||||||
|
|
||||||
timerRepository.alarmSoundUri = (
|
timerRepository.alarmSoundUri = (
|
||||||
preferenceRepository.getStringPreference("alarm_sound")
|
preferenceRepository.getStringPreference("alarm_sound")
|
||||||
|
|||||||
26
app/src/main/res/drawable/dnd.xml
Normal file
26
app/src/main/res/drawable/dnd.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (c) 2025 Nishant Mishra
|
||||||
|
~
|
||||||
|
~ This file is part of Tomato - a minimalist pomodoro timer for Android.
|
||||||
|
~
|
||||||
|
~ Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
|
||||||
|
~ General Public License as published by the Free Software Foundation, either version 3 of the
|
||||||
|
~ License, or (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
|
||||||
|
~ the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
~ Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along with Tomato.
|
||||||
|
~ 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="M320,520h320q17,0 28.5,-11.5T680,480q0,-17 -11.5,-28.5T640,440L320,440q-17,0 -28.5,11.5T280,480q0,17 11.5,28.5T320,520ZM480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880Z" />
|
||||||
|
</vector>
|
||||||
@@ -79,4 +79,6 @@
|
|||||||
<string name="appearance">Appearance</string>
|
<string name="appearance">Appearance</string>
|
||||||
<string name="durations">Durations</string>
|
<string name="durations">Durations</string>
|
||||||
<string name="sound">Sound</string>
|
<string name="sound">Sound</string>
|
||||||
|
<string name="dnd">Do Not Disturb</string>
|
||||||
|
<string name="dnd_desc">Turn on DND when running a Focus timer</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user