Persist the exploration, proposal, spec, design, tasks, and verify/archive reports produced during the multi-device EQ, alarm-countdown, and notification-visual-polish SDD cycles.
6.3 KiB
Alarm Pre-Notice Countdown Specification
Purpose
True per-minute live countdown for the 30-minute alarm pre-notice notification, mirroring the proven snooze-countdown repeating-alarm pattern. Replaces the current frozen, single-shot pre-notice ("30 min" forever) with a self-healing chain that updates every minute (29, 28, ... 1) until the real alarm fires.
Requirements
Requirement: First Pre-Notice Post
The system MUST post the initial pre-notice notification at triggerAtMillis - 30min (T-30min), unchanged from current behavior.
Scenario: First post at T-30min
- GIVEN an alarm scheduled to fire at time T
- WHEN the system clock reaches T-30min
- THEN an exact alarm fires
ACTION_PRE_NOTICE - AND a notification showing "30 min" remaining is posted using
notificationIdForAlarm(id)
Requirement: Per-Minute Tick Re-Arm
After posting a pre-notice notification, the system MUST re-arm itself to fire again at the next minute boundary, reusing ACTION_PRE_NOTICE for both the first post and every subsequent tick (no separate action constant).
Scenario: Tick re-arms next minute
- GIVEN
ACTION_PRE_NOTICEhas just fired and posted a notification with remaining minutesRwhereR > 1 - WHEN the post completes
- THEN
AlarmScheduler.armNextPreNoticeCountdownTick(id, R)arms a new exact alarm attriggerAtMillis - (R - 1) * 60_000L - AND the new alarm uses requestCode slot 9
Scenario: Tick updates notification content
- GIVEN the tick chain is active for alarm
id - WHEN a re-armed
ACTION_PRE_NOTICEfires at a later minute boundary - THEN the notification at
notificationIdForAlarm(id)is updated (not duplicated) to show the new remaining-minutes value
Requirement: Self-Healing Minute Computation
Each tick MUST compute remaining minutes from the current wall-clock time relative to triggerAtMillis, using ceilMinutes(), rather than decrementing a stored counter.
Scenario: Normal tick sequence
- GIVEN consecutive ticks fire close to their scheduled minute boundaries
- WHEN each tick computes remaining minutes via
ceilMinutes(triggerAtMillis - now) - THEN the displayed sequence is 29, 28, 27, ... 1 with no manual decrement state
Scenario: Missed tick self-heals by jumping, not crashing
- GIVEN the OS delays or coalesces a scheduled tick (e.g. Doze quota) so the receiver fires late
- WHEN the delayed tick recomputes remaining minutes from wall clock
- THEN the displayed countdown jumps forward to the correct current value (e.g. skips from 15 to 12) instead of crashing, looping, or showing a stale/negative value
Requirement: Self-Stop at Final Minute
The tick chain MUST stop re-arming once computed remaining minutes is <= 1; the final minute is left to the real fire alarm, not a tick.
Scenario: Chain stops before final minute
- GIVEN a tick fires and computes remaining minutes
R <= 1 - WHEN the tick finishes posting/updating the notification
- THEN no further
armNextPreNoticeCountdownTickcall is made - AND the alarm's existing
setAlarmClockfire alarm remains the sole next trigger
Requirement: Consistent Rounding via ceilMinutes
The system MUST use ceilMinutes() for pre-notice remaining-minutes computation, replacing the prior floor-based computeRemainingMinutes(), for consistency with the snooze-countdown chain.
Scenario: Rounding matches snooze countdown
- GIVEN identical time-remaining deltas for a pre-notice tick and a snooze-countdown tick
- WHEN both compute their displayed minute value
- THEN both use
ceilMinutes()and produce the same rounding result for equivalent inputs
Requirement: Tick Chain Cancellation
The system MUST tear down the pending pre-notice tick alarm at all of the following sites: cancelAlarm(), the scheduleSpec no-next-trigger branch, the snooze-transition branch, ACTION_SKIP_NEXT, and ACTION_POSTPONE_NEXT. No site may leave an orphaned repeating alarm.
Scenario: Full alarm cancellation tears down tick chain
- GIVEN a pre-notice tick chain is active for alarm
id - WHEN
cancelAlarm(id)is called - THEN the pending pre-notice tick
PendingIntent(slot 9) is cancelled - AND no further
ACTION_PRE_NOTICEticks fire forid
Scenario: No-next-trigger reschedule cancels tick chain
- GIVEN a pre-notice tick chain is active for alarm
id - WHEN
scheduleSpecrecomputes and finds no next trigger time forid - THEN the pending pre-notice tick is cancelled in the same branch that already cancels the single-shot pre-notice and snooze-countdown pendings
Scenario: Snooze transition cancels pre-notice tick chain
- GIVEN a pre-notice tick chain is active for alarm
id - WHEN the user snoozes the alarm, transitioning it into snooze-countdown mode
- THEN the pre-notice tick chain is cancelled
- AND no pre-notice notification or alarm remains pending while snooze-countdown is active
Scenario: Skip-next action cancels tick chain
- GIVEN a pre-notice tick chain is active for alarm
id - WHEN the user taps "Skip" on the pre-notice notification, triggering
ACTION_SKIP_NEXT - THEN the pending pre-notice tick alarm for
idis cancelled - AND no further pre-notice ticks fire for the skipped occurrence
Scenario: Postpone-next action cancels tick chain
- GIVEN a pre-notice tick chain is active for alarm
id - WHEN the user taps "Postpone" on the pre-notice notification, triggering
ACTION_POSTPONE_NEXT - THEN the pending pre-notice tick alarm for
idis cancelled - AND no further pre-notice ticks fire for the postponed occurrence
Requirement: Notification ID Reuse and Mutual Exclusivity with Snooze
The pre-notice tick chain MUST reuse the same notification ID (notificationIdForAlarm(id)) as snooze-countdown, and the two chains MUST remain mutually exclusive in time for the same alarm id.
Scenario: Pre-notice and snooze-countdown never run concurrently
- GIVEN alarm
idhas an active pre-notice tick chain - WHEN the alarm is not snoozed
- THEN no snooze-countdown chain is scheduled for
idconcurrently, preserving the existingscheduleSpecbranch invariant onsnoozeUntilMillis
Scenario: Notification updates in place, no duplicate
- GIVEN a pre-notice tick posts an update for alarm
id - WHEN the notification ID matches a previously posted pre-notice or snooze-countdown notification for the same
id - THEN the system tray shows a single updated notification, not a duplicate entry