refactor(architecture): reorganize state variables into a separate state layer
This change also ensures a single source of truth for states used by the UI and those used by the Service. #117
This commit is contained in:
@@ -69,14 +69,14 @@ class MainActivity : ComponentActivity() {
|
|||||||
) {
|
) {
|
||||||
val colorScheme = colorScheme
|
val colorScheme = colorScheme
|
||||||
LaunchedEffect(colorScheme) {
|
LaunchedEffect(colorScheme) {
|
||||||
appContainer.appTimerRepository.colorScheme = colorScheme
|
appContainer.stateRepository.colorScheme = colorScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
AppScreen(
|
AppScreen(
|
||||||
isPlus = isPlus,
|
isPlus = isPlus,
|
||||||
isAODEnabled = settingsState.aodEnabled,
|
isAODEnabled = settingsState.aodEnabled,
|
||||||
setTimerFrequency = {
|
setTimerFrequency = {
|
||||||
appContainer.appTimerRepository.timerFrequency = it
|
appContainer.stateRepository.timerFrequency = it
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -86,13 +86,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
// Reduce the timer loop frequency when not visible to save battery power
|
// Reduce the timer loop frequency when not visible to save battery
|
||||||
appContainer.appTimerRepository.timerFrequency = 1f
|
appContainer.stateRepository.timerFrequency = 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
// Increase the timer loop frequency again when visible to make the progress smoother
|
// Increase the timer loop frequency again when visible to make the progress smoother
|
||||||
appContainer.appTimerRepository.timerFrequency = 60f
|
appContainer.stateRepository.timerFrequency = 60f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,19 +31,16 @@ 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.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.utils.millisecondsToStr
|
|
||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val appPreferenceRepository: AppPreferenceRepository
|
val appPreferenceRepository: AppPreferenceRepository
|
||||||
val appStatRepository: AppStatRepository
|
val appStatRepository: AppStatRepository
|
||||||
val appTimerRepository: AppTimerRepository
|
val stateRepository: StateRepository
|
||||||
val billingManager: BillingManager
|
val billingManager: BillingManager
|
||||||
val notificationManager: NotificationManagerCompat
|
val notificationManager: NotificationManagerCompat
|
||||||
val notificationManagerService: NotificationManager
|
val notificationManagerService: NotificationManager
|
||||||
val notificationBuilder: NotificationCompat.Builder
|
val notificationBuilder: NotificationCompat.Builder
|
||||||
val serviceHelper: ServiceHelper
|
val serviceHelper: ServiceHelper
|
||||||
val timerState: MutableStateFlow<TimerState>
|
|
||||||
val time: MutableStateFlow<Long>
|
val time: MutableStateFlow<Long>
|
||||||
var activityTurnScreenOn: (Boolean) -> Unit
|
var activityTurnScreenOn: (Boolean) -> Unit
|
||||||
}
|
}
|
||||||
@@ -58,7 +55,9 @@ class DefaultAppContainer(context: Context) : AppContainer {
|
|||||||
AppStatRepository(AppDatabase.getDatabase(context).statDao())
|
AppStatRepository(AppDatabase.getDatabase(context).statDao())
|
||||||
}
|
}
|
||||||
|
|
||||||
override val appTimerRepository: AppTimerRepository by lazy { AppTimerRepository() }
|
override val stateRepository: StateRepository by lazy {
|
||||||
|
StateRepository()
|
||||||
|
}
|
||||||
|
|
||||||
override val billingManager: BillingManager by lazy { BillingManagerProvider.manager }
|
override val billingManager: BillingManager by lazy { BillingManagerProvider.manager }
|
||||||
|
|
||||||
@@ -93,20 +92,9 @@ class DefaultAppContainer(context: Context) : AppContainer {
|
|||||||
ServiceHelper(context)
|
ServiceHelper(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val timerState: MutableStateFlow<TimerState> by lazy {
|
|
||||||
MutableStateFlow(
|
|
||||||
TimerState(
|
|
||||||
totalTime = appTimerRepository.focusTime,
|
|
||||||
timeStr = millisecondsToStr(appTimerRepository.focusTime),
|
|
||||||
nextTimeStr = millisecondsToStr(appTimerRepository.shortBreakTime)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override val time: MutableStateFlow<Long> by lazy {
|
override val time: MutableStateFlow<Long> by lazy {
|
||||||
MutableStateFlow(appTimerRepository.focusTime)
|
MutableStateFlow(stateRepository.settingsState.value.focusTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
override var activityTurnScreenOn: (Boolean) -> Unit = {}
|
override var activityTurnScreenOn: (Boolean) -> Unit = {}
|
||||||
|
|
||||||
}
|
}
|
||||||
31
app/src/main/java/org/nsh07/pomodoro/data/StateRepository.kt
Normal file
31
app/src/main/java/org/nsh07/pomodoro/data/StateRepository.kt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.data
|
||||||
|
|
||||||
|
import androidx.compose.material3.ColorScheme
|
||||||
|
import androidx.compose.material3.lightColorScheme
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsState
|
||||||
|
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
|
||||||
|
|
||||||
|
class StateRepository {
|
||||||
|
val timerState = MutableStateFlow(TimerState())
|
||||||
|
val settingsState = MutableStateFlow(SettingsState())
|
||||||
|
var timerFrequency: Float = 60f
|
||||||
|
var colorScheme: ColorScheme = lightColorScheme()
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.data
|
|
||||||
|
|
||||||
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
|
|
||||||
* source of truth for the timer durations for the various ViewModels in the app.
|
|
||||||
*/
|
|
||||||
interface TimerRepository {
|
|
||||||
var focusTime: Long
|
|
||||||
var shortBreakTime: Long
|
|
||||||
var longBreakTime: Long
|
|
||||||
|
|
||||||
var sessionLength: Int
|
|
||||||
|
|
||||||
var timerFrequency: Float
|
|
||||||
|
|
||||||
var alarmEnabled: Boolean
|
|
||||||
var vibrateEnabled: Boolean
|
|
||||||
var dndEnabled: Boolean
|
|
||||||
|
|
||||||
var colorScheme: ColorScheme
|
|
||||||
|
|
||||||
var alarmSoundUri: Uri?
|
|
||||||
|
|
||||||
var serviceRunning: MutableStateFlow<Boolean>
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See [TimerRepository] for more details
|
|
||||||
*/
|
|
||||||
class AppTimerRepository : TimerRepository {
|
|
||||||
override var focusTime = 25 * 60 * 1000L
|
|
||||||
override var shortBreakTime = 5 * 60 * 1000L
|
|
||||||
override var longBreakTime = 15 * 60 * 1000L
|
|
||||||
override var sessionLength = 4
|
|
||||||
override var timerFrequency: Float = 60f
|
|
||||||
override var alarmEnabled = true
|
|
||||||
override var vibrateEnabled = true
|
|
||||||
override var dndEnabled: Boolean = false
|
|
||||||
override var colorScheme = lightColorScheme()
|
|
||||||
override var alarmSoundUri: Uri? =
|
|
||||||
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
|
||||||
override var serviceRunning = MutableStateFlow(false)
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
@@ -53,12 +52,13 @@ class TimerService : Service() {
|
|||||||
(application as TomatoApplication).container
|
(application as TomatoApplication).container
|
||||||
}
|
}
|
||||||
|
|
||||||
private val timerRepository by lazy { appContainer.appTimerRepository }
|
private val stateRepository by lazy { appContainer.stateRepository }
|
||||||
private val statRepository by lazy { appContainer.appStatRepository }
|
private val statRepository by lazy { appContainer.appStatRepository }
|
||||||
private val notificationManager by lazy { appContainer.notificationManager }
|
private val notificationManager by lazy { appContainer.notificationManager }
|
||||||
private val notificationManagerService by lazy { appContainer.notificationManagerService }
|
private val notificationManagerService by lazy { appContainer.notificationManagerService }
|
||||||
private val notificationBuilder by lazy { appContainer.notificationBuilder }
|
private val notificationBuilder by lazy { appContainer.notificationBuilder }
|
||||||
private val _timerState by lazy { appContainer.timerState }
|
private val _timerState by lazy { stateRepository.timerState }
|
||||||
|
private val _settingsState by lazy { stateRepository.settingsState }
|
||||||
private val _time by lazy { appContainer.time }
|
private val _time by lazy { appContainer.time }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,8 +68,6 @@ class TimerService : Service() {
|
|||||||
get() = _time.value
|
get() = _time.value
|
||||||
set(value) = _time.update { value }
|
set(value) = _time.update { value }
|
||||||
|
|
||||||
private val timerState by lazy { _timerState.asStateFlow() }
|
|
||||||
|
|
||||||
private var cycles = 0
|
private var cycles = 0
|
||||||
private var startTime = 0L
|
private var startTime = 0L
|
||||||
private var pauseTime = 0L
|
private var pauseTime = 0L
|
||||||
@@ -94,7 +92,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cs by lazy { timerRepository.colorScheme }
|
private val cs by lazy { stateRepository.colorScheme }
|
||||||
|
|
||||||
private lateinit var notificationStyle: NotificationCompat.ProgressStyle
|
private lateinit var notificationStyle: NotificationCompat.ProgressStyle
|
||||||
|
|
||||||
@@ -104,12 +102,12 @@ class TimerService : Service() {
|
|||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
timerRepository.serviceRunning.update { true }
|
stateRepository.timerState.update { it.copy(serviceRunning = true) }
|
||||||
alarm = initializeMediaPlayer()
|
alarm = initializeMediaPlayer()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
timerRepository.serviceRunning.update { false }
|
stateRepository.timerState.update { it.copy(serviceRunning = false) }
|
||||||
runBlocking {
|
runBlocking {
|
||||||
job.cancel()
|
job.cancel()
|
||||||
saveTimeToDb()
|
saveTimeToDb()
|
||||||
@@ -129,7 +127,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Actions.RESET.toString() -> {
|
Actions.RESET.toString() -> {
|
||||||
if (timerState.value.timerRunning) toggleTimer()
|
if (_timerState.value.timerRunning) toggleTimer()
|
||||||
skipScope.launch {
|
skipScope.launch {
|
||||||
resetTimer()
|
resetTimer()
|
||||||
stopForegroundService()
|
stopForegroundService()
|
||||||
@@ -148,7 +146,7 @@ class TimerService : Service() {
|
|||||||
private fun toggleTimer() {
|
private fun toggleTimer() {
|
||||||
updateProgressSegments()
|
updateProgressSegments()
|
||||||
|
|
||||||
if (timerState.value.timerRunning) {
|
if (_timerState.value.timerRunning) {
|
||||||
setDoNotDisturb(false)
|
setDoNotDisturb(false)
|
||||||
notificationBuilder.clearActions().addTimerActions(
|
notificationBuilder.clearActions().addTimerActions(
|
||||||
this, R.drawable.play, getString(R.string.start)
|
this, R.drawable.play, getString(R.string.start)
|
||||||
@@ -159,7 +157,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
pauseTime = SystemClock.elapsedRealtime()
|
pauseTime = SystemClock.elapsedRealtime()
|
||||||
} else {
|
} else {
|
||||||
if (timerState.value.timerMode == TimerMode.FOCUS) setDoNotDisturb(true)
|
if (_timerState.value.timerMode == TimerMode.FOCUS) setDoNotDisturb(true)
|
||||||
else setDoNotDisturb(false)
|
else setDoNotDisturb(false)
|
||||||
notificationBuilder.clearActions().addTimerActions(
|
notificationBuilder.clearActions().addTimerActions(
|
||||||
this, R.drawable.pause, getString(R.string.stop)
|
this, R.drawable.pause, getString(R.string.stop)
|
||||||
@@ -171,19 +169,20 @@ class TimerService : Service() {
|
|||||||
|
|
||||||
timerScope.launch {
|
timerScope.launch {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!timerState.value.timerRunning) break
|
if (!_timerState.value.timerRunning) break
|
||||||
if (startTime == 0L) startTime = SystemClock.elapsedRealtime()
|
if (startTime == 0L) startTime = SystemClock.elapsedRealtime()
|
||||||
|
|
||||||
time = when (timerState.value.timerMode) {
|
val settingsState = _settingsState.value
|
||||||
TimerMode.FOCUS -> timerRepository.focusTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
time = when (_timerState.value.timerMode) {
|
||||||
|
TimerMode.FOCUS -> settingsState.focusTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
||||||
|
|
||||||
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
||||||
|
|
||||||
else -> timerRepository.longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
else -> settingsState.longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
iterations =
|
iterations =
|
||||||
(iterations + 1) % timerRepository.timerFrequency.toInt().coerceAtLeast(1)
|
(iterations + 1) % stateRepository.timerFrequency.toInt().coerceAtLeast(1)
|
||||||
|
|
||||||
if (iterations == 0) showTimerNotification(time.toInt())
|
if (iterations == 0) showTimerNotification(time.toInt())
|
||||||
|
|
||||||
@@ -199,7 +198,7 @@ class TimerService : Service() {
|
|||||||
timeStr = millisecondsToStr(time)
|
timeStr = millisecondsToStr(time)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val totalTime = timerState.value.totalTime
|
val totalTime = _timerState.value.totalTime
|
||||||
|
|
||||||
if (totalTime - time < lastSavedDuration)
|
if (totalTime - time < lastSavedDuration)
|
||||||
lastSavedDuration =
|
lastSavedDuration =
|
||||||
@@ -208,7 +207,7 @@ class TimerService : Service() {
|
|||||||
saveTimeToDb()
|
saveTimeToDb()
|
||||||
}
|
}
|
||||||
|
|
||||||
delay((1000f / timerRepository.timerFrequency).toLong())
|
delay((1000f / stateRepository.timerFrequency).toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,21 +220,23 @@ class TimerService : Service() {
|
|||||||
fun showTimerNotification(
|
fun showTimerNotification(
|
||||||
remainingTime: Int, paused: Boolean = false, complete: Boolean = false
|
remainingTime: Int, paused: Boolean = false, complete: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
|
|
||||||
if (complete) notificationBuilder.clearActions().addStopAlarmAction(this)
|
if (complete) notificationBuilder.clearActions().addStopAlarmAction(this)
|
||||||
|
|
||||||
val totalTime = when (timerState.value.timerMode) {
|
val totalTime = when (_timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> timerRepository.focusTime.toInt()
|
TimerMode.FOCUS -> settingsState.focusTime.toInt()
|
||||||
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt()
|
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
|
||||||
else -> timerRepository.longBreakTime.toInt()
|
else -> settingsState.longBreakTime.toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentTimer = when (timerState.value.timerMode) {
|
val currentTimer = when (_timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> getString(R.string.focus)
|
TimerMode.FOCUS -> getString(R.string.focus)
|
||||||
TimerMode.SHORT_BREAK -> getString(R.string.short_break)
|
TimerMode.SHORT_BREAK -> getString(R.string.short_break)
|
||||||
else -> getString(R.string.long_break)
|
else -> getString(R.string.long_break)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nextTimer = when (timerState.value.nextTimerMode) {
|
val nextTimer = when (_timerState.value.nextTimerMode) {
|
||||||
TimerMode.FOCUS -> getString(R.string.focus)
|
TimerMode.FOCUS -> getString(R.string.focus)
|
||||||
TimerMode.SHORT_BREAK -> getString(R.string.short_break)
|
TimerMode.SHORT_BREAK -> getString(R.string.short_break)
|
||||||
else -> getString(R.string.long_break)
|
else -> getString(R.string.long_break)
|
||||||
@@ -258,14 +259,14 @@ class TimerService : Service() {
|
|||||||
getString(
|
getString(
|
||||||
R.string.up_next_notification,
|
R.string.up_next_notification,
|
||||||
nextTimer,
|
nextTimer,
|
||||||
timerState.value.nextTimeStr
|
_timerState.value.nextTimeStr
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.setStyle(
|
.setStyle(
|
||||||
notificationStyle
|
notificationStyle
|
||||||
.setProgress( // Set the current progress by filling the previous intervals and part of the current interval
|
.setProgress( // Set the current progress by filling the previous intervals and part of the current interval
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
||||||
(totalTime - remainingTime) + ((cycles + 1) / 2) * timerRepository.focusTime.toInt() + (cycles / 2) * timerRepository.shortBreakTime.toInt()
|
(totalTime - remainingTime) + ((cycles + 1) / 2) * settingsState.focusTime.toInt() + (cycles / 2) * settingsState.shortBreakTime.toInt()
|
||||||
} else (totalTime - remainingTime)
|
} else (totalTime - remainingTime)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -283,37 +284,38 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateProgressSegments() {
|
private fun updateProgressSegments() {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
notificationStyle = NotificationCompat.ProgressStyle()
|
notificationStyle = NotificationCompat.ProgressStyle()
|
||||||
.also {
|
.also {
|
||||||
// Add all the Focus, Short break and long break intervals in order
|
// Add all the Focus, Short break and long break intervals in order
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
||||||
// Android 16 and later supports live updates
|
// Android 16 and later supports live updates
|
||||||
// Set progress bar sections if on Baklava or later
|
// Set progress bar sections if on Baklava or later
|
||||||
for (i in 0..<timerRepository.sessionLength * 2) {
|
for (i in 0..<settingsState.sessionLength * 2) {
|
||||||
if (i % 2 == 0) it.addProgressSegment(
|
if (i % 2 == 0) it.addProgressSegment(
|
||||||
NotificationCompat.ProgressStyle.Segment(
|
NotificationCompat.ProgressStyle.Segment(
|
||||||
timerRepository.focusTime.toInt()
|
settingsState.focusTime.toInt()
|
||||||
)
|
)
|
||||||
.setColor(cs.primary.toArgb())
|
.setColor(cs.primary.toArgb())
|
||||||
)
|
)
|
||||||
else if (i != (timerRepository.sessionLength * 2 - 1)) it.addProgressSegment(
|
else if (i != (settingsState.sessionLength * 2 - 1)) it.addProgressSegment(
|
||||||
NotificationCompat.ProgressStyle.Segment(
|
NotificationCompat.ProgressStyle.Segment(
|
||||||
timerRepository.shortBreakTime.toInt()
|
settingsState.shortBreakTime.toInt()
|
||||||
).setColor(cs.tertiary.toArgb())
|
).setColor(cs.tertiary.toArgb())
|
||||||
)
|
)
|
||||||
else it.addProgressSegment(
|
else it.addProgressSegment(
|
||||||
NotificationCompat.ProgressStyle.Segment(
|
NotificationCompat.ProgressStyle.Segment(
|
||||||
timerRepository.longBreakTime.toInt()
|
settingsState.longBreakTime.toInt()
|
||||||
).setColor(cs.tertiary.toArgb())
|
).setColor(cs.tertiary.toArgb())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
it.addProgressSegment(
|
it.addProgressSegment(
|
||||||
NotificationCompat.ProgressStyle.Segment(
|
NotificationCompat.ProgressStyle.Segment(
|
||||||
when (timerState.value.timerMode) {
|
when (_timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> timerRepository.focusTime.toInt()
|
TimerMode.FOCUS -> settingsState.focusTime.toInt()
|
||||||
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt()
|
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
|
||||||
else -> timerRepository.longBreakTime.toInt()
|
else -> settingsState.longBreakTime.toInt()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -322,10 +324,12 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun resetTimer() {
|
private suspend fun resetTimer() {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
|
|
||||||
updateProgressSegments()
|
updateProgressSegments()
|
||||||
saveTimeToDb()
|
saveTimeToDb()
|
||||||
lastSavedDuration = 0
|
lastSavedDuration = 0
|
||||||
time = timerRepository.focusTime
|
time = settingsState.focusTime
|
||||||
cycles = 0
|
cycles = 0
|
||||||
startTime = 0L
|
startTime = 0L
|
||||||
pauseTime = 0L
|
pauseTime = 0L
|
||||||
@@ -336,15 +340,16 @@ class TimerService : Service() {
|
|||||||
timerMode = TimerMode.FOCUS,
|
timerMode = TimerMode.FOCUS,
|
||||||
timeStr = millisecondsToStr(time),
|
timeStr = millisecondsToStr(time),
|
||||||
totalTime = time,
|
totalTime = time,
|
||||||
nextTimerMode = if (timerRepository.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
|
nextTimerMode = if (settingsState.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
|
||||||
nextTimeStr = millisecondsToStr(if (timerRepository.sessionLength > 1) timerRepository.shortBreakTime else timerRepository.longBreakTime),
|
nextTimeStr = millisecondsToStr(if (settingsState.sessionLength > 1) settingsState.shortBreakTime else settingsState.longBreakTime),
|
||||||
currentFocusCount = 1,
|
currentFocusCount = 1,
|
||||||
totalFocusCount = timerRepository.sessionLength
|
totalFocusCount = settingsState.sessionLength
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun skipTimer(fromButton: Boolean = false) {
|
private suspend fun skipTimer(fromButton: Boolean = false) {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
updateProgressSegments()
|
updateProgressSegments()
|
||||||
saveTimeToDb()
|
saveTimeToDb()
|
||||||
updateProgressSegments()
|
updateProgressSegments()
|
||||||
@@ -354,30 +359,30 @@ class TimerService : Service() {
|
|||||||
pauseTime = 0L
|
pauseTime = 0L
|
||||||
pauseDuration = 0L
|
pauseDuration = 0L
|
||||||
|
|
||||||
cycles = (cycles + 1) % (timerRepository.sessionLength * 2)
|
cycles = (cycles + 1) % (settingsState.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 = settingsState.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 == (settingsState.sessionLength - 1) * 2) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
||||||
nextTimeStr = if (cycles == (timerRepository.sessionLength - 1) * 2) millisecondsToStr(
|
nextTimeStr = if (cycles == (settingsState.sessionLength - 1) * 2) millisecondsToStr(
|
||||||
timerRepository.longBreakTime
|
settingsState.longBreakTime
|
||||||
) else millisecondsToStr(
|
) else millisecondsToStr(
|
||||||
timerRepository.shortBreakTime
|
settingsState.shortBreakTime
|
||||||
),
|
),
|
||||||
currentFocusCount = cycles / 2 + 1,
|
currentFocusCount = cycles / 2 + 1,
|
||||||
totalFocusCount = timerRepository.sessionLength
|
totalFocusCount = settingsState.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 == (settingsState.sessionLength * 2) - 1
|
||||||
time = if (long) timerRepository.longBreakTime else timerRepository.shortBreakTime
|
time = if (long) settingsState.longBreakTime else settingsState.shortBreakTime
|
||||||
|
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
@@ -385,14 +390,15 @@ class TimerService : Service() {
|
|||||||
timeStr = millisecondsToStr(time),
|
timeStr = millisecondsToStr(time),
|
||||||
totalTime = time,
|
totalTime = time,
|
||||||
nextTimerMode = TimerMode.FOCUS,
|
nextTimerMode = TimerMode.FOCUS,
|
||||||
nextTimeStr = millisecondsToStr(timerRepository.focusTime)
|
nextTimeStr = millisecondsToStr(settingsState.focusTime)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startAlarm() {
|
fun startAlarm() {
|
||||||
if (timerRepository.alarmEnabled) alarm?.start()
|
val settingsState = _settingsState.value
|
||||||
|
if (settingsState.alarmEnabled) alarm?.start()
|
||||||
|
|
||||||
appContainer.activityTurnScreenOn(true)
|
appContainer.activityTurnScreenOn(true)
|
||||||
|
|
||||||
@@ -401,7 +407,7 @@ class TimerService : Service() {
|
|||||||
stopAlarm()
|
stopAlarm()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timerRepository.vibrateEnabled) {
|
if (settingsState.vibrateEnabled) {
|
||||||
if (!vibrator.hasVibrator()) {
|
if (!vibrator.hasVibrator()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -413,14 +419,15 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun stopAlarm() {
|
fun stopAlarm() {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
autoAlarmStopScope?.cancel()
|
autoAlarmStopScope?.cancel()
|
||||||
|
|
||||||
if (timerRepository.alarmEnabled) {
|
if (settingsState.alarmEnabled) {
|
||||||
alarm?.pause()
|
alarm?.pause()
|
||||||
alarm?.seekTo(0)
|
alarm?.seekTo(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timerRepository.vibrateEnabled) {
|
if (settingsState.vibrateEnabled) {
|
||||||
vibrator.cancel()
|
vibrator.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,10 +441,10 @@ class TimerService : Service() {
|
|||||||
getString(R.string.start_next)
|
getString(R.string.start_next)
|
||||||
)
|
)
|
||||||
showTimerNotification(
|
showTimerNotification(
|
||||||
when (timerState.value.timerMode) {
|
when (_timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> timerRepository.focusTime.toInt()
|
TimerMode.FOCUS -> settingsState.focusTime.toInt()
|
||||||
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt()
|
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
|
||||||
else -> timerRepository.longBreakTime.toInt()
|
else -> settingsState.longBreakTime.toInt()
|
||||||
}, paused = true, complete = false
|
}, paused = true, complete = false
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -451,7 +458,7 @@ class TimerService : Service() {
|
|||||||
.setUsage(AudioAttributes.USAGE_ALARM)
|
.setUsage(AudioAttributes.USAGE_ALARM)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
timerRepository.alarmSoundUri?.let {
|
_settingsState.value.alarmSoundUri?.let {
|
||||||
setDataSource(applicationContext, it)
|
setDataSource(applicationContext, it)
|
||||||
prepare()
|
prepare()
|
||||||
}
|
}
|
||||||
@@ -463,7 +470,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setDoNotDisturb(doNotDisturb: Boolean) {
|
private fun setDoNotDisturb(doNotDisturb: Boolean) {
|
||||||
if (timerRepository.dndEnabled && notificationManagerService.isNotificationPolicyAccessGranted()) {
|
if (_settingsState.value.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)
|
||||||
@@ -477,8 +484,8 @@ class TimerService : Service() {
|
|||||||
|
|
||||||
suspend fun saveTimeToDb() {
|
suspend fun saveTimeToDb() {
|
||||||
saveLock.withLock {
|
saveLock.withLock {
|
||||||
val elapsedTime = timerState.value.totalTime - time
|
val elapsedTime = _timerState.value.totalTime - time
|
||||||
when (timerState.value.timerMode) {
|
when (_timerState.value.timerMode) {
|
||||||
TimerMode.FOCUS -> statRepository.addFocusTime(
|
TimerMode.FOCUS -> statRepository.addFocusTime(
|
||||||
(elapsedTime - lastSavedDuration).coerceAtLeast(0L)
|
(elapsedTime - lastSavedDuration).coerceAtLeast(0L)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.net.toUri
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.nsh07.pomodoro.R
|
import org.nsh07.pomodoro.R
|
||||||
@@ -92,10 +91,10 @@ fun AlarmSettings(
|
|||||||
|
|
||||||
var alarmName by remember { mutableStateOf("...") }
|
var alarmName by remember { mutableStateOf("...") }
|
||||||
|
|
||||||
LaunchedEffect(settingsState.alarmSound) {
|
LaunchedEffect(settingsState.alarmSoundUri) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
alarmName =
|
alarmName =
|
||||||
RingtoneManager.getRingtone(context, settingsState.alarmSound.toUri())
|
RingtoneManager.getRingtone(context, settingsState.alarmSoundUri)
|
||||||
?.getTitle(context) ?: ""
|
?.getTitle(context) ?: ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -119,11 +118,11 @@ fun AlarmSettings(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("LocalContextGetResourceValueCall")
|
@SuppressLint("LocalContextGetResourceValueCall")
|
||||||
val ringtonePickerIntent = remember(settingsState.alarmSound) {
|
val ringtonePickerIntent = remember(settingsState.alarmSoundUri) {
|
||||||
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
Intent(RingtoneManager.ACTION_RINGTONE_PICKER).apply {
|
||||||
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
|
putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_ALARM)
|
||||||
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(R.string.alarm_sound))
|
putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, context.getString(R.string.alarm_sound))
|
||||||
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, settingsState.alarmSound.toUri())
|
putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, settingsState.alarmSoundUri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,17 +17,27 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
|
package org.nsh07.pomodoro.ui.settingsScreen.viewModel
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.Settings
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class SettingsState(
|
data class SettingsState(
|
||||||
val theme: String = "auto",
|
val theme: String = "auto",
|
||||||
val alarmSound: String = "",
|
|
||||||
val colorScheme: String = Color.White.toString(),
|
val colorScheme: String = Color.White.toString(),
|
||||||
val blackTheme: Boolean = false,
|
val blackTheme: Boolean = false,
|
||||||
val aodEnabled: Boolean = false,
|
val aodEnabled: Boolean = false,
|
||||||
val alarmEnabled: Boolean = true,
|
val alarmEnabled: Boolean = true,
|
||||||
val vibrateEnabled: Boolean = true,
|
val vibrateEnabled: Boolean = true,
|
||||||
val dndEnabled: Boolean = false
|
val dndEnabled: Boolean = false,
|
||||||
|
|
||||||
|
val focusTime: Long = 25 * 60 * 1000L,
|
||||||
|
val shortBreakTime: Long = 5 * 60 * 1000L,
|
||||||
|
val longBreakTime: Long = 15 * 60 * 1000L,
|
||||||
|
|
||||||
|
val sessionLength: Int = 4,
|
||||||
|
|
||||||
|
val alarmSoundUri: Uri? =
|
||||||
|
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import androidx.compose.material3.SliderState
|
|||||||
import androidx.compose.runtime.mutableStateListOf
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.ViewModel
|
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
|
||||||
@@ -35,51 +36,57 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.FlowPreview
|
import kotlinx.coroutines.FlowPreview
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.nsh07.pomodoro.TomatoApplication
|
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.PreferenceRepository
|
||||||
import org.nsh07.pomodoro.data.TimerRepository
|
import org.nsh07.pomodoro.data.StateRepository
|
||||||
import org.nsh07.pomodoro.service.ServiceHelper
|
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.TimerAction
|
||||||
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode
|
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode
|
||||||
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
|
|
||||||
import org.nsh07.pomodoro.utils.millisecondsToStr
|
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: PreferenceRepository,
|
||||||
|
private val stateRepository: StateRepository,
|
||||||
private val serviceHelper: ServiceHelper,
|
private val serviceHelper: ServiceHelper,
|
||||||
private val time: MutableStateFlow<Long>,
|
private val time: MutableStateFlow<Long>
|
||||||
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()
|
val serviceRunning = stateRepository.timerState.map { it.serviceRunning }
|
||||||
|
.stateIn(
|
||||||
|
viewModelScope,
|
||||||
|
SharingStarted.WhileSubscribed(5000),
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
private val _settingsState = MutableStateFlow(SettingsState())
|
private val _settingsState = stateRepository.settingsState
|
||||||
val settingsState = _settingsState.asStateFlow()
|
val settingsState = _settingsState.asStateFlow()
|
||||||
|
|
||||||
val focusTimeTextFieldState by lazy {
|
val focusTimeTextFieldState by lazy {
|
||||||
TextFieldState((timerRepository.focusTime / 60000).toString())
|
TextFieldState((_settingsState.value.focusTime / 60000).toString())
|
||||||
}
|
}
|
||||||
val shortBreakTimeTextFieldState by lazy {
|
val shortBreakTimeTextFieldState by lazy {
|
||||||
TextFieldState((timerRepository.shortBreakTime / 60000).toString())
|
TextFieldState((_settingsState.value.shortBreakTime / 60000).toString())
|
||||||
}
|
}
|
||||||
val longBreakTimeTextFieldState by lazy {
|
val longBreakTimeTextFieldState by lazy {
|
||||||
TextFieldState((timerRepository.longBreakTime / 60000).toString())
|
TextFieldState((_settingsState.value.longBreakTime / 60000).toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
val sessionsSliderState by lazy {
|
val sessionsSliderState by lazy {
|
||||||
SliderState(
|
SliderState(
|
||||||
value = timerRepository.sessionLength.toFloat(),
|
value = _settingsState.value.sessionLength.toFloat(),
|
||||||
steps = 4,
|
steps = 4,
|
||||||
valueRange = 1f..6f,
|
valueRange = 1f..6f,
|
||||||
onValueChangeFinished = ::updateSessionLength
|
onValueChangeFinished = ::updateSessionLength
|
||||||
@@ -110,11 +117,15 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSessionLength() {
|
private fun updateSessionLength() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
timerRepository.sessionLength = preferenceRepository.saveIntPreference(
|
_settingsState.update { currentState ->
|
||||||
"session_length",
|
currentState.copy(
|
||||||
sessionsSliderState.value.toInt()
|
sessionLength = preferenceRepository.saveIntPreference(
|
||||||
)
|
"session_length",
|
||||||
|
sessionsSliderState.value.toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
refreshTimer()
|
refreshTimer()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,11 +136,13 @@ class SettingsViewModel(
|
|||||||
.debounce(500)
|
.debounce(500)
|
||||||
.collect {
|
.collect {
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
timerRepository.focusTime = it.toString().toLong() * 60 * 1000
|
_settingsState.update { currentState ->
|
||||||
|
currentState.copy(focusTime = it.toString().toLong() * 60 * 1000)
|
||||||
|
}
|
||||||
refreshTimer()
|
refreshTimer()
|
||||||
preferenceRepository.saveIntPreference(
|
preferenceRepository.saveIntPreference(
|
||||||
"focus_time",
|
"focus_time",
|
||||||
timerRepository.focusTime.toInt()
|
_settingsState.value.focusTime.toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,11 +152,13 @@ class SettingsViewModel(
|
|||||||
.debounce(500)
|
.debounce(500)
|
||||||
.collect {
|
.collect {
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
timerRepository.shortBreakTime = it.toString().toLong() * 60 * 1000
|
_settingsState.update { currentState ->
|
||||||
|
currentState.copy(shortBreakTime = it.toString().toLong() * 60 * 1000)
|
||||||
|
}
|
||||||
refreshTimer()
|
refreshTimer()
|
||||||
preferenceRepository.saveIntPreference(
|
preferenceRepository.saveIntPreference(
|
||||||
"short_break_time",
|
"short_break_time",
|
||||||
timerRepository.shortBreakTime.toInt()
|
_settingsState.value.shortBreakTime.toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,11 +168,13 @@ class SettingsViewModel(
|
|||||||
.debounce(500)
|
.debounce(500)
|
||||||
.collect {
|
.collect {
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
timerRepository.longBreakTime = it.toString().toLong() * 60 * 1000
|
_settingsState.update { currentState ->
|
||||||
|
currentState.copy(longBreakTime = it.toString().toLong() * 60 * 1000)
|
||||||
|
}
|
||||||
refreshTimer()
|
refreshTimer()
|
||||||
preferenceRepository.saveIntPreference(
|
preferenceRepository.saveIntPreference(
|
||||||
"long_break_time",
|
"long_break_time",
|
||||||
timerRepository.longBreakTime.toInt()
|
_settingsState.value.longBreakTime.toInt()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +190,6 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
private fun saveAlarmEnabled(enabled: Boolean) {
|
private fun saveAlarmEnabled(enabled: Boolean) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
timerRepository.alarmEnabled = enabled
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(alarmEnabled = enabled)
|
currentState.copy(alarmEnabled = enabled)
|
||||||
}
|
}
|
||||||
@@ -183,7 +199,6 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
private fun saveVibrateEnabled(enabled: Boolean) {
|
private fun saveVibrateEnabled(enabled: Boolean) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
timerRepository.vibrateEnabled = enabled
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(vibrateEnabled = enabled)
|
currentState.copy(vibrateEnabled = enabled)
|
||||||
}
|
}
|
||||||
@@ -193,7 +208,6 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
private fun saveDndEnabled(enabled: Boolean) {
|
private fun saveDndEnabled(enabled: Boolean) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
timerRepository.dndEnabled = enabled
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(dndEnabled = enabled)
|
currentState.copy(dndEnabled = enabled)
|
||||||
}
|
}
|
||||||
@@ -203,9 +217,8 @@ class SettingsViewModel(
|
|||||||
|
|
||||||
private fun saveAlarmSound(uri: Uri?) {
|
private fun saveAlarmSound(uri: Uri?) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
timerRepository.alarmSoundUri = uri
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(alarmSound = uri.toString())
|
currentState.copy(alarmSoundUri = uri)
|
||||||
}
|
}
|
||||||
preferenceRepository.saveStringPreference("alarm_sound", uri.toString())
|
preferenceRepository.saveStringPreference("alarm_sound", uri.toString())
|
||||||
}
|
}
|
||||||
@@ -248,32 +261,71 @@ class SettingsViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun reloadSettings() {
|
suspend fun reloadSettings() {
|
||||||
|
var settingsState = _settingsState.value
|
||||||
|
val focusTime =
|
||||||
|
preferenceRepository.getIntPreference("focus_time")?.toLong()
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"focus_time",
|
||||||
|
settingsState.focusTime.toInt()
|
||||||
|
).toLong()
|
||||||
|
val shortBreakTime =
|
||||||
|
preferenceRepository.getIntPreference("short_break_time")?.toLong()
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"short_break_time",
|
||||||
|
settingsState.shortBreakTime.toInt()
|
||||||
|
).toLong()
|
||||||
|
val longBreakTime =
|
||||||
|
preferenceRepository.getIntPreference("long_break_time")?.toLong()
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"long_break_time",
|
||||||
|
settingsState.longBreakTime.toInt()
|
||||||
|
).toLong()
|
||||||
|
val sessionLength =
|
||||||
|
preferenceRepository.getIntPreference("session_length")
|
||||||
|
?: preferenceRepository.saveIntPreference(
|
||||||
|
"session_length",
|
||||||
|
settingsState.sessionLength
|
||||||
|
)
|
||||||
|
|
||||||
|
val alarmSoundUri = (
|
||||||
|
preferenceRepository.getStringPreference("alarm_sound")
|
||||||
|
?: preferenceRepository.saveStringPreference(
|
||||||
|
"alarm_sound",
|
||||||
|
(Settings.System.DEFAULT_ALARM_ALERT_URI
|
||||||
|
?: Settings.System.DEFAULT_RINGTONE_URI).toString()
|
||||||
|
)
|
||||||
|
).toUri()
|
||||||
|
|
||||||
val theme = preferenceRepository.getStringPreference("theme")
|
val theme = preferenceRepository.getStringPreference("theme")
|
||||||
?: preferenceRepository.saveStringPreference("theme", "auto")
|
?: preferenceRepository.saveStringPreference("theme", settingsState.theme)
|
||||||
val colorScheme = preferenceRepository.getStringPreference("color_scheme")
|
val colorScheme = preferenceRepository.getStringPreference("color_scheme")
|
||||||
?: preferenceRepository.saveStringPreference("color_scheme", Color.White.toString())
|
?: preferenceRepository.saveStringPreference("color_scheme", settingsState.colorScheme)
|
||||||
val blackTheme = preferenceRepository.getBooleanPreference("black_theme")
|
val blackTheme = preferenceRepository.getBooleanPreference("black_theme")
|
||||||
?: preferenceRepository.saveBooleanPreference("black_theme", false)
|
?: preferenceRepository.saveBooleanPreference("black_theme", settingsState.blackTheme)
|
||||||
val aodEnabled = preferenceRepository.getBooleanPreference("aod_enabled")
|
val aodEnabled = preferenceRepository.getBooleanPreference("aod_enabled")
|
||||||
?: preferenceRepository.saveBooleanPreference("aod_enabled", false)
|
?: preferenceRepository.saveBooleanPreference("aod_enabled", settingsState.aodEnabled)
|
||||||
val alarmSound = preferenceRepository.getStringPreference("alarm_sound")
|
|
||||||
?: preferenceRepository.saveStringPreference(
|
|
||||||
"alarm_sound",
|
|
||||||
(Settings.System.DEFAULT_ALARM_ALERT_URI
|
|
||||||
?: Settings.System.DEFAULT_RINGTONE_URI).toString()
|
|
||||||
)
|
|
||||||
val alarmEnabled = preferenceRepository.getBooleanPreference("alarm_enabled")
|
val alarmEnabled = preferenceRepository.getBooleanPreference("alarm_enabled")
|
||||||
?: preferenceRepository.saveBooleanPreference("alarm_enabled", true)
|
?: preferenceRepository.saveBooleanPreference(
|
||||||
|
"alarm_enabled",
|
||||||
|
settingsState.alarmEnabled
|
||||||
|
)
|
||||||
val vibrateEnabled = preferenceRepository.getBooleanPreference("vibrate_enabled")
|
val vibrateEnabled = preferenceRepository.getBooleanPreference("vibrate_enabled")
|
||||||
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
|
?: preferenceRepository.saveBooleanPreference(
|
||||||
|
"vibrate_enabled",
|
||||||
|
settingsState.vibrateEnabled
|
||||||
|
)
|
||||||
val dndEnabled = preferenceRepository.getBooleanPreference("dnd_enabled")
|
val dndEnabled = preferenceRepository.getBooleanPreference("dnd_enabled")
|
||||||
?: preferenceRepository.saveBooleanPreference("dnd_enabled", false)
|
?: preferenceRepository.saveBooleanPreference("dnd_enabled", settingsState.dndEnabled)
|
||||||
|
|
||||||
_settingsState.update { currentState ->
|
_settingsState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
|
focusTime = focusTime,
|
||||||
|
shortBreakTime = shortBreakTime,
|
||||||
|
longBreakTime = longBreakTime,
|
||||||
|
sessionLength = sessionLength,
|
||||||
theme = theme,
|
theme = theme,
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
alarmSound = alarmSound,
|
alarmSoundUri = alarmSoundUri,
|
||||||
blackTheme = blackTheme,
|
blackTheme = blackTheme,
|
||||||
aodEnabled = aodEnabled,
|
aodEnabled = aodEnabled,
|
||||||
alarmEnabled = alarmEnabled,
|
alarmEnabled = alarmEnabled,
|
||||||
@@ -281,21 +333,40 @@ class SettingsViewModel(
|
|||||||
dndEnabled = dndEnabled
|
dndEnabled = dndEnabled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun refreshTimer() {
|
settingsState = _settingsState.value
|
||||||
if (!serviceRunning.value) {
|
|
||||||
time.update { timerRepository.focusTime }
|
|
||||||
|
|
||||||
timerState.update { currentState ->
|
time.update { settingsState.focusTime }
|
||||||
|
|
||||||
|
if (!stateRepository.timerState.value.serviceRunning)
|
||||||
|
stateRepository.timerState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
timerMode = TimerMode.FOCUS,
|
timerMode = TimerMode.FOCUS,
|
||||||
timeStr = millisecondsToStr(time.value),
|
timeStr = millisecondsToStr(time.value),
|
||||||
totalTime = time.value,
|
totalTime = time.value,
|
||||||
nextTimerMode = if (timerRepository.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
|
nextTimerMode = if (settingsState.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
|
||||||
nextTimeStr = millisecondsToStr(if (timerRepository.sessionLength > 1) timerRepository.shortBreakTime else timerRepository.longBreakTime),
|
nextTimeStr = millisecondsToStr(if (settingsState.sessionLength > 1) settingsState.shortBreakTime else settingsState.longBreakTime),
|
||||||
currentFocusCount = 1,
|
currentFocusCount = 1,
|
||||||
totalFocusCount = timerRepository.sessionLength
|
totalFocusCount = settingsState.sessionLength
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshTimer() {
|
||||||
|
if (!serviceRunning.value) {
|
||||||
|
val settingsState = _settingsState.value
|
||||||
|
|
||||||
|
time.update { settingsState.focusTime }
|
||||||
|
|
||||||
|
stateRepository.timerState.update { currentState ->
|
||||||
|
currentState.copy(
|
||||||
|
timerMode = TimerMode.FOCUS,
|
||||||
|
timeStr = millisecondsToStr(time.value),
|
||||||
|
totalTime = time.value,
|
||||||
|
nextTimerMode = if (settingsState.sessionLength > 1) TimerMode.SHORT_BREAK else TimerMode.LONG_BREAK,
|
||||||
|
nextTimeStr = millisecondsToStr(if (settingsState.sessionLength > 1) settingsState.shortBreakTime else settingsState.longBreakTime),
|
||||||
|
currentFocusCount = 1,
|
||||||
|
totalFocusCount = settingsState.sessionLength
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -307,18 +378,16 @@ class SettingsViewModel(
|
|||||||
val application = (this[APPLICATION_KEY] as TomatoApplication)
|
val application = (this[APPLICATION_KEY] as TomatoApplication)
|
||||||
val appBillingManager = application.container.billingManager
|
val appBillingManager = application.container.billingManager
|
||||||
val appPreferenceRepository = application.container.appPreferenceRepository
|
val appPreferenceRepository = application.container.appPreferenceRepository
|
||||||
val appTimerRepository = application.container.appTimerRepository
|
|
||||||
val serviceHelper = application.container.serviceHelper
|
val serviceHelper = application.container.serviceHelper
|
||||||
|
val stateRepository = application.container.stateRepository
|
||||||
val time = application.container.time
|
val time = application.container.time
|
||||||
val timerState = application.container.timerState
|
|
||||||
|
|
||||||
SettingsViewModel(
|
SettingsViewModel(
|
||||||
billingManager = appBillingManager,
|
billingManager = appBillingManager,
|
||||||
preferenceRepository = appPreferenceRepository,
|
preferenceRepository = appPreferenceRepository,
|
||||||
serviceHelper = serviceHelper,
|
serviceHelper = serviceHelper,
|
||||||
time = time,
|
stateRepository = stateRepository,
|
||||||
timerRepository = appTimerRepository,
|
time = time
|
||||||
timerState = timerState
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -17,7 +27,8 @@ data class TimerState(
|
|||||||
val showBrandTitle: Boolean = true,
|
val showBrandTitle: Boolean = true,
|
||||||
val currentFocusCount: Int = 1,
|
val currentFocusCount: Int = 1,
|
||||||
val totalFocusCount: Int = 4,
|
val totalFocusCount: Int = 4,
|
||||||
val alarmRinging: Boolean = false
|
val alarmRinging: Boolean = false,
|
||||||
|
val serviceRunning: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class TimerMode {
|
enum class TimerMode {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package org.nsh07.pomodoro.ui.timerScreen.viewModel
|
package org.nsh07.pomodoro.ui.timerScreen.viewModel
|
||||||
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.lifecycle.ViewModel
|
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
|
||||||
@@ -37,122 +35,49 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.nsh07.pomodoro.TomatoApplication
|
import org.nsh07.pomodoro.TomatoApplication
|
||||||
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.StateRepository
|
||||||
import org.nsh07.pomodoro.service.ServiceHelper
|
import org.nsh07.pomodoro.service.ServiceHelper
|
||||||
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(
|
||||||
private val preferenceRepository: PreferenceRepository,
|
|
||||||
private val serviceHelper: ServiceHelper,
|
private val serviceHelper: ServiceHelper,
|
||||||
|
private val stateRepository: StateRepository,
|
||||||
private val statRepository: StatRepository,
|
private val statRepository: StatRepository,
|
||||||
private val timerRepository: TimerRepository,
|
|
||||||
private val _timerState: MutableStateFlow<TimerState>,
|
|
||||||
private val _time: MutableStateFlow<Long>
|
private val _time: MutableStateFlow<Long>
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val timerState: StateFlow<TimerState> = _timerState.asStateFlow()
|
val timerState: StateFlow<TimerState> = stateRepository.timerState.asStateFlow()
|
||||||
|
|
||||||
val time: StateFlow<Long> = _time.asStateFlow()
|
val time: StateFlow<Long> = _time.asStateFlow()
|
||||||
|
|
||||||
val progress = _time.combine(_timerState) { remainingTime, uiState ->
|
val progress = _time.combine(stateRepository.timerState) { remainingTime, uiState ->
|
||||||
(uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime
|
(uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime
|
||||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0f)
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), 0f)
|
||||||
|
|
||||||
private var cycles = 0
|
|
||||||
|
|
||||||
private var startTime = 0L
|
|
||||||
private var pauseTime = 0L
|
|
||||||
private var pauseDuration = 0L
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (!timerRepository.serviceRunning.value)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
var lastDate = statRepository.getLastDate()
|
||||||
timerRepository.focusTime =
|
val today = LocalDate.now()
|
||||||
preferenceRepository.getIntPreference("focus_time")?.toLong()
|
|
||||||
?: preferenceRepository.saveIntPreference(
|
|
||||||
"focus_time",
|
|
||||||
timerRepository.focusTime.toInt()
|
|
||||||
).toLong()
|
|
||||||
timerRepository.shortBreakTime =
|
|
||||||
preferenceRepository.getIntPreference("short_break_time")?.toLong()
|
|
||||||
?: preferenceRepository.saveIntPreference(
|
|
||||||
"short_break_time",
|
|
||||||
timerRepository.shortBreakTime.toInt()
|
|
||||||
).toLong()
|
|
||||||
timerRepository.longBreakTime =
|
|
||||||
preferenceRepository.getIntPreference("long_break_time")?.toLong()
|
|
||||||
?: preferenceRepository.saveIntPreference(
|
|
||||||
"long_break_time",
|
|
||||||
timerRepository.longBreakTime.toInt()
|
|
||||||
).toLong()
|
|
||||||
timerRepository.sessionLength =
|
|
||||||
preferenceRepository.getIntPreference("session_length")
|
|
||||||
?: preferenceRepository.saveIntPreference(
|
|
||||||
"session_length",
|
|
||||||
timerRepository.sessionLength
|
|
||||||
)
|
|
||||||
|
|
||||||
timerRepository.alarmEnabled =
|
// Fills dates between today and lastDate with 0s to ensure continuous history
|
||||||
preferenceRepository.getBooleanPreference("alarm_enabled")
|
if (lastDate != null) {
|
||||||
?: preferenceRepository.saveBooleanPreference("alarm_enabled", true)
|
while (ChronoUnit.DAYS.between(lastDate, today) > 0) {
|
||||||
timerRepository.vibrateEnabled =
|
lastDate = lastDate?.plusDays(1)
|
||||||
preferenceRepository.getBooleanPreference("vibrate_enabled")
|
statRepository.insertStat(Stat(lastDate!!, 0, 0, 0, 0, 0))
|
||||||
?: preferenceRepository.saveBooleanPreference("vibrate_enabled", true)
|
|
||||||
timerRepository.dndEnabled =
|
|
||||||
preferenceRepository.getBooleanPreference("dnd_enabled")
|
|
||||||
?: preferenceRepository.saveBooleanPreference("dnd_enabled", false)
|
|
||||||
|
|
||||||
timerRepository.alarmSoundUri = (
|
|
||||||
preferenceRepository.getStringPreference("alarm_sound")
|
|
||||||
?: preferenceRepository.saveStringPreference(
|
|
||||||
"alarm_sound",
|
|
||||||
(Settings.System.DEFAULT_ALARM_ALERT_URI
|
|
||||||
?: Settings.System.DEFAULT_RINGTONE_URI).toString()
|
|
||||||
)
|
|
||||||
).toUri()
|
|
||||||
|
|
||||||
_time.update { timerRepository.focusTime }
|
|
||||||
cycles = 0
|
|
||||||
startTime = 0L
|
|
||||||
pauseTime = 0L
|
|
||||||
pauseDuration = 0L
|
|
||||||
|
|
||||||
_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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastDate = statRepository.getLastDate()
|
|
||||||
val today = LocalDate.now()
|
|
||||||
|
|
||||||
// Fills dates between today and lastDate with 0s to ensure continuous history
|
|
||||||
if (lastDate != null) {
|
|
||||||
while (ChronoUnit.DAYS.between(lastDate, today) > 0) {
|
|
||||||
lastDate = lastDate?.plusDays(1)
|
|
||||||
statRepository.insertStat(Stat(lastDate!!, 0, 0, 0, 0, 0))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
statRepository.insertStat(Stat(today, 0, 0, 0, 0, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(1500)
|
|
||||||
|
|
||||||
_timerState.update { currentState ->
|
|
||||||
currentState.copy(showBrandTitle = false)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
statRepository.insertStat(Stat(today, 0, 0, 0, 0, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delay(1500)
|
||||||
|
|
||||||
|
stateRepository.timerState.update { currentState ->
|
||||||
|
currentState.copy(showBrandTitle = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onAction(action: TimerAction) {
|
fun onAction(action: TimerAction) {
|
||||||
@@ -163,19 +88,15 @@ class TimerViewModel(
|
|||||||
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 appPreferenceRepository = application.container.appPreferenceRepository
|
|
||||||
val appStatRepository = application.container.appStatRepository
|
val appStatRepository = application.container.appStatRepository
|
||||||
val appTimerRepository = application.container.appTimerRepository
|
val stateRepository = application.container.stateRepository
|
||||||
val serviceHelper = application.container.serviceHelper
|
val serviceHelper = application.container.serviceHelper
|
||||||
val timerState = application.container.timerState
|
|
||||||
val time = application.container.time
|
val time = application.container.time
|
||||||
|
|
||||||
TimerViewModel(
|
TimerViewModel(
|
||||||
preferenceRepository = appPreferenceRepository,
|
|
||||||
serviceHelper = serviceHelper,
|
serviceHelper = serviceHelper,
|
||||||
|
stateRepository = stateRepository,
|
||||||
statRepository = appStatRepository,
|
statRepository = appStatRepository,
|
||||||
timerRepository = appTimerRepository,
|
|
||||||
_timerState = timerState,
|
|
||||||
_time = time
|
_time = time
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user