Write a separate ViewModel for settings screen

This commit is contained in:
Nishant Mishra
2025-07-08 08:54:47 +05:30
parent 6381fa3da5
commit 40c6608d79
8 changed files with 176 additions and 151 deletions

View File

@@ -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)

View File

@@ -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()
}
} }

View 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
}

View File

@@ -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),

View File

@@ -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()
) )
} }

View File

@@ -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
)
}
}
}
}

View File

@@ -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)
} }

View 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)
}