ARCHITECTURE gains §12 for the shell and loses the two rows M4 paid off; the permissions row now says what M4 actually asks for and leaves the rest to M10's self-check. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
5.7 KiB
5.7 KiB
Changelog
All notable changes to Clockula are documented here.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
Tag sections feed the release notes — see docs/RELEASING.md.
[Unreleased]
Added
- Project skeleton: Gradle build, version catalog, Hilt, Compose and the Material 3
Expressive theme over floret-kit's
identitymodule, seeded#6B7A5C— the third 120° hue rotation of Calendula's slate, completing the family's palette. - floret-kit as a git submodule wired in as a Gradle composite build.
- Crash capture and the report surface via floret-kit's
core-crash; per-app language support viacore-locale. - Launcher and notification icons: a line-art clock face carrying the family's Calendula bloom badge.
- Full release pipeline, Codeberg-canonical from the first commit — CI, translations, release (F-Droid repo + Codeberg release + Google Play) and Renovate, with the F-Droid reproducibility rules intact.
- The locked specification:
docs/PLAN.mdanddocs/ROADMAP.md. - Preferences over floret-kit's new
core-prefs:SettingsPrefsholds the appearance slice (theme mode, dynamic colour) in a DataStore of Clockula's own, under the family's key names. The theme now follows the stored preference — light/dark override and dynamic colour persist across launches instead of always following the system — and an unrelated preference write no longer re-emits it. - floret-kit's
core-disupplies the@IoDispatcherthe preference store runs on, so Clockula no longer declares its own dispatcher qualifier. - Clockula's own storage, headless: a Room database with
alarms,timers,world_clocksandstopwatch_laps, its schema exported and committed at every version so each migration is reviewable and testable. - Plain-Kotlin alarms, timers, world clocks and stopwatch runs behind four repository interfaces exposing Flows — nothing above the data layer knows Room exists, and a test fails the build if anyone reaches through. A corrupt row degrades on read (a clamped time, an unknown ringtone, a rotted zone id) rather than crashing the list it appears in.
- Alarm and timer settings are inherited, not copied: a per-alarm ringtone, snooze, vibrate, volume ramp or dismiss challenge is an override, so changing an app default later reaches every alarm the user never customised.
- The clock defaults and the stopwatch's running state now live in DataStore alongside the appearance preferences, clamped on read as well as on write, so a hand-edited or restored file cannot produce a setting the user could never have chosen.
- The wall-clock / elapsed-realtime split, made explicit: alarm records follow the system clock, while running timers and the stopwatch are anchored to time since boot. Changing the device's time — forwards or backwards — cannot warp either, and a timer that survives a reboot falls back to its wall-clock estimate and says so instead of vanishing.
- Alarms that ring. A repeat schedule that is re-resolved against the device's zone every time anything moves, so the clock going forwards, backwards or through a daylight-saving transition cannot lose one: a 02:30 alarm on a spring-forward night rings at 03:30 rather than vanishing, and on a fall-back night rings once rather than twice.
- Skip-next-occurrence that skips exactly one occurrence, snooze with a per-alarm interval and limit, and a snooze that is still kept after a reboot.
- An alarm that keeps ringing through a reboot or a process kill — the ring is rebuilt from storage, not from memory — and one that still rings when the full-screen-intent or notification permission is denied. The chain of fallbacks ends in vibration, never in silence.
- The post-reboot repair: a running timer no longer counts down from an anchor the reboot killed, and the stopwatch no longer invents a segment it never ran.
- The app shell: four tabs — alarms, timers, stopwatch, world clock — under a navigation bar that becomes a rail on a tablet, chosen by the window's size rather than by hand. Back from a tab goes home to Alarms; back from Alarms leaves the app, and the predictive-back gesture previews whichever of the two is actually about to happen.
- The live pill: whatever is counting is visible from every tab, and pausable and stoppable without going to find the row that owns it. It stays up while paused — a control that vanishes under the thumb that pressed it is no control — shows how many other timers are running behind it, and flips a timer that reaches zero to "finished" the moment it does, rather than counting into negative time. Stop resets; it never deletes a timer the user configured.
- The real ring screen, over the lock screen with the display kept on for exactly as long as the window lives: the time, the label, snooze, and an optional dismiss challenge — a two-term sum, or a two-second hold — that can never strand anyone. Three wrong answers or sixty seconds of ringing, either one, opens a way out; snooze is never gated; solving the challenge dismisses on the spot instead of asking twice; and the hold completes from an accessibility click, so a screen reader is not locked out of its own alarm. Back cannot silence an alarm.
- The first Compose motion: fade-through between peer tabs, the kit's springs for the pill coming and going and for the challenge block opening, all of which already stand down when the system's "remove animations" setting is on.
- Notification permission is asked for once, on first launch, and the fact that it was asked is recorded rather than guessed at.