fix(timers): apply a timer setting changed mid-ring
The service ignored a decision for the timer already sounding, so switching timer vibration off in Settings while a timer rang kept it buzzing until the ring ended. A decision whose settings changed now restarts the ring, its ramp picking up where it was. The package-manager check for a fresh install also moved out of the settle's DataStore transaction. Refs #22
This commit is contained in:
1 parent
820317f9d9
commit
058fc27749
2 files changed
+14
-3
No files matched your search
@@ -141,6 +141,9 @@ class SettingsPrefs @Inject constructor(private val store: PrefStore) {
|
||||
*/
|
||||
suspend fun settleTimerVibrate(freshInstall: () -> Boolean) {
|
||||
if (store.get(ClockPrefs.timerVibrate) != null) return
|
||||
// Asked outside the transaction: a binder call has no business holding
|
||||
// the store's write lock.
|
||||
val freshPackage = freshInstall()
|
||||
// One transaction, so nothing written meanwhile can make the snapshot
|
||||
// it decides on stale. Raw keys for the reads: the kit reads a Pref
|
||||
// only from a store, not from a transaction's preferences.
|
||||
@@ -149,7 +152,7 @@ class SettingsPrefs @Inject constructor(private val store: PrefStore) {
|
||||
// Settled meanwhile; a garbled value falls through and is replaced.
|
||||
if (stored?.toBooleanStrictOrNull() != null) return@edit
|
||||
val neverRan = stringPreferencesKey(SystemPrefs.lastBootId.name) !in prefs
|
||||
prefs[ClockPrefs.timerVibrate] = if (neverRan && freshInstall()) {
|
||||
prefs[ClockPrefs.timerVibrate] = if (neverRan && freshPackage) {
|
||||
false
|
||||
} else {
|
||||
prefs[booleanPreferencesKey(ClockPrefs.vibrate.name)] ?: ClockPrefs.vibrate.default
|
||||
|
||||
@@ -18,6 +18,7 @@ import de.jeanlucmakiola.clockula.domain.timer.TimerNotificationState
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerMode
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerRing
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerRingDecision
|
||||
import de.jeanlucmakiola.clockula.domain.timer.TimerSettings
|
||||
import de.jeanlucmakiola.clockula.ring.RingAudioPlayer
|
||||
import de.jeanlucmakiola.clockula.ring.RingVibrator
|
||||
import de.jeanlucmakiola.clockula.timer.AlarmRingStatus
|
||||
@@ -93,6 +94,9 @@ class TimerService : Service() {
|
||||
/** Which timer the audio is currently sounding for, so an unchanged decision does nothing. */
|
||||
private var sounding: Long? = null
|
||||
|
||||
/** What [sounding] rings with, so a settings change mid-ring is applied rather than ignored. */
|
||||
private var soundingSettings: TimerSettings? = null
|
||||
|
||||
/** Owned here, scoped to the ring session it was spent in: the rule itself is pure (D17). */
|
||||
private var alreadyAlerted: Boolean = false
|
||||
|
||||
@@ -228,10 +232,13 @@ class TimerService : Service() {
|
||||
when (decision) {
|
||||
is TimerRingDecision.Sound -> {
|
||||
// An unchanged decision does nothing: restarting the player on
|
||||
// every emission would stutter the ring.
|
||||
if (sounding == decision.subjectId) return
|
||||
// every emission would stutter the ring. A changed setting (the
|
||||
// user switched timer vibration off mid-ring, say) restarts it,
|
||||
// the ramp picking up where it was.
|
||||
if (sounding == decision.subjectId && soundingSettings == decision.settings) return
|
||||
releaseRing()
|
||||
sounding = decision.subjectId
|
||||
soundingSettings = decision.settings
|
||||
acquireWakeLock()
|
||||
// Vibration first, and the sound without waiting for it: a sound
|
||||
// another app plays can take seconds to open, and this runs
|
||||
@@ -334,6 +341,7 @@ class TimerService : Service() {
|
||||
/** The whole ring released, not just the audio: an orphaned looping player cannot be stopped. */
|
||||
private fun releaseRing() {
|
||||
sounding = null
|
||||
soundingSettings = null
|
||||
audio.stop()
|
||||
vibrator.stop()
|
||||
wakeLock?.let { if (it.isHeld) runCatching { it.release() } }
|
||||
|
||||
Reference in new issue
Block a user