diff --git a/app/src/main/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotification.kt b/app/src/main/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotification.kt index c61cf67..8cea429 100644 --- a/app/src/main/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotification.kt +++ b/app/src/main/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotification.kt @@ -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, 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, ) } diff --git a/app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerEngine.kt b/app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerEngine.kt index 426dfec..04b27cc 100644 --- a/app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerEngine.kt +++ b/app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerEngine.kt @@ -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) diff --git a/app/src/test/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotificationPolicyTest.kt b/app/src/test/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotificationPolicyTest.kt index 920c72d..a0b6c96 100644 --- a/app/src/test/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotificationPolicyTest.kt +++ b/app/src/test/java/de/jeanlucmakiola/clockula/domain/timer/TimerNotificationPolicyTest.kt @@ -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`() {