fix: hold a timer's heads-up while an alarm is ringing

A timer whose ring was held back by a ringing alarm still popped a
heads-up over the alarm's screen, then alerted again once the alarm
ended. The heads-up now waits with the ring.
This commit is contained in:
makiolaj committed 2026-10-21 17:36:35 +02:00
1 parent 85c12811b0
commit 805da39074
3 files changed
+24 -3

No files matched your search

@@ -22,10 +22,16 @@ data class TimerNotificationState(
/** One notification for however many timers: the subject, and a count (D17). */
object TimerNotificationPolicy {
/** Null when nothing is active. [active] is [TimerReadings.active]'s output. */
/**
* Null when nothing is active. [active] is [TimerReadings.active]'s output.
* [ringSuppressed] is a ringing alarm holding the timer's ring back: its
* heads-up waits too, rather than popping over the alarm's screen and
* then, when the alarm ends, a second time.
*/
fun stateFor(
active: List<TimerReading>,
alreadyAlerted: Boolean,
ringSuppressed: Boolean = false,
): TimerNotificationState? {
val subject = TimerReadings.subject(active) ?: return null
return TimerNotificationState(
@@ -46,7 +52,7 @@ object TimerNotificationPolicy {
actions = actionsFor(subject.mode),
// An expiry heads-ups exactly once; a countdown update never does,
// and there is no second notification and no channel gymnastics.
alertOnce = subject.mode != TimerMode.EXPIRED || alreadyAlerted,
alertOnce = subject.mode != TimerMode.EXPIRED || alreadyAlerted || ringSuppressed,
)
}
@@ -289,7 +289,11 @@ class TimerEngine @Inject constructor(
val sounding = outcome.decision as? TimerRingDecision.Sound
return TimerServiceState(
notification = TimerNotificationPolicy.stateFor(active, alreadyAlerted),
notification = TimerNotificationPolicy.stateFor(
active,
alreadyAlerted,
ringSuppressed = outcome.decision is TimerRingDecision.Suppressed,
),
ring = outcome.decision,
// Floored, so a rounding error cannot spin the service's loop (D20).
expiresIn = TimerExpiry.nextDeadline(rows, now, wallNow)
@@ -144,6 +144,17 @@ class TimerNotificationPolicyTest {
assertThat(state.alertOnce).isTrue()
}
@Test
fun `an expiry held back by a ringing alarm does not heads-up yet`() {
val state = TimerNotificationPolicy.stateFor(
listOf(expired()),
alreadyAlerted = false,
ringSuppressed = true,
)!!
assertThat(state.alertOnce).isTrue()
}
/** §5.5 #11 */
@Test
fun `a countdown never alerts`() {