Files
FreeTLab bccc5c48b8
Build & Deploy PluriWave / Análisis de código (push) Successful in 38s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m30s
docs(openspec): add SDD artifact trail for recent alarm and EQ changes
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.
2026-07-04 12:42:11 +02:00

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_NOTICE has just fired and posted a notification with remaining minutes R where R > 1
  • WHEN the post completes
  • THEN AlarmScheduler.armNextPreNoticeCountdownTick(id, R) arms a new exact alarm at triggerAtMillis - (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_NOTICE fires 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 armNextPreNoticeCountdownTick call is made
  • AND the alarm's existing setAlarmClock fire 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_NOTICE ticks fire for id

Scenario: No-next-trigger reschedule cancels tick chain

  • GIVEN a pre-notice tick chain is active for alarm id
  • WHEN scheduleSpec recomputes and finds no next trigger time for id
  • 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 id is 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 id is 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 id has an active pre-notice tick chain
  • WHEN the alarm is not snoozed
  • THEN no snooze-countdown chain is scheduled for id concurrently, preserving the existing scheduleSpec branch invariant on snoozeUntilMillis

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