refactor(shell): the pill's stopwatch half goes through the engine
M6 left this as M7's: the pill wrote to the stopwatch repository directly because there was no engine to ask. There is one now, so the pill's buttons mean exactly what the tab's and the shade's mean, service transitions included. `LivePillSelector` is re-pointed at `StopwatchReadings` — the same extraction M6 did for the timers — and its existing cases pass unmodified, which is the proof that the reading moved rather than changed. A stopwatch notification now opens the Stopwatch tab.
This commit is contained in:
1 parent
7b727e0d40
commit
5e73e81dc3
7 files changed
+139
-17
No files matched your search
@@ -3,7 +3,7 @@ package de.jeanlucmakiola.clockula.domain.live
|
||||
import de.jeanlucmakiola.clockula.domain.StopwatchRun
|
||||
import de.jeanlucmakiola.clockula.domain.StopwatchState
|
||||
import de.jeanlucmakiola.clockula.domain.Timer
|
||||
import de.jeanlucmakiola.clockula.domain.snapshotAt
|
||||
import de.jeanlucmakiola.clockula.domain.stopwatch.StopwatchReadings
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerMode
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerReadings
|
||||
import kotlin.time.Duration
|
||||
@@ -58,14 +58,20 @@ object LivePillSelector {
|
||||
)
|
||||
}
|
||||
|
||||
if (stopwatch.state == StopwatchState.IDLE) return null
|
||||
// The snapshot, not the stored row: a run whose anchor did not survive a
|
||||
// reboot reads as paused at what it banked rather than as running.
|
||||
val snapshot = stopwatch.snapshotAt(elapsedRealtime)
|
||||
if (!StopwatchReadings.isActive(stopwatch)) return null
|
||||
// The reading, not the stored row, and not a second copy of it: the pill,
|
||||
// the notification and the tab all ask "what does this read right now",
|
||||
// and `StopwatchReadings` is the one answer (M7 D6). A run whose anchor
|
||||
// did not survive a reboot reads as paused at what it banked.
|
||||
val reading = StopwatchReadings.of(stopwatch, elapsedRealtime)
|
||||
return LivePillState(
|
||||
subject = LivePillSubject.StopwatchSubject,
|
||||
mode = if (snapshot.isRunning) LivePillMode.RUNNING else LivePillMode.PAUSED,
|
||||
value = snapshot.elapsed,
|
||||
mode = if (reading.mode == StopwatchState.RUNNING) {
|
||||
LivePillMode.RUNNING
|
||||
} else {
|
||||
LivePillMode.PAUSED
|
||||
},
|
||||
value = reading.elapsed,
|
||||
otherActiveTimers = 0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,8 +29,9 @@ object ShellDefaults {
|
||||
|
||||
/**
|
||||
* One second: the pill formats to whole seconds, so anything faster is
|
||||
* recomposition nobody can see. The stopwatch tab's centisecond readout is
|
||||
* M7's, and will ask the ticker for its own period.
|
||||
* recomposition nobody can see. The stopwatch tab's centisecond readout
|
||||
* asks the ticker for its own period, `StopwatchDefaults.ReadoutTick`
|
||||
* (~60 Hz), and only while the stopwatch is running (M7 D9).
|
||||
*/
|
||||
val PillTick: Duration = 1.seconds
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package de.jeanlucmakiola.clockula.ui.shell
|
||||
|
||||
import de.jeanlucmakiola.clockula.stopwatch.StopwatchIntents
|
||||
import de.jeanlucmakiola.clockula.timer.TimerIntents
|
||||
|
||||
/** What a tab tap does to the back stack. Pure data; the composable replays it. */
|
||||
@@ -55,6 +56,7 @@ object ShellNavigation {
|
||||
*/
|
||||
fun tabForAction(action: String?): ClockulaDestination? = when (action) {
|
||||
TimerIntents.ACTION_SHOW_TIMERS -> ClockulaDestination.TIMERS
|
||||
StopwatchIntents.ACTION_SHOW_STOPWATCH -> ClockulaDestination.STOPWATCH
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,11 @@ package de.jeanlucmakiola.clockula.ui.shell
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import de.jeanlucmakiola.clockula.data.stopwatch.StopwatchRepository
|
||||
import de.jeanlucmakiola.clockula.data.timers.TimerRepository
|
||||
import de.jeanlucmakiola.clockula.domain.live.LivePillMode
|
||||
import de.jeanlucmakiola.clockula.domain.live.LivePillState
|
||||
import de.jeanlucmakiola.clockula.domain.live.LivePillSubject
|
||||
import de.jeanlucmakiola.clockula.stopwatch.StopwatchEngine
|
||||
import de.jeanlucmakiola.clockula.timer.TimerEngine
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -20,7 +20,9 @@ import javax.inject.Inject
|
||||
class ShellViewModel @Inject internal constructor(
|
||||
source: LivePillSource,
|
||||
private val timers: TimerRepository,
|
||||
private val stopwatch: StopwatchRepository,
|
||||
// M7 D8: the pill's stopwatch branch goes through the engine too, so the
|
||||
// foreground service moves with the state it shows.
|
||||
private val stopwatchEngine: StopwatchEngine,
|
||||
// M6 D25: the pill's timer branch goes through the engine, so pausing from
|
||||
// the pill moves the expiry slot and the service with it.
|
||||
private val timerEngine: TimerEngine,
|
||||
@@ -42,7 +44,7 @@ class ShellViewModel @Inject internal constructor(
|
||||
if (resume) timerEngine.start(subject.timerId) else timerEngine.pause(subject.timerId)
|
||||
|
||||
LivePillSubject.StopwatchSubject ->
|
||||
if (resume) stopwatch.start() else stopwatch.pause()
|
||||
if (resume) stopwatchEngine.start() else stopwatchEngine.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,7 +58,7 @@ class ShellViewModel @Inject internal constructor(
|
||||
// makes a timer its own preset (D13). Through the engine, so
|
||||
// stopping a *sounding* timer closes the ring session with it.
|
||||
is LivePillSubject.TimerSubject -> timerEngine.reset(subject.timerId)
|
||||
LivePillSubject.StopwatchSubject -> stopwatch.reset()
|
||||
LivePillSubject.StopwatchSubject -> stopwatchEngine.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user