`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.
192 lines
13 KiB
Markdown
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.
|