feat(data): ui preferences, the real ticker and its binding
The ticker is injected rather than read from the composition so the shell's per-second text can be driven by a test scheduler instead of a wall clock. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
This commit is contained in:
@@ -5,11 +5,13 @@ import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import de.jeanlucmakiola.clockula.data.time.AndroidBootIdProvider
|
||||
import de.jeanlucmakiola.clockula.data.time.RealTicker
|
||||
import de.jeanlucmakiola.clockula.data.time.SystemElapsedRealtimeClock
|
||||
import de.jeanlucmakiola.clockula.data.time.SystemWallClock
|
||||
import de.jeanlucmakiola.clockula.data.time.SystemZoneProvider
|
||||
import de.jeanlucmakiola.clockula.domain.time.BootIdProvider
|
||||
import de.jeanlucmakiola.clockula.domain.time.ElapsedRealtimeClock
|
||||
import de.jeanlucmakiola.clockula.domain.time.Ticker
|
||||
import de.jeanlucmakiola.clockula.domain.time.WallClock
|
||||
import de.jeanlucmakiola.clockula.domain.time.ZoneProvider
|
||||
|
||||
@@ -34,4 +36,7 @@ abstract class TimeModule {
|
||||
|
||||
@Binds
|
||||
abstract fun bindBootIdProvider(impl: AndroidBootIdProvider): BootIdProvider
|
||||
|
||||
@Binds
|
||||
abstract fun bindTicker(impl: RealTicker): Ticker
|
||||
}
|
||||
|
||||
@@ -92,4 +92,16 @@ class SettingsPrefs @Inject constructor(private val store: PrefStore) {
|
||||
suspend fun setTimerDuration(duration: Duration) = store.set(ClockPrefs.timerDurationMillis, duration)
|
||||
|
||||
suspend fun setHomeZoneId(zoneId: String?) = store.set(ClockPrefs.homeZoneId, zoneId)
|
||||
|
||||
// --- first-launch bookkeeping (M4) ---
|
||||
|
||||
/**
|
||||
* Whether the POST_NOTIFICATIONS request has already been shown once.
|
||||
* Distinct-until-changed through [PrefStore.flow], so writing an unrelated
|
||||
* preference does not re-emit it.
|
||||
*/
|
||||
val notificationPermissionAsked: Flow<Boolean> = store.flow(UiPrefs.notificationPermissionAsked)
|
||||
|
||||
suspend fun markNotificationPermissionAsked() =
|
||||
store.set(UiPrefs.notificationPermissionAsked, true)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package de.jeanlucmakiola.clockula.data.prefs
|
||||
|
||||
import de.jeanlucmakiola.floret.prefs.Pref
|
||||
import de.jeanlucmakiola.floret.prefs.booleanPref
|
||||
|
||||
/** The shell's own on-disk keys — first-launch bookkeeping, not a user setting. */
|
||||
object UiPrefs {
|
||||
/** Whether the POST_NOTIFICATIONS request has been shown once. */
|
||||
val notificationPermissionAsked: Pref<Boolean> =
|
||||
booleanPref("notification_permission_asked", false)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package de.jeanlucmakiola.clockula.data.time
|
||||
|
||||
import de.jeanlucmakiola.clockula.domain.time.Ticker
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlin.time.Duration
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/** The [Ticker] seam over `delay`: emit, then wait [ticks]' period, forever. */
|
||||
@Singleton
|
||||
class RealTicker @Inject constructor() : Ticker {
|
||||
|
||||
/**
|
||||
* Cold, and per-collector: two readouts on different cadences get two
|
||||
* independent streams, and a collector that walks away takes its own
|
||||
* `delay` with it. The first emission is immediate, so a subscriber is
|
||||
* never blank for a period.
|
||||
*/
|
||||
override fun ticks(period: Duration): Flow<Unit> = flow {
|
||||
while (true) {
|
||||
emit(Unit)
|
||||
delay(period)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user