feat(settings): disable editing time when service is running, auto reload

when navigating to timer screen
This commit is contained in:
Nishant Mishra
2025-11-09 22:08:47 +05:30
parent f8985564f7
commit 9ec3e6851f
8 changed files with 52 additions and 12 deletions

View File

@@ -21,6 +21,7 @@ import android.net.Uri
import android.provider.Settings
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.lightColorScheme
import kotlinx.coroutines.flow.MutableStateFlow
/**
* Interface that holds the timer durations for each timer type. This repository maintains a single
@@ -43,7 +44,7 @@ interface TimerRepository {
var alarmSoundUri: Uri?
var serviceRunning: Boolean
var serviceRunning: MutableStateFlow<Boolean>
}
/**
@@ -61,5 +62,5 @@ class AppTimerRepository : TimerRepository {
override var colorScheme = lightColorScheme()
override var alarmSoundUri: Uri? =
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
override var serviceRunning = false
override var serviceRunning = MutableStateFlow(false)
}

View File

@@ -98,12 +98,12 @@ class TimerService : Service() {
override fun onCreate() {
super.onCreate()
timerRepository.serviceRunning = true
timerRepository.serviceRunning.update { true }
alarm = initializeMediaPlayer()
}
override fun onDestroy() {
timerRepository.serviceRunning = false
timerRepository.serviceRunning.update { false }
runBlocking {
job.cancel()
saveTimeToDb()

View File

@@ -101,6 +101,7 @@ fun SettingsScreenRoot(
val longBreakTimeInputFieldState = viewModel.longBreakTimeTextFieldState
val isPlus by viewModel.isPlus.collectAsStateWithLifecycle()
val serviceRunning by viewModel.serviceRunning.collectAsStateWithLifecycle()
val settingsState by viewModel.settingsState.collectAsStateWithLifecycle()
@@ -115,6 +116,7 @@ fun SettingsScreenRoot(
SettingsScreen(
isPlus = isPlus,
serviceRunning = serviceRunning,
settingsState = settingsState,
backStack = backStack,
focusTimeInputFieldState = focusTimeInputFieldState,
@@ -132,6 +134,7 @@ fun SettingsScreenRoot(
@Composable
private fun SettingsScreen(
isPlus: Boolean,
serviceRunning: Boolean,
settingsState: SettingsState,
backStack: SnapshotStateList<Screen.Settings>,
focusTimeInputFieldState: TextFieldState,
@@ -292,6 +295,7 @@ private fun SettingsScreen(
entry<Screen.Settings.Timer> {
TimerSettings(
isPlus = isPlus,
serviceRunning = serviceRunning,
settingsState = settingsState,
focusTimeInputFieldState = focusTimeInputFieldState,
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,

View File

@@ -22,6 +22,7 @@ import androidx.annotation.StringRes
data class SettingsSwitchItem(
val checked: Boolean,
val enabled: Boolean = true,
@param:DrawableRes val icon: Int,
@param:StringRes val label: Int,
@param:StringRes val description: Int,

View File

@@ -46,12 +46,14 @@ import org.nsh07.pomodoro.ui.theme.CustomColors.listItemColors
@Composable
fun MinuteInputField(
state: TextFieldState,
enabled: Boolean,
shape: Shape,
modifier: Modifier = Modifier,
imeAction: ImeAction = ImeAction.Next
) {
BasicTextField(
state = state,
enabled = enabled,
lineLimits = TextFieldLineLimits.SingleLine,
inputTransformation = MinutesInputTransformation,
// outputTransformation = MinutesOutputTransformation,
@@ -63,7 +65,7 @@ fun MinuteInputField(
fontFamily = interClock,
fontSize = 57.sp,
letterSpacing = (-2).sp,
color = colorScheme.onSurfaceVariant,
color = if (enabled) colorScheme.onSurfaceVariant else colorScheme.outlineVariant,
textAlign = TextAlign.Center
),
cursorBrush = SolidColor(colorScheme.onSurface),

View File

@@ -50,6 +50,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Slider
@@ -60,7 +61,8 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberSliderState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -76,6 +78,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.service.TimerService
import org.nsh07.pomodoro.ui.settingsScreen.SettingsSwitchItem
import org.nsh07.pomodoro.ui.settingsScreen.components.MinuteInputField
import org.nsh07.pomodoro.ui.settingsScreen.components.PlusDivider
@@ -95,6 +98,7 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
@Composable
fun TimerSettings(
isPlus: Boolean,
serviceRunning: Boolean,
settingsState: SettingsState,
focusTimeInputFieldState: TextFieldState,
shortBreakTimeInputFieldState: TextFieldState,
@@ -111,14 +115,21 @@ fun TimerSettings(
val notificationManagerService =
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
LaunchedEffect(Unit) {
if (!notificationManagerService.isNotificationPolicyAccessGranted())
onAction(SettingsAction.SaveDndEnabled(false))
DisposableEffect(Unit) {
onDispose {
if (!serviceRunning) {
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.RESET.toString()
context.startService(it)
}
}
}
}
val switchItems = listOf(
SettingsSwitchItem(
checked = settingsState.dndEnabled,
enabled = !serviceRunning,
icon = R.drawable.dnd,
label = R.string.dnd,
description = R.string.dnd_desc,
@@ -171,6 +182,20 @@ fun TimerSettings(
.padding(horizontal = 16.dp)
) {
item {
CompositionLocalProvider(LocalContentColor provides colorScheme.error) {
AnimatedVisibility(serviceRunning) {
Column {
Spacer(Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Icon(painterResource(R.drawable.info), null)
Text("Reset the timer to change settings")
}
}
}
}
Spacer(Modifier.height(14.dp))
}
item {
@@ -190,6 +215,7 @@ fun TimerSettings(
)
MinuteInputField(
state = focusTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape(
topStart = topListItemShape.topStart,
bottomStart = topListItemShape.topStart,
@@ -210,6 +236,7 @@ fun TimerSettings(
)
MinuteInputField(
state = shortBreakTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape(middleListItemShape.topStart),
imeAction = ImeAction.Next
)
@@ -225,6 +252,7 @@ fun TimerSettings(
)
MinuteInputField(
state = longBreakTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape(
topStart = bottomListItemShape.topStart,
bottomStart = bottomListItemShape.topStart,
@@ -257,6 +285,7 @@ fun TimerSettings(
)
Slider(
state = sessionsSliderState,
enabled = !serviceRunning,
modifier = Modifier.padding(vertical = 4.dp)
)
}
@@ -281,6 +310,7 @@ fun TimerSettings(
trailingContent = {
Switch(
checked = item.checked,
enabled = item.enabled,
onCheckedChange = { item.onClick(it) },
thumbContent = {
if (item.checked) {
@@ -405,6 +435,7 @@ private fun TimerSettingsPreview() {
)
TimerSettings(
isPlus = false,
serviceRunning = true,
settingsState = remember { SettingsState() },
focusTimeInputFieldState = focusTimeInputFieldState,
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,

View File

@@ -54,6 +54,7 @@ class SettingsViewModel(
val backStack = mutableStateListOf<Screen.Settings>(Screen.Settings.Main)
val isPlus = billingManager.isPlus
val serviceRunning = timerRepository.serviceRunning.asStateFlow()
private val _settingsState = MutableStateFlow(SettingsState())
val settingsState = _settingsState.asStateFlow()
@@ -273,14 +274,14 @@ class SettingsViewModel(
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = (this[APPLICATION_KEY] as TomatoApplication)
val appBillingManager = application.container.billingManager
val appPreferenceRepository = application.container.appPreferenceRepository
val appTimerRepository = application.container.appTimerRepository
val appBillingManager = application.container.billingManager
SettingsViewModel(
billingManager = appBillingManager,
preferenceRepository = appPreferenceRepository,
timerRepository = appTimerRepository,
timerRepository = appTimerRepository
)
}
}

View File

@@ -70,7 +70,7 @@ class TimerViewModel(
private var pauseDuration = 0L
init {
if (!timerRepository.serviceRunning)
if (!timerRepository.serviceRunning.value)
viewModelScope.launch(Dispatchers.IO) {
timerRepository.focusTime =
preferenceRepository.getIntPreference("focus_time")?.toLong()