From 7f9219e929fb2ed167e8db3528493dec2b45b98f Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sat, 12 Sep 2026 16:48:36 +0200 Subject: [PATCH] feat(timer): the engine, its expiry slot, the service and the receivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- app/src/main/AndroidManifest.xml | 22 + .../de/jeanlucmakiola/clockula/ClockulaApp.kt | 16 +- .../alarm/receiver/SystemEventReceiver.kt | 32 +- .../clockula/timer/AlarmRingStatus.kt | 10 + .../clockula/timer/AlarmStateRingStatus.kt | 30 + .../clockula/timer/TimerEngine.kt | 254 ++++++ .../clockula/timer/TimerIntents.kt | 43 + .../clockula/timer/TimerScheduler.kt | 11 + .../clockula/timer/TimerServiceHandle.kt | 6 + .../timer/android/AndroidTimerScheduler.kt | 57 ++ .../timer/android/ServiceTimerHandle.kt | 29 + .../clockula/timer/di/TimerModule.kt | 35 + .../timer/receiver/TimerActionReceiver.kt | 52 ++ .../timer/receiver/TimerExpiryReceiver.kt | 40 + .../timer/service/TimerNotifications.kt | 192 +++++ .../clockula/timer/service/TimerService.kt | 323 ++++++++ .../clockula/timer/TimerEngineTest.kt | 739 ++++++++++++++++++ .../clockula/timer/TimerIntentsTest.kt | 84 ++ 18 files changed, 1971 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/AlarmRingStatus.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/AlarmStateRingStatus.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerEngine.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerIntents.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerScheduler.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/TimerServiceHandle.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/android/AndroidTimerScheduler.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/android/ServiceTimerHandle.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/di/TimerModule.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/receiver/TimerActionReceiver.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/receiver/TimerExpiryReceiver.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/service/TimerNotifications.kt create mode 100644 app/src/main/java/de/jeanlucmakiola/clockula/timer/service/TimerService.kt create mode 100644 app/src/test/java/de/jeanlucmakiola/clockula/timer/TimerEngineTest.kt create mode 100644 app/src/test/java/de/jeanlucmakiola/clockula/timer/TimerIntentsTest.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 823a9e1..e893c84 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -104,6 +104,28 @@ android:name=".alarm.receiver.AlarmActionReceiver" android:exported="false" /> + + + + + + + + +