feat(timer): the engine, its expiry slot, the service and the receivers
One AlarmManager slot for every timer, registered on ELAPSED_REALTIME_WAKEUP so the clock the domain anchors on is the clock the platform wakes on. Its fire carries no id and is an idempotent sweep, backed up by a second trigger inside the service, because neither has to be reliable on its own. The foreground service posts per state change rather than per second — the platform chronometer draws the countdown — and its actions are broadcasts, so pause and reset still work with the process dead. Every verb is guarded against a stale id. Expiry reuses the alarm's audio path: the same player, vibrator, source policy and fallback-to-vibration chain, with a zero ramp and its own channel. No full-screen intent, no challenge, no snooze — a timer is not an alarm. Two timers expiring together share one ring; a timer expiring after a previous one's auto-silence window lapsed gets a sound of its own, which is the whole point of having a timer.
This commit is contained in:
@@ -104,6 +104,28 @@
|
||||
android:name=".alarm.receiver.AlarmActionReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- The timers: one foreground service covering both phases, the
|
||||
countdown and the ring. systemExempted for the same reason the
|
||||
alarm's ringing service is, and M6 adds **no** permission for it —
|
||||
the foreground-service, wake-lock, vibrate and notification set
|
||||
above already covers it (slice plan D6, D18). -->
|
||||
<service
|
||||
android:name=".timer.service.TimerService"
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="systemExempted" />
|
||||
|
||||
<!-- The single expiry slot arriving. It carries no timer id: the fire is
|
||||
an idempotent sweep (D5). Unexported, like the alarm's. -->
|
||||
<receiver
|
||||
android:name=".timer.receiver.TimerExpiryReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- The timer notification's buttons. Broadcasts rather than service
|
||||
starts, so they work with the process dead (D16). -->
|
||||
<receiver
|
||||
android:name=".timer.receiver.TimerActionReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- Protected system broadcasts are delivered to unexported receivers —
|
||||
this is how androidx.work declares its own RescheduleReceiver.
|
||||
LOCKED_BOOT_COMPLETED is deliberately absent: the database and the
|
||||
|
||||
Reference in New Issue
Block a user