Files
clockula/CHANGELOG.md
T
makiolaj 9ab3927efe docs: the system boundary, and what it refuses
`ARCHITECTURE.md` gains §17: the door, the split between the Android-free
validator and the reader that guards the unparcel, the range rule (clamp what
is happening now, drop what would define a future ring), the candidate table
for "which alarm did you mean", and the three deliberate narrowings — including
why a link we cannot read dismisses nothing rather than everything.

§4 records schema v3 and its migration; §13 records the reading of `PLAN.md` §6
this milestone builds, since a malformed intent lands the user in an editor
over a row created from the valid extras, there being no "new alarm" sentinel.
2026-09-23 11:02:20 +02:00

192 lines
13 KiB
Markdown

# Changelog
All notable changes to Clockula are documented here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Tag sections feed the release notes — see [`docs/RELEASING.md`](docs/RELEASING.md).
## [Unreleased]
### Added
- Project skeleton: Gradle build, version catalog, Hilt, Compose and the Material 3
Expressive theme over floret-kit's `identity` module, 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 via `core-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.md` and `docs/ROADMAP.md`.
- Preferences over floret-kit's new `core-prefs`: `SettingsPrefs` holds 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-di` supplies the `@IoDispatcher` the 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_clocks` and `stopwatch_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.
- The alarms tab, for real: a list of your alarms that says when each one will
ring — "in 9h 12m" — and marks the next one to go off, with the repeat days
and a switch on every row. A switched-off alarm stays where it is, faded
rather than shuffled to the bottom, and the countdown keeps up on its own
without anything being saved.
- Create, edit, delete, enable and disable an alarm, applied as you make them
rather than behind a Save button: tapping + creates an alarm at the next whole
hour and opens it, every control writes at once, and back is just back.
- The Material time picker, with a keypad to type a time and the dial behind one
toggle, following the device's own 12- or 24-hour setting.
- A repeat-day selector that starts on your locale's first day of the week, not
on a hardcoded Monday, and a "skip next alarm" switch for a repeating one —
so you can sleep in on Thursday without switching the alarm off and
forgetting to switch it back.
- A per-alarm sound, vibration, gradual volume, snooze length, snooze limit and
dismiss challenge — each of which can say "follow the app default" rather than
silently copying today's default and drifting away from it later. Every row
shows what it currently resolves to.
- A ringtone picker with the device's own alarm sounds, a file you choose
yourself, and a Silent option that still vibrates — because an alarm that does
literally nothing is not an alarm. You can play a sound before taking it.
- A sound Clockula cannot read right now says so under the row, and your choice
is kept rather than quietly replaced with the default: an unmounted card comes
back. If a file you picked cannot be held onto permanently, that is disclosed
too instead of being hidden.
- The timers tab, for real: several timers at once, each with its own label,
length and sound, and a keypad that sets one in a couple of taps — with saved
presets you can add to and remove from as you go. A timer starts the moment
you press Start, so an accidental tap leaves nothing behind to delete.
- Pause, resume, reset and "+1 min" on every timer, from the row, from the live
pill and from the notification — and "+1 min" on a timer that has just rung
starts it again with a whole minute, rather than handing back a paused timer
you have to press Start on.
- A notification that counts the timer down without waking the device every
second: the system draws the ticking, so Clockula posts once when something
actually changes. It carries the same two buttons the row does, and they work
with the app closed.
- A timer keeps counting across a reboot and rings when you come back — the
countdown is anchored on the device's uptime, so changing the system clock
cannot move it in either direction. Change the clock while a timer runs and
the notification's countdown follows the change instead of going wrong.
- Two timers finishing together share one sound and one ten-minute window, and
each is stopped on its own, so you can still tell which one you have dealt
with. A timer left ringing goes quiet after ten minutes but stays finished, so
you can come back and add a minute to it — and the next timer to finish rings
for itself rather than inheriting that silence.
- A timer that finishes while an alarm is ringing waits its turn instead of
talking over it: it is still marked finished, its notification says so, and it
starts sounding the moment the alarm is done.
- Progress on each timer card is drawn by the Expressive wavy indicator, and the
wave itself is the state — it flattens when you pause and when the timer is
done.
- The stopwatch tab, for real: start, pause and reset, with a readout down to
hundredths of a second that stays legible because the fraction is drawn
smaller than the seconds rather than shouting alongside them.
- Laps, each with its own split and its running total, newest at the top, and
the fastest and slowest of them marked — in colour and in words, so a screen
reader hears the distinction too. The lap you are in the middle of has its own
row, counting up.
- An ongoing notification with Lap and Pause in the shade, which costs no
battery to keep accurate because the system draws the ticking; Clockula posts
once when something actually changes.
- A stopwatch that keeps counting while the app is closed, and that comes back
after a restart paused — with every lap intact — at the time it had banked,
rather than inventing the time it lost. Changing the system clock, in either
direction, cannot move it at all.
- The app's big numbers now sit in fixed columns, so a readout no longer jitters
as its digits change — on every screen that shows one, not only the stopwatch.
- A world clock tab, with an analog face for your own time zone whose dial
changes shape with the hour — so a glance says whether it is the middle of the
night there, without a line of text saying so.
- Cities added from the device's own time-zone database, searched by name in
your language — accents and all, so typing "sao" finds São Paulo — with the
country and the offset from GMT shown before you pick.
- Each city shows its time, how far ahead or behind your own it is, and whether
it is a different day; all of it re-read from the real time-zone rules, so a
daylight-saving change in either place is right on the day it happens.
- Cities reordered by dragging them, or by a "move up" / "move down" action a
screen reader can reach — a list you can only rearrange by dragging is a list
some people cannot rearrange at all.
- A home time zone you can set by hand or leave following the device, and which
every city's offset and day difference are measured against.
- Other apps can now drive Clockula. The whole `android.provider.AlarmClock`
contract is answered — setting an alarm, setting a timer, showing the alarms
or the timers, dismissing an alarm, snoozing one and dismissing a timer — so
an assistant, an automation app or a watch companion can ask for any of it.
- Nothing another app sends can make Clockula write something you did not ask
for: a nonsense hour opens the alarm in the editor instead of setting one at
some other time, an unusable sound falls back to your default instead of
blocking the alarm, and a request that matches two of your alarms asks which
one you meant rather than guessing. A link to an alarm or a timer that cannot
be read dismisses nothing at all, rather than falling back to "all of them".
- An alarm or timer set by voice without showing you a screen removes itself
once it has been dismissed, so talking to your assistant every morning no
longer leaves a list of dead 06:30 alarms behind.
### Changed
- Tapping the alarm icon in the status bar, or the next-alarm line on the lock
screen, now opens your alarms. It used to open the ringing screen for an alarm
that was not ringing, which closed itself again straight away.
- The database moved to version 3, adding one column to the alarms and timers
tables for the alarms and timers another app asked to be temporary. Existing
alarms and timers are unaffected and stay exactly as they were.