Animate progress

This commit is contained in:
Nishant Mishra
2025-06-29 19:42:57 +05:30
parent 41995776f9
commit f59bb5600c
4 changed files with 41 additions and 34 deletions

View File

@@ -4,6 +4,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.nsh07.pomodoro.ui.timerScreen.TimerScreen import org.nsh07.pomodoro.ui.timerScreen.TimerScreen
@@ -16,6 +17,15 @@ fun AppScreen(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val remainingTime by viewModel.time.collectAsStateWithLifecycle()
TimerScreen(uiState = uiState, resetTimer = viewModel::resetTimer, toggleTimer = viewModel::toggleTimer, modifier = modifier) val progress by rememberUpdatedState((uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime)
TimerScreen(
uiState = uiState,
progress = { progress },
resetTimer = viewModel::resetTimer,
toggleTimer = viewModel::toggleTimer,
modifier = modifier
)
} }

View File

@@ -49,6 +49,7 @@ import org.nsh07.pomodoro.ui.viewModel.UiState
@Composable @Composable
fun TimerScreen( fun TimerScreen(
uiState: UiState, uiState: UiState,
progress: () -> Float,
resetTimer: () -> Unit, resetTimer: () -> Unit,
toggleTimer: () -> Unit, toggleTimer: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
@@ -144,7 +145,7 @@ fun TimerScreen(
Column(horizontalAlignment = Alignment.CenterHorizontally) { Column(horizontalAlignment = Alignment.CenterHorizontally) {
Box(contentAlignment = Alignment.Center) { Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator( CircularProgressIndicator(
progress = { (uiState.totalTime.toFloat() - uiState.remainingTime) / uiState.totalTime }, progress = progress,
modifier = Modifier.size(350.dp), modifier = Modifier.size(350.dp),
color = color, color = color,
trackColor = colorContainer, trackColor = colorContainer,
@@ -238,5 +239,5 @@ fun TimerScreenPreview() {
val uiState = UiState( val uiState = UiState(
timeStr = "08:34", nextTimeStr = "5:00" timeStr = "08:34", nextTimeStr = "5:00"
) )
TimerScreen(uiState, {}, {}) TimerScreen(uiState, { 0.3f }, {}, {})
} }

View File

@@ -4,7 +4,6 @@ data class UiState(
val timerMode: TimerMode = TimerMode.FOCUS, val timerMode: TimerMode = TimerMode.FOCUS,
val timeStr: String = "25:00", val timeStr: String = "25:00",
val totalTime: Int = 25 * 60, val totalTime: Int = 25 * 60,
val remainingTime: Int = 25 * 60,
val timerRunning: Boolean = false, val timerRunning: Boolean = false,
val nextTimerMode: TimerMode = TimerMode.SHORT_BREAK, val nextTimerMode: TimerMode = TimerMode.SHORT_BREAK,
val nextTimeStr: String = "5:00" val nextTimeStr: String = "5:00"

View File

@@ -12,36 +12,36 @@ import kotlinx.coroutines.launch
import java.util.Locale import java.util.Locale
class UiViewModel : ViewModel() { class UiViewModel : ViewModel() {
val focusTime = 10 val focusTime = 10000
val shortBreakTime = 5 val shortBreakTime = 5000
val longBreakTime = 20 val longBreakTime = 20000
private val _uiState = MutableStateFlow( private val _uiState = MutableStateFlow(
UiState( UiState(
totalTime = focusTime, totalTime = focusTime,
remainingTime = focusTime, timeStr = millisecondsToStr(focusTime),
timeStr = secondsToStr(focusTime), nextTimeStr = millisecondsToStr(shortBreakTime)
nextTimeStr = secondsToStr(shortBreakTime)
) )
) )
val uiState: StateFlow<UiState> = _uiState.asStateFlow() val uiState: StateFlow<UiState> = _uiState.asStateFlow()
var timerJob: Job? = null var timerJob: Job? = null
var time = focusTime private val _time = MutableStateFlow(focusTime)
val time: StateFlow<Int> = _time.asStateFlow()
var cycles = 0 var cycles = 0
fun resetTimer() { fun resetTimer() {
time = focusTime _time.update { focusTime }
cycles = 0 cycles = 0
_uiState.update { currentState -> _uiState.update { currentState ->
currentState.copy( currentState.copy(
timerMode = TimerMode.FOCUS, timerMode = TimerMode.FOCUS,
timeStr = secondsToStr(time), timeStr = millisecondsToStr(time.value),
totalTime = time, totalTime = time.value,
remainingTime = time,
nextTimerMode = TimerMode.SHORT_BREAK, nextTimerMode = TimerMode.SHORT_BREAK,
nextTimeStr = secondsToStr(shortBreakTime) nextTimeStr = millisecondsToStr(shortBreakTime)
) )
} }
} }
@@ -57,58 +57,55 @@ class UiViewModel : ViewModel() {
timerJob = viewModelScope.launch { timerJob = viewModelScope.launch {
while (true) { while (true) {
if (!uiState.value.timerRunning) break if (!uiState.value.timerRunning) break
time-- _time.update { it - 10 }
if (time < 0) { if (time.value < 0) {
cycles++ cycles++
if (cycles % 2 == 0) { if (cycles % 2 == 0) {
time = focusTime _time.update { focusTime }
_uiState.update { currentState -> _uiState.update { currentState ->
currentState.copy( currentState.copy(
timerMode = TimerMode.FOCUS, timerMode = TimerMode.FOCUS,
timeStr = secondsToStr(time), timeStr = millisecondsToStr(time.value),
totalTime = time, totalTime = time.value,
remainingTime = time,
nextTimerMode = if (cycles % 6 == 0) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK, nextTimerMode = if (cycles % 6 == 0) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
nextTimeStr = if (cycles % 6 == 0) secondsToStr(longBreakTime) else secondsToStr( nextTimeStr = if (cycles % 6 == 0) millisecondsToStr(longBreakTime) else millisecondsToStr(
shortBreakTime shortBreakTime
) )
) )
} }
} else { } else {
val long = cycles % 7 == 0 val long = cycles % 7 == 0
time = if (long) longBreakTime else shortBreakTime _time.update { if (long) longBreakTime else shortBreakTime }
_uiState.update { currentState -> _uiState.update { currentState ->
currentState.copy( currentState.copy(
timerMode = if (long) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK, timerMode = if (long) TimerMode.LONG_BREAK else TimerMode.SHORT_BREAK,
timeStr = secondsToStr(time), timeStr = millisecondsToStr(time.value),
totalTime = time, totalTime = time.value,
remainingTime = time,
nextTimerMode = TimerMode.FOCUS, nextTimerMode = TimerMode.FOCUS,
nextTimeStr = secondsToStr(focusTime) nextTimeStr = millisecondsToStr(focusTime)
) )
} }
} }
} else { } else {
_uiState.update { currentState -> _uiState.update { currentState ->
currentState.copy( currentState.copy(
timeStr = secondsToStr(time), timeStr = millisecondsToStr(time.value)
remainingTime = time
) )
} }
} }
delay(1000) delay(10)
} }
} }
} }
} }
private fun secondsToStr(t: Int): String { private fun millisecondsToStr(t: Int): String {
val min = t / 60 val min = (t / 1000) / 60
val sec = t % 60 val sec = (t / 1000) % 60
return String.format(locale = Locale.getDefault(), "%02d:%02d", min, sec) return String.format(locale = Locale.getDefault(), "%02d:%02d", min, sec)
} }
} }