Write a separate ViewModel for settings screen
This commit is contained in:
@@ -9,11 +9,11 @@ import org.nsh07.pomodoro.ui.AppScreen
|
|||||||
import org.nsh07.pomodoro.ui.NavItem
|
import org.nsh07.pomodoro.ui.NavItem
|
||||||
import org.nsh07.pomodoro.ui.Screen
|
import org.nsh07.pomodoro.ui.Screen
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
||||||
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
|
import org.nsh07.pomodoro.ui.viewModel.TimerViewModel
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private val viewModel: UiViewModel by viewModels(factoryProducer = { UiViewModel.Factory })
|
private val viewModel: TimerViewModel by viewModels(factoryProducer = { TimerViewModel.Factory })
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.content.Context
|
|||||||
|
|
||||||
interface AppContainer {
|
interface AppContainer {
|
||||||
val appPreferencesRepository: AppPreferenceRepository
|
val appPreferencesRepository: AppPreferenceRepository
|
||||||
|
val appTimerRepository: AppTimerRepository
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultAppContainer(context: Context) : AppContainer {
|
class DefaultAppContainer(context: Context) : AppContainer {
|
||||||
@@ -14,4 +15,8 @@ class DefaultAppContainer(context: Context) : AppContainer {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val appTimerRepository: AppTimerRepository by lazy {
|
||||||
|
AppTimerRepository()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
13
app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt
Normal file
13
app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package org.nsh07.pomodoro.data
|
||||||
|
|
||||||
|
interface TimerRepository {
|
||||||
|
var focusTime: Int
|
||||||
|
var shortBreakTime: Int
|
||||||
|
var longBreakTime: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppTimerRepository : TimerRepository {
|
||||||
|
override var focusTime = 25 * 60 * 1000
|
||||||
|
override var shortBreakTime = 5 * 60 * 1000
|
||||||
|
override var longBreakTime = 15 * 60 * 1000
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import androidx.compose.animation.scaleOut
|
|||||||
import androidx.compose.foundation.layout.calculateEndPadding
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
import androidx.compose.foundation.layout.calculateStartPadding
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.text.input.TextFieldState
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -25,7 +24,6 @@ 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
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
@@ -43,30 +41,20 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.nsh07.pomodoro.MainActivity.Companion.screens
|
import org.nsh07.pomodoro.MainActivity.Companion.screens
|
||||||
import org.nsh07.pomodoro.ui.settingsScreen.SettingsScreen
|
import org.nsh07.pomodoro.ui.settingsScreen.SettingsScreenRoot
|
||||||
import org.nsh07.pomodoro.ui.statsScreen.StatsScreen
|
import org.nsh07.pomodoro.ui.statsScreen.StatsScreen
|
||||||
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
|
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
|
||||||
import org.nsh07.pomodoro.ui.viewModel.UiViewModel
|
import org.nsh07.pomodoro.ui.viewModel.TimerViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppScreen(
|
fun AppScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: UiViewModel = viewModel(factory = UiViewModel.Factory)
|
viewModel: TimerViewModel = viewModel(factory = TimerViewModel.Factory)
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val remainingTime by viewModel.time.collectAsStateWithLifecycle()
|
val remainingTime by viewModel.time.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
|
||||||
viewModel.focusTimeTextFieldState
|
|
||||||
}
|
|
||||||
val shortBreakTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
|
||||||
viewModel.shortBreakTimeTextFieldState
|
|
||||||
}
|
|
||||||
val longBreakTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
|
||||||
viewModel.longBreakTimeTextFieldState
|
|
||||||
}
|
|
||||||
|
|
||||||
val progress by rememberUpdatedState((uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime)
|
val progress by rememberUpdatedState((uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime)
|
||||||
var showBrandTitle by remember { mutableStateOf(true) }
|
var showBrandTitle by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
@@ -149,7 +137,7 @@ fun AppScreen(
|
|||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
showBrandTitle = showBrandTitle,
|
showBrandTitle = showBrandTitle,
|
||||||
progress = { progress },
|
progress = { progress },
|
||||||
resetTimer = viewModel::updateTimerConstants,
|
resetTimer = viewModel::resetTimer,
|
||||||
skipTimer = viewModel::skipTimer,
|
skipTimer = viewModel::skipTimer,
|
||||||
toggleTimer = viewModel::toggleTimer,
|
toggleTimer = viewModel::toggleTimer,
|
||||||
modifier = modifier.padding(
|
modifier = modifier.padding(
|
||||||
@@ -161,12 +149,7 @@ fun AppScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
entry<Screen.Settings> {
|
entry<Screen.Settings> {
|
||||||
SettingsScreen(
|
SettingsScreenRoot(
|
||||||
focusTimeInputFieldState,
|
|
||||||
shortBreakTimeInputFieldState,
|
|
||||||
longBreakTimeInputFieldState,
|
|
||||||
viewModel::startTimeFieldsCollection,
|
|
||||||
viewModel::stopTimeFieldsCollection,
|
|
||||||
modifier = modifier.padding(
|
modifier = modifier.padding(
|
||||||
start = contentPadding.calculateStartPadding(layoutDirection),
|
start = contentPadding.calculateStartPadding(layoutDirection),
|
||||||
end = contentPadding.calculateEndPadding(layoutDirection),
|
end = contentPadding.calculateEndPadding(layoutDirection),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import androidx.compose.material3.TopAppBar
|
|||||||
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.DisposableEffect
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
@@ -41,27 +41,43 @@ import androidx.compose.ui.tooling.preview.Devices
|
|||||||
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.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import org.nsh07.pomodoro.R
|
import org.nsh07.pomodoro.R
|
||||||
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTitle
|
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTitle
|
||||||
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
import org.nsh07.pomodoro.ui.theme.TomatoTheme
|
||||||
|
import org.nsh07.pomodoro.ui.viewModel.SettingsViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsScreenRoot(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
|
||||||
|
) {
|
||||||
|
val focusTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
||||||
|
viewModel.focusTimeTextFieldState
|
||||||
|
}
|
||||||
|
val shortBreakTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
||||||
|
viewModel.shortBreakTimeTextFieldState
|
||||||
|
}
|
||||||
|
val longBreakTimeInputFieldState = rememberSaveable(saver = TextFieldState.Saver) {
|
||||||
|
viewModel.longBreakTimeTextFieldState
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsScreen(
|
||||||
|
focusTimeInputFieldState = focusTimeInputFieldState,
|
||||||
|
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
|
||||||
|
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreen(
|
private fun SettingsScreen(
|
||||||
focusTimeInputFieldState: TextFieldState,
|
focusTimeInputFieldState: TextFieldState,
|
||||||
shortBreakTimeInputFieldState: TextFieldState,
|
shortBreakTimeInputFieldState: TextFieldState,
|
||||||
longBreakTimeInputFieldState: TextFieldState,
|
longBreakTimeInputFieldState: TextFieldState,
|
||||||
startCollectingTimeFields: () -> Unit,
|
|
||||||
stopCollectingTimeFields: () -> Unit,
|
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
DisposableEffect(Unit) {
|
|
||||||
startCollectingTimeFields()
|
|
||||||
onDispose {
|
|
||||||
stopCollectingTimeFields()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
|
||||||
val sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f)
|
val sessionsSliderState = rememberSliderState(value = 3f, steps = 3, valueRange = 1f..5f)
|
||||||
|
|
||||||
@@ -197,8 +213,6 @@ fun SettingsScreenPreview() {
|
|||||||
focusTimeInputFieldState = rememberTextFieldState((25 * 60 * 1000).toString()),
|
focusTimeInputFieldState = rememberTextFieldState((25 * 60 * 1000).toString()),
|
||||||
shortBreakTimeInputFieldState = rememberTextFieldState((5 * 60 * 1000).toString()),
|
shortBreakTimeInputFieldState = rememberTextFieldState((5 * 60 * 1000).toString()),
|
||||||
longBreakTimeInputFieldState = rememberTextFieldState((15 * 60 * 1000).toString()),
|
longBreakTimeInputFieldState = rememberTextFieldState((15 * 60 * 1000).toString()),
|
||||||
startCollectingTimeFields = {},
|
|
||||||
stopCollectingTimeFields = {},
|
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package org.nsh07.pomodoro.ui.viewModel
|
||||||
|
|
||||||
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
|
import kotlinx.coroutines.FlowPreview
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.nsh07.pomodoro.TomatoApplication
|
||||||
|
import org.nsh07.pomodoro.data.AppPreferenceRepository
|
||||||
|
import org.nsh07.pomodoro.data.TimerRepository
|
||||||
|
|
||||||
|
@OptIn(FlowPreview::class)
|
||||||
|
class SettingsViewModel(
|
||||||
|
private val preferenceRepository: AppPreferenceRepository,
|
||||||
|
private val timerRepository: TimerRepository
|
||||||
|
) : ViewModel() {
|
||||||
|
val focusTimeTextFieldState =
|
||||||
|
TextFieldState((timerRepository.focusTime / 60000).toString())
|
||||||
|
val shortBreakTimeTextFieldState =
|
||||||
|
TextFieldState((timerRepository.shortBreakTime / 60000).toString())
|
||||||
|
val longBreakTimeTextFieldState =
|
||||||
|
TextFieldState((timerRepository.longBreakTime / 60000).toString())
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch {
|
||||||
|
snapshotFlow { focusTimeTextFieldState.text }
|
||||||
|
.debounce(500)
|
||||||
|
.collect {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
timerRepository.focusTime = preferenceRepository.saveIntPreference(
|
||||||
|
"focus_time",
|
||||||
|
it.toString().toInt() * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snapshotFlow { shortBreakTimeTextFieldState.text }
|
||||||
|
.debounce(500)
|
||||||
|
.collect {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
timerRepository.shortBreakTime = preferenceRepository.saveIntPreference(
|
||||||
|
"short_break_time",
|
||||||
|
it.toString().toInt() * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snapshotFlow { longBreakTimeTextFieldState.text }
|
||||||
|
.debounce(500)
|
||||||
|
.collect {
|
||||||
|
if (it.isNotEmpty()) {
|
||||||
|
timerRepository.longBreakTime = preferenceRepository.saveIntPreference(
|
||||||
|
"long_break_time",
|
||||||
|
it.toString().toInt() * 60 * 1000
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||||
|
initializer {
|
||||||
|
val application = (this[APPLICATION_KEY] as TomatoApplication)
|
||||||
|
val appPreferenceRepository = application.container.appPreferencesRepository
|
||||||
|
val appTimerRepository = application.container.appTimerRepository
|
||||||
|
|
||||||
|
SettingsViewModel(
|
||||||
|
preferenceRepository = appPreferenceRepository,
|
||||||
|
timerRepository = appTimerRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package org.nsh07.pomodoro.ui.viewModel
|
package org.nsh07.pomodoro.ui.viewModel
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.compose.foundation.text.input.TextFieldState
|
|
||||||
import androidx.compose.foundation.text.input.delete
|
|
||||||
import androidx.compose.runtime.snapshotFlow
|
|
||||||
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
|
||||||
@@ -17,44 +14,42 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.debounce
|
|
||||||
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.AppPreferenceRepository
|
import org.nsh07.pomodoro.data.AppPreferenceRepository
|
||||||
import java.util.Locale
|
import org.nsh07.pomodoro.data.TimerRepository
|
||||||
import kotlin.math.ceil
|
import org.nsh07.pomodoro.utils.millisecondsToStr
|
||||||
|
|
||||||
@OptIn(FlowPreview::class)
|
@OptIn(FlowPreview::class)
|
||||||
class UiViewModel(
|
class TimerViewModel(
|
||||||
private val preferenceRepository: AppPreferenceRepository
|
private val preferenceRepository: AppPreferenceRepository,
|
||||||
|
private val timerRepository: TimerRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
var focusTime = 25 * 60 * 1000
|
|
||||||
var shortBreakTime = 5 * 60 * 1000
|
|
||||||
var longBreakTime = 15 * 60 * 1000
|
|
||||||
|
|
||||||
val focusTimeTextFieldState = TextFieldState((focusTime / 60000).toString())
|
|
||||||
val shortBreakTimeTextFieldState = TextFieldState((shortBreakTime / 60000).toString())
|
|
||||||
val longBreakTimeTextFieldState = TextFieldState((longBreakTime / 60000).toString())
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
updateTimerConstants(true)
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
timerRepository.focusTime = preferenceRepository.getIntPreference("focus_time")
|
||||||
|
?: preferenceRepository.saveIntPreference("focus_time", timerRepository.focusTime)
|
||||||
|
timerRepository.shortBreakTime = preferenceRepository.getIntPreference("short_break_time")
|
||||||
|
?: preferenceRepository.saveIntPreference("short_break_time", timerRepository.shortBreakTime)
|
||||||
|
timerRepository.longBreakTime = preferenceRepository.getIntPreference("long_break_time")
|
||||||
|
?: preferenceRepository.saveIntPreference("long_break_time", timerRepository.longBreakTime)
|
||||||
|
|
||||||
|
resetTimer()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _uiState = MutableStateFlow(
|
private val _uiState = MutableStateFlow(
|
||||||
UiState(
|
UiState(
|
||||||
totalTime = focusTime,
|
totalTime = timerRepository.focusTime,
|
||||||
timeStr = millisecondsToStr(focusTime),
|
timeStr = millisecondsToStr(timerRepository.focusTime),
|
||||||
nextTimeStr = millisecondsToStr(shortBreakTime)
|
nextTimeStr = millisecondsToStr(timerRepository.shortBreakTime)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
|
||||||
var timerJob: Job? = null
|
var timerJob: Job? = null
|
||||||
var focusTimeJob: Job? = null
|
|
||||||
var shortBreakTimeJob: Job? = null
|
|
||||||
var longBreakTimeJob: Job? = null
|
|
||||||
|
|
||||||
private val _time = MutableStateFlow(focusTime)
|
private val _time = MutableStateFlow(timerRepository.focusTime)
|
||||||
val time: StateFlow<Int> = _time.asStateFlow()
|
val time: StateFlow<Int> = _time.asStateFlow()
|
||||||
|
|
||||||
private var cycles = 0
|
private var cycles = 0
|
||||||
@@ -63,7 +58,7 @@ class UiViewModel(
|
|||||||
private var pauseDuration = 0L
|
private var pauseDuration = 0L
|
||||||
|
|
||||||
fun resetTimer() {
|
fun resetTimer() {
|
||||||
_time.update { focusTime }
|
_time.update { timerRepository.focusTime }
|
||||||
cycles = 0
|
cycles = 0
|
||||||
startTime = 0L
|
startTime = 0L
|
||||||
pauseTime = 0L
|
pauseTime = 0L
|
||||||
@@ -75,7 +70,7 @@ class UiViewModel(
|
|||||||
timeStr = millisecondsToStr(time.value),
|
timeStr = millisecondsToStr(time.value),
|
||||||
totalTime = time.value,
|
totalTime = time.value,
|
||||||
nextTimerMode = TimerMode.SHORT_BREAK,
|
nextTimerMode = TimerMode.SHORT_BREAK,
|
||||||
nextTimeStr = millisecondsToStr(shortBreakTime)
|
nextTimeStr = millisecondsToStr(timerRepository.shortBreakTime)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,7 +82,7 @@ class UiViewModel(
|
|||||||
cycles = (cycles + 1) % 8
|
cycles = (cycles + 1) % 8
|
||||||
|
|
||||||
if (cycles % 2 == 0) {
|
if (cycles % 2 == 0) {
|
||||||
_time.update { focusTime }
|
_time.update { timerRepository.focusTime }
|
||||||
_uiState.update { currentState ->
|
_uiState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
timerMode = TimerMode.FOCUS,
|
timerMode = TimerMode.FOCUS,
|
||||||
@@ -95,15 +90,15 @@ class UiViewModel(
|
|||||||
totalTime = time.value,
|
totalTime = time.value,
|
||||||
nextTimerMode = if (cycles == 6) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
nextTimerMode = if (cycles == 6) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
|
||||||
nextTimeStr = if (cycles == 6) millisecondsToStr(
|
nextTimeStr = if (cycles == 6) millisecondsToStr(
|
||||||
longBreakTime
|
timerRepository.longBreakTime
|
||||||
) else millisecondsToStr(
|
) else millisecondsToStr(
|
||||||
shortBreakTime
|
timerRepository.shortBreakTime
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val long = cycles == 7
|
val long = cycles == 7
|
||||||
_time.update { if (long) longBreakTime else shortBreakTime }
|
_time.update { if (long) timerRepository.longBreakTime else timerRepository.shortBreakTime }
|
||||||
|
|
||||||
_uiState.update { currentState ->
|
_uiState.update { currentState ->
|
||||||
currentState.copy(
|
currentState.copy(
|
||||||
@@ -111,7 +106,7 @@ class UiViewModel(
|
|||||||
timeStr = millisecondsToStr(time.value),
|
timeStr = millisecondsToStr(time.value),
|
||||||
totalTime = time.value,
|
totalTime = time.value,
|
||||||
nextTimerMode = TimerMode.FOCUS,
|
nextTimerMode = TimerMode.FOCUS,
|
||||||
nextTimeStr = millisecondsToStr(focusTime)
|
nextTimeStr = millisecondsToStr(timerRepository.focusTime)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,13 +131,13 @@ class UiViewModel(
|
|||||||
_time.update {
|
_time.update {
|
||||||
when (uiState.value.timerMode) {
|
when (uiState.value.timerMode) {
|
||||||
TimerMode.FOCUS ->
|
TimerMode.FOCUS ->
|
||||||
focusTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
timerRepository.focusTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
||||||
|
|
||||||
TimerMode.SHORT_BREAK ->
|
TimerMode.SHORT_BREAK ->
|
||||||
shortBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
timerRepository.shortBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
||||||
|
|
||||||
else ->
|
else ->
|
||||||
longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
timerRepository.longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,92 +162,18 @@ class UiViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateTimerConstants(updateTextFields: Boolean = false) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
focusTime = preferenceRepository.getIntPreference("focus_time")
|
|
||||||
?: preferenceRepository.saveIntPreference("focus_time", focusTime)
|
|
||||||
shortBreakTime = preferenceRepository.getIntPreference("short_break_time")
|
|
||||||
?: preferenceRepository.saveIntPreference("short_break_time", shortBreakTime)
|
|
||||||
longBreakTime = preferenceRepository.getIntPreference("long_break_time")
|
|
||||||
?: preferenceRepository.saveIntPreference("long_break_time", longBreakTime)
|
|
||||||
|
|
||||||
if (updateTextFields) {
|
|
||||||
focusTimeTextFieldState.edit {
|
|
||||||
delete(0, length)
|
|
||||||
append((focusTime / 60000).toString())
|
|
||||||
}
|
|
||||||
shortBreakTimeTextFieldState.edit {
|
|
||||||
delete(0, length)
|
|
||||||
append((shortBreakTime / 60000).toString())
|
|
||||||
}
|
|
||||||
longBreakTimeTextFieldState.edit {
|
|
||||||
delete(0, length)
|
|
||||||
append((longBreakTime / 60000).toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetTimer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startTimeFieldsCollection() {
|
|
||||||
focusTimeJob = viewModelScope.launch {
|
|
||||||
snapshotFlow { focusTimeTextFieldState.text }
|
|
||||||
.debounce(500)
|
|
||||||
.collect {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
focusTime = preferenceRepository.saveIntPreference(
|
|
||||||
"focus_time",
|
|
||||||
it.toString().toInt() * 60 * 1000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
shortBreakTimeJob = viewModelScope.launch {
|
|
||||||
snapshotFlow { shortBreakTimeTextFieldState.text }
|
|
||||||
.debounce(500)
|
|
||||||
.collect {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
shortBreakTime = preferenceRepository.saveIntPreference(
|
|
||||||
"short_break_time",
|
|
||||||
it.toString().toInt() * 60 * 1000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
longBreakTimeJob = viewModelScope.launch {
|
|
||||||
snapshotFlow { longBreakTimeTextFieldState.text }
|
|
||||||
.debounce(500)
|
|
||||||
.collect {
|
|
||||||
if (it.isNotEmpty()) {
|
|
||||||
longBreakTime = preferenceRepository.saveIntPreference(
|
|
||||||
"long_break_time",
|
|
||||||
it.toString().toInt() * 60 * 1000
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stopTimeFieldsCollection() {
|
|
||||||
focusTimeJob?.cancel()
|
|
||||||
shortBreakTimeJob?.cancel()
|
|
||||||
longBreakTimeJob?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 appPreferenceRepository = application.container.appPreferencesRepository
|
val appPreferenceRepository = application.container.appPreferencesRepository
|
||||||
UiViewModel(preferenceRepository = appPreferenceRepository)
|
val appTimerRepository = application.container.appTimerRepository
|
||||||
|
|
||||||
|
TimerViewModel(
|
||||||
|
preferenceRepository = appPreferenceRepository,
|
||||||
|
timerRepository = appTimerRepository
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun millisecondsToStr(t: Int): String {
|
|
||||||
val min = (ceil(t / 1000.0).toInt() / 60)
|
|
||||||
val sec = (ceil(t / 1000.0).toInt() % 60)
|
|
||||||
return String.format(locale = Locale.getDefault(), "%02d:%02d", min, sec)
|
|
||||||
}
|
}
|
||||||
10
app/src/main/java/org/nsh07/pomodoro/utils/Utils.kt
Normal file
10
app/src/main/java/org/nsh07/pomodoro/utils/Utils.kt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package org.nsh07.pomodoro.utils
|
||||||
|
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
|
fun millisecondsToStr(t: Int): String {
|
||||||
|
val min = (ceil(t / 1000.0).toInt() / 60)
|
||||||
|
val sec = (ceil(t / 1000.0).toInt() % 60)
|
||||||
|
return String.format(locale = Locale.getDefault(), "%02d:%02d", min, sec)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user