Merge pull request #133 from nsh07/auto-timer-update

Auto timer update
This commit is contained in:
Nishant Mishra
2025-11-09 22:52:09 +05:30
committed by GitHub
12 changed files with 153 additions and 47 deletions

View File

@@ -29,6 +29,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.billing.BillingManager import org.nsh07.pomodoro.billing.BillingManager
import org.nsh07.pomodoro.billing.BillingManagerProvider import org.nsh07.pomodoro.billing.BillingManagerProvider
import org.nsh07.pomodoro.service.ServiceHelper
import org.nsh07.pomodoro.service.addTimerActions import org.nsh07.pomodoro.service.addTimerActions
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
import org.nsh07.pomodoro.utils.millisecondsToStr import org.nsh07.pomodoro.utils.millisecondsToStr
@@ -41,6 +42,7 @@ interface AppContainer {
val notificationManager: NotificationManagerCompat val notificationManager: NotificationManagerCompat
val notificationManagerService: NotificationManager val notificationManagerService: NotificationManager
val notificationBuilder: NotificationCompat.Builder val notificationBuilder: NotificationCompat.Builder
val serviceHelper: ServiceHelper
val timerState: MutableStateFlow<TimerState> val timerState: MutableStateFlow<TimerState>
val time: MutableStateFlow<Long> val time: MutableStateFlow<Long>
var activityTurnScreenOn: (Boolean) -> Unit var activityTurnScreenOn: (Boolean) -> Unit
@@ -87,6 +89,10 @@ class DefaultAppContainer(context: Context) : AppContainer {
.setVisibility(VISIBILITY_PUBLIC) .setVisibility(VISIBILITY_PUBLIC)
} }
override val serviceHelper: ServiceHelper by lazy {
ServiceHelper(context)
}
override val timerState: MutableStateFlow<TimerState> by lazy { override val timerState: MutableStateFlow<TimerState> by lazy {
MutableStateFlow( MutableStateFlow(
TimerState( TimerState(

View File

@@ -21,6 +21,7 @@ import android.net.Uri
import android.provider.Settings import android.provider.Settings
import androidx.compose.material3.ColorScheme import androidx.compose.material3.ColorScheme
import androidx.compose.material3.lightColorScheme 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 * 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 alarmSoundUri: Uri?
var serviceRunning: Boolean var serviceRunning: MutableStateFlow<Boolean>
} }
/** /**
@@ -61,5 +62,5 @@ class AppTimerRepository : TimerRepository {
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
override var serviceRunning = false override var serviceRunning = MutableStateFlow(false)
} }

View File

@@ -0,0 +1,58 @@
/*
* 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/>.
*/
package org.nsh07.pomodoro.service
import android.content.Context
import android.content.Intent
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerAction
/**
* Helper class that holds a reference to [Context] and helps call [Context.startService] in
* [androidx.lifecycle.ViewModel]s. This class must be managed by an [android.app.Application] class
* to scope it to the Activity's lifecycle and prevent leaks.
*/
class ServiceHelper(private val context: Context) {
fun startService(action: TimerAction) {
when (action) {
TimerAction.ResetTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.RESET.toString()
context.startService(it)
}
is TimerAction.SkipTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.SKIP.toString()
context.startService(it)
}
TimerAction.StopAlarm ->
Intent(context, TimerService::class.java).also {
it.action =
TimerService.Actions.STOP_ALARM.toString()
context.startService(it)
}
TimerAction.ToggleTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.TOGGLE.toString()
context.startService(it)
}
}
}
}

View File

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

View File

@@ -63,7 +63,6 @@ import org.nsh07.pomodoro.ui.settingsScreen.SettingsScreenRoot
import org.nsh07.pomodoro.ui.statsScreen.StatsScreenRoot import org.nsh07.pomodoro.ui.statsScreen.StatsScreenRoot
import org.nsh07.pomodoro.ui.timerScreen.AlarmDialog import org.nsh07.pomodoro.ui.timerScreen.AlarmDialog
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerAction
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerViewModel import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerViewModel
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@@ -163,34 +162,7 @@ fun AppScreen(
timerState = uiState, timerState = uiState,
isPlus = isPlus, isPlus = isPlus,
progress = { progress }, progress = { progress },
onAction = { action -> onAction = timerViewModel::onAction,
when (action) {
TimerAction.ResetTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.RESET.toString()
context.startService(it)
}
is TimerAction.SkipTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.SKIP.toString()
context.startService(it)
}
TimerAction.StopAlarm ->
Intent(context, TimerService::class.java).also {
it.action =
TimerService.Actions.STOP_ALARM.toString()
context.startService(it)
}
TimerAction.ToggleTimer ->
Intent(context, TimerService::class.java).also {
it.action = TimerService.Actions.TOGGLE.toString()
context.startService(it)
}
}
},
modifier = modifier modifier = modifier
.padding( .padding(
start = contentPadding.calculateStartPadding(layoutDirection), start = contentPadding.calculateStartPadding(layoutDirection),

View File

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

View File

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

View File

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

View File

@@ -50,6 +50,7 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LargeFlexibleTopAppBar import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme.colorScheme import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.MaterialTheme.typography import androidx.compose.material3.MaterialTheme.typography
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
@@ -60,7 +61,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberSliderState import androidx.compose.material3.rememberSliderState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.CompositionLocalProvider
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
@@ -95,6 +96,7 @@ import org.nsh07.pomodoro.ui.theme.TomatoShapeDefaults.topListItemShape
@Composable @Composable
fun TimerSettings( fun TimerSettings(
isPlus: Boolean, isPlus: Boolean,
serviceRunning: Boolean,
settingsState: SettingsState, settingsState: SettingsState,
focusTimeInputFieldState: TextFieldState, focusTimeInputFieldState: TextFieldState,
shortBreakTimeInputFieldState: TextFieldState, shortBreakTimeInputFieldState: TextFieldState,
@@ -111,14 +113,10 @@ fun TimerSettings(
val notificationManagerService = val notificationManagerService =
remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager } remember { context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager }
LaunchedEffect(Unit) {
if (!notificationManagerService.isNotificationPolicyAccessGranted())
onAction(SettingsAction.SaveDndEnabled(false))
}
val switchItems = listOf( val switchItems = listOf(
SettingsSwitchItem( SettingsSwitchItem(
checked = settingsState.dndEnabled, checked = settingsState.dndEnabled,
enabled = !serviceRunning,
icon = R.drawable.dnd, icon = R.drawable.dnd,
label = R.string.dnd, label = R.string.dnd,
description = R.string.dnd_desc, description = R.string.dnd_desc,
@@ -171,6 +169,20 @@ fun TimerSettings(
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
) { ) {
item { 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(stringResource(R.string.timer_settings_reset_info))
}
}
}
}
Spacer(Modifier.height(14.dp)) Spacer(Modifier.height(14.dp))
} }
item { item {
@@ -190,6 +202,7 @@ fun TimerSettings(
) )
MinuteInputField( MinuteInputField(
state = focusTimeInputFieldState, state = focusTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape( shape = RoundedCornerShape(
topStart = topListItemShape.topStart, topStart = topListItemShape.topStart,
bottomStart = topListItemShape.topStart, bottomStart = topListItemShape.topStart,
@@ -210,6 +223,7 @@ fun TimerSettings(
) )
MinuteInputField( MinuteInputField(
state = shortBreakTimeInputFieldState, state = shortBreakTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape(middleListItemShape.topStart), shape = RoundedCornerShape(middleListItemShape.topStart),
imeAction = ImeAction.Next imeAction = ImeAction.Next
) )
@@ -225,6 +239,7 @@ fun TimerSettings(
) )
MinuteInputField( MinuteInputField(
state = longBreakTimeInputFieldState, state = longBreakTimeInputFieldState,
enabled = !serviceRunning,
shape = RoundedCornerShape( shape = RoundedCornerShape(
topStart = bottomListItemShape.topStart, topStart = bottomListItemShape.topStart,
bottomStart = bottomListItemShape.topStart, bottomStart = bottomListItemShape.topStart,
@@ -257,6 +272,7 @@ fun TimerSettings(
) )
Slider( Slider(
state = sessionsSliderState, state = sessionsSliderState,
enabled = !serviceRunning,
modifier = Modifier.padding(vertical = 4.dp) modifier = Modifier.padding(vertical = 4.dp)
) )
} }
@@ -281,6 +297,7 @@ fun TimerSettings(
trailingContent = { trailingContent = {
Switch( Switch(
checked = item.checked, checked = item.checked,
enabled = item.enabled,
onCheckedChange = { item.onClick(it) }, onCheckedChange = { item.onClick(it) },
thumbContent = { thumbContent = {
if (item.checked) { if (item.checked) {
@@ -405,6 +422,7 @@ private fun TimerSettingsPreview() {
) )
TimerSettings( TimerSettings(
isPlus = false, isPlus = false,
serviceRunning = true,
settingsState = remember { SettingsState() }, settingsState = remember { SettingsState() },
focusTimeInputFieldState = focusTimeInputFieldState, focusTimeInputFieldState = focusTimeInputFieldState,
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState, shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,

View File

@@ -43,17 +43,26 @@ import org.nsh07.pomodoro.TomatoApplication
import org.nsh07.pomodoro.billing.BillingManager import org.nsh07.pomodoro.billing.BillingManager
import org.nsh07.pomodoro.data.AppPreferenceRepository import org.nsh07.pomodoro.data.AppPreferenceRepository
import org.nsh07.pomodoro.data.TimerRepository import org.nsh07.pomodoro.data.TimerRepository
import org.nsh07.pomodoro.service.ServiceHelper
import org.nsh07.pomodoro.ui.Screen import org.nsh07.pomodoro.ui.Screen
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerAction
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
import org.nsh07.pomodoro.utils.millisecondsToStr
@OptIn(FlowPreview::class, ExperimentalMaterial3Api::class) @OptIn(FlowPreview::class, ExperimentalMaterial3Api::class)
class SettingsViewModel( class SettingsViewModel(
private val billingManager: BillingManager, private val billingManager: BillingManager,
private val preferenceRepository: AppPreferenceRepository, private val preferenceRepository: AppPreferenceRepository,
private val serviceHelper: ServiceHelper,
private val time: MutableStateFlow<Long>,
private val timerRepository: TimerRepository, private val timerRepository: TimerRepository,
private val timerState: MutableStateFlow<TimerState>
) : ViewModel() { ) : ViewModel() {
val backStack = mutableStateListOf<Screen.Settings>(Screen.Settings.Main) val backStack = mutableStateListOf<Screen.Settings>(Screen.Settings.Main)
val isPlus = billingManager.isPlus val isPlus = billingManager.isPlus
val serviceRunning = timerRepository.serviceRunning.asStateFlow()
private val _settingsState = MutableStateFlow(SettingsState()) private val _settingsState = MutableStateFlow(SettingsState())
val settingsState = _settingsState.asStateFlow() val settingsState = _settingsState.asStateFlow()
@@ -106,6 +115,7 @@ class SettingsViewModel(
"session_length", "session_length",
sessionsSliderState.value.toInt() sessionsSliderState.value.toInt()
) )
refreshTimer()
} }
} }
@@ -116,6 +126,7 @@ class SettingsViewModel(
.collect { .collect {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
timerRepository.focusTime = it.toString().toLong() * 60 * 1000 timerRepository.focusTime = it.toString().toLong() * 60 * 1000
refreshTimer()
preferenceRepository.saveIntPreference( preferenceRepository.saveIntPreference(
"focus_time", "focus_time",
timerRepository.focusTime.toInt() timerRepository.focusTime.toInt()
@@ -129,6 +140,7 @@ class SettingsViewModel(
.collect { .collect {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
timerRepository.shortBreakTime = it.toString().toLong() * 60 * 1000 timerRepository.shortBreakTime = it.toString().toLong() * 60 * 1000
refreshTimer()
preferenceRepository.saveIntPreference( preferenceRepository.saveIntPreference(
"short_break_time", "short_break_time",
timerRepository.shortBreakTime.toInt() timerRepository.shortBreakTime.toInt()
@@ -142,6 +154,7 @@ class SettingsViewModel(
.collect { .collect {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
timerRepository.longBreakTime = it.toString().toLong() * 60 * 1000 timerRepository.longBreakTime = it.toString().toLong() * 60 * 1000
refreshTimer()
preferenceRepository.saveIntPreference( preferenceRepository.saveIntPreference(
"long_break_time", "long_break_time",
timerRepository.longBreakTime.toInt() timerRepository.longBreakTime.toInt()
@@ -152,6 +165,7 @@ class SettingsViewModel(
} }
fun cancelTextFieldFlowCollection() { fun cancelTextFieldFlowCollection() {
if (!serviceRunning.value) serviceHelper.startService(TimerAction.ResetTimer)
focusFlowCollectionJob?.cancel() focusFlowCollectionJob?.cancel()
shortBreakFlowCollectionJob?.cancel() shortBreakFlowCollectionJob?.cancel()
longBreakFlowCollectionJob?.cancel() longBreakFlowCollectionJob?.cancel()
@@ -269,18 +283,42 @@ class SettingsViewModel(
} }
} }
private fun refreshTimer() {
if (!serviceRunning.value) {
time.update { timerRepository.focusTime }
timerState.update { currentState ->
currentState.copy(
timerMode = TimerMode.FOCUS,
timeStr = millisecondsToStr(time.value),
totalTime = time.value,
nextTimerMode = if (timerRepository.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
nextTimeStr = millisecondsToStr(if (timerRepository.sessionLength > 1) timerRepository.shortBreakTime else timerRepository.longBreakTime),
currentFocusCount = 1,
totalFocusCount = timerRepository.sessionLength
)
}
}
}
companion object { companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory { val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { initializer {
val application = (this[APPLICATION_KEY] as TomatoApplication) val application = (this[APPLICATION_KEY] as TomatoApplication)
val appBillingManager = application.container.billingManager
val appPreferenceRepository = application.container.appPreferenceRepository val appPreferenceRepository = application.container.appPreferenceRepository
val appTimerRepository = application.container.appTimerRepository val appTimerRepository = application.container.appTimerRepository
val appBillingManager = application.container.billingManager val serviceHelper = application.container.serviceHelper
val time = application.container.time
val timerState = application.container.timerState
SettingsViewModel( SettingsViewModel(
billingManager = appBillingManager, billingManager = appBillingManager,
preferenceRepository = appPreferenceRepository, preferenceRepository = appPreferenceRepository,
serviceHelper = serviceHelper,
time = time,
timerRepository = appTimerRepository, timerRepository = appTimerRepository,
timerState = timerState
) )
} }
} }

View File

@@ -17,10 +17,9 @@
package org.nsh07.pomodoro.ui.timerScreen.viewModel package org.nsh07.pomodoro.ui.timerScreen.viewModel
import android.app.Application
import android.provider.Settings import android.provider.Settings
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -42,19 +41,20 @@ import org.nsh07.pomodoro.data.PreferenceRepository
import org.nsh07.pomodoro.data.Stat import org.nsh07.pomodoro.data.Stat
import org.nsh07.pomodoro.data.StatRepository import org.nsh07.pomodoro.data.StatRepository
import org.nsh07.pomodoro.data.TimerRepository import org.nsh07.pomodoro.data.TimerRepository
import org.nsh07.pomodoro.service.ServiceHelper
import org.nsh07.pomodoro.utils.millisecondsToStr import org.nsh07.pomodoro.utils.millisecondsToStr
import java.time.LocalDate import java.time.LocalDate
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
@OptIn(FlowPreview::class) @OptIn(FlowPreview::class)
class TimerViewModel( class TimerViewModel(
application: Application,
private val preferenceRepository: PreferenceRepository, private val preferenceRepository: PreferenceRepository,
private val serviceHelper: ServiceHelper,
private val statRepository: StatRepository, private val statRepository: StatRepository,
private val timerRepository: TimerRepository, private val timerRepository: TimerRepository,
private val _timerState: MutableStateFlow<TimerState>, private val _timerState: MutableStateFlow<TimerState>,
private val _time: MutableStateFlow<Long> private val _time: MutableStateFlow<Long>
) : AndroidViewModel(application) { ) : ViewModel() {
val timerState: StateFlow<TimerState> = _timerState.asStateFlow() val timerState: StateFlow<TimerState> = _timerState.asStateFlow()
val time: StateFlow<Long> = _time.asStateFlow() val time: StateFlow<Long> = _time.asStateFlow()
@@ -70,7 +70,7 @@ class TimerViewModel(
private var pauseDuration = 0L private var pauseDuration = 0L
init { init {
if (!timerRepository.serviceRunning) if (!timerRepository.serviceRunning.value)
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
timerRepository.focusTime = timerRepository.focusTime =
preferenceRepository.getIntPreference("focus_time")?.toLong() preferenceRepository.getIntPreference("focus_time")?.toLong()
@@ -155,6 +155,10 @@ class TimerViewModel(
} }
} }
fun onAction(action: TimerAction) {
serviceHelper.startService(action)
}
companion object { companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory { val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer { initializer {
@@ -162,12 +166,13 @@ class TimerViewModel(
val appPreferenceRepository = application.container.appPreferenceRepository val appPreferenceRepository = application.container.appPreferenceRepository
val appStatRepository = application.container.appStatRepository val appStatRepository = application.container.appStatRepository
val appTimerRepository = application.container.appTimerRepository val appTimerRepository = application.container.appTimerRepository
val serviceHelper = application.container.serviceHelper
val timerState = application.container.timerState val timerState = application.container.timerState
val time = application.container.time val time = application.container.time
TimerViewModel( TimerViewModel(
application = application,
preferenceRepository = appPreferenceRepository, preferenceRepository = appPreferenceRepository,
serviceHelper = serviceHelper,
statRepository = appStatRepository, statRepository = appStatRepository,
timerRepository = appTimerRepository, timerRepository = appTimerRepository,
_timerState = timerState, _timerState = timerState,

View File

@@ -93,4 +93,5 @@
<string name="bmc">BuyMeACoffee</string> <string name="bmc">BuyMeACoffee</string>
<string name="selected">Selected</string> <string name="selected">Selected</string>
<string name="help_with_translation">Help with translation</string> <string name="help_with_translation">Help with translation</string>
<string name="timer_settings_reset_info">Reset the timer to change settings</string>
</resources> </resources>