From f5052f3fa9a072d43ea0d7aec2fa79f44f422c23 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Tue, 6 Oct 2026 19:14:11 +0200 Subject: [PATCH] chore: 1.0.0-beta.1 The first public build. CHANGELOG's Unreleased section is rewritten as user-facing notes, since it becomes the beta's release notes. The README gains an install section, and the store short description fits Play's 80 characters. --- CHANGELOG.md | 284 ++++-------------- README.md | 45 ++- app/build.gradle.kts | 12 +- .../android/en-US/short_description.txt | 2 +- 4 files changed, 90 insertions(+), 253 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a6daf..dd7bc36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,240 +4,58 @@ 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). +Tag sections feed the release notes; see [`docs/RELEASING.md`](docs/RELEASING.md). ## [Unreleased] +The first release of Clockula: alarms, timers, a stopwatch and a world clock. + ### 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. -- A settings screen, reachable from a gear icon on every tab: theme, dynamic - colour and language, the alarm/timer/clock defaults every new alarm or timer - inherits, help, and the app version. -- Back up your alarms, timers and world clocks to a JSON file in a folder you - choose, and restore them on this device or another one. Restoring replaces - everything currently on the device — the app says so before you confirm. - Nothing about a ringing alarm or a running timer's live state travels with - the backup, only the configuration. -- A "why might my alarm not ring?" screen that checks the actual state of your - device — exact-alarm and notification permissions, full-screen alerts, - battery optimisation, Do Not Disturb, your alarm volume, and whether the - system has actually scheduled Clockula's next alarm — and links straight to - whichever settings page would fix each one. -- Three home-screen widgets: your next alarm, a running or paused timer (with - pause, resume and reset right on the widget), and a clock that grows to show - your world clocks at larger sizes. Each one updates the moment something - changes — no polling, no stale numbers. -- Wake up to your own music: "Choose from a music app…" in the sound picker - lets any app that shares its library (VLC, AntennaPod and many others) play - your alarm or timer — a playlist, an album, a single track, or just whatever - it played last. The music app does the playing, so Clockula still has no - internet permission. If the app can't play — uninstalled, logged out, - offline, or stops partway — your alarm rings with the default sound instead. - It does the same when the music might not be heard: headphones or a - Bluetooth device connected, or Do Not Disturb muting media. -- Swipe left or right to move between Alarms, Timers, Stopwatch and World - clock. Phones only: tablets and foldables with a side rail keep tap - navigation. -- The World clock tab shows your home time as text under the analog face, in - your 12/24-hour format. -- The quick-timer widget can start any duration up to 59:59: pick "Custom…" in - its settings and type it on the timer keypad. -- Widgets can follow the system theme or always be light or dark, and on - Android 12+ use the app's own colours instead of the wallpaper's. This is set - per widget in its settings; the next-alarm and timer widgets now have a - settings screen for it, and the analog and digital clocks offer it on - Android 12+. - -### 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. -- Tapping the digital or analog clock widget opens Alarms, and each clock - widget has an "On tap" setting to open Alarms, Timers, Stopwatch or World - clock instead. - -### Removed -- The "Default duration" setting under Settings > Timers. It had no effect, - since new timers are typed on the keypad. Older backup files that contain it - still import. - -### Fixed -- The add-timer keypad shrinks to fit short screens, landscape, split screen - and large display or font sizes, so the Start button is never cut off. The - same goes for the keypad on the empty Timers tab. -- Tapping a widget sometimes showed the screen the app was last left on - instead of the widget's tab. +- **Alarms that ring.** Alarms are scheduled as real alarm clocks, exempt from + battery saving and shown on the lock screen and in the status bar. They + survive a reboot, a process kill and a change of the device's clock, and + daylight-saving nights neither skip nor double one. If the full-screen or + notification permission is denied, an alarm still rings. The last fallback + is vibration, never silence. +- An alarm list that shows when each alarm will ring and marks the next one. + Changes apply as you make them, with no Save button. +- Repeat days that start on your locale's first day of the week, and a "skip + next alarm" switch so you can sleep in once without turning the alarm off. +- Per-alarm sound, vibration, gradual volume, snooze length and limit, and a + dismiss challenge (a quick sum or a two-second hold). Each one can follow the + app default, so changing a default later still reaches it. +- Your own sounds: the device's alarm tones, a file you pick, Silent (which + still vibrates), or music from an app that shares its library, such as VLC + or AntennaPod. If the music can't play, or might not be heard, the alarm + rings with the default sound instead. +- A ring screen over the lock screen with snooze and dismiss. The dismiss + challenge always gives you a way out, works with a screen reader, and Back + can't silence an alarm. +- **Timers.** Run several at once, each with its own label, length and sound, + set from a keypad with saved presets. Pause, resume, reset and +1 min work + from the timer, from the live pill and from the notification. Timers keep + counting through a reboot and aren't affected by changes to the clock. +- **Stopwatch** with laps, the fastest and slowest marked, and Lap and Pause + in the notification. It keeps counting while the app is closed. +- **World clock.** An analog face for your own time, plus cities from the + device's time-zone database, searchable in your language. Each city shows + the offset from your time and whether it's a different day there. Reorder + them by dragging or with a screen reader, and set a home time zone or follow + the device. +- A live pill that shows whatever is counting from every tab. +- Swipe between tabs on phones. Tablets and foldables get a side rail. +- Other apps can set, show, snooze and dismiss alarms and timers through + Android's standard alarm intents, so assistants and automation apps work + with Clockula. A voice-set alarm cleans up after itself once dismissed. +- Settings: theme, dynamic colour, language, and the defaults every new alarm + and timer inherits. +- Back up alarms, timers, world clocks and settings to a JSON file, and restore + them on this device or another one. +- "Why might my alarm not ring?", a self-check that reads your device's real + state: permissions, battery optimisation, Do Not Disturb, alarm volume, and + whether the system has Clockula's next alarm. It links straight to the + setting that fixes each problem. +- Home-screen widgets: next alarm, timer (with controls), a quick timer, and + analog and digital clocks that show your world clocks at larger sizes. Each + widget can follow the system theme or stay light or dark, and choose what a + tap opens. diff --git a/README.md b/README.md index d3f5776..51ad69a 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,20 @@ Alarms, timers, stopwatch and world clock. Open standards, no account required.< Material 3 Expressive MIT License +

+Get it on Obtainium +

+ Clockula is the clock sibling to [Calendula](https://codeberg.org/jlmakiola/calendula) (calendar) and [Agendula](https://codeberg.org/jlmakiola/agendula) (tasks). The name follows theirs: *clock*, given Calendula's `-ula` ending. A Calendula flower head is botanically a cluster of many small *florets*, so the three apps are -florets of one bloom — and they share one, [floret-kit](https://codeberg.org/jlmakiola/floret-kit). +florets of one bloom. They also share one library, [floret-kit](https://codeberg.org/jlmakiola/floret-kit). -> 🚧 **Early development.** The spec is locked; the app is being built. See -> [`docs/ROADMAP.md`](docs/ROADMAP.md) for where it actually is. +> 🧪 **Beta.** Clockula 1.0.0 is in beta: feature-complete, and being tested +> before the stable release. See [Install](#install) to try it. ## The honest difference from its siblings @@ -28,31 +32,44 @@ Calendula is a pure front-end over Android's `CalendarContract`. Agendula speaks the dmfs `TaskContract`. Both put a good interface on a standard someone else already maintains. -**There is no open provider behind a clock.** Alarms, timers and laps are +There is no open provider behind a clock. Alarms, timers and laps are Clockula's own data, so Clockula is the first app in the family to own its -storage — by design, not as a fallback. What that commitment is replaced with is -the first section of [`docs/PLAN.md`](docs/PLAN.md), and it comes down to three -things: +storage, and does so by design. The first section of +[`docs/PLAN.md`](docs/PLAN.md) sets out what takes the place of a shared +standard, and it comes down to three things: -- **Clockula speaks Android's standard alarm contract.** It implements +- Clockula speaks Android's standard alarm contract. It implements `android.provider.AlarmClock` in full, so your assistant and your automation apps can set alarms and timers in it exactly as they would in any stock clock, and the system status bar and lock screen show its next alarm. -- **World clocks are IANA time zones**, not a bespoke city list that goes stale. -- **Your data exports** to a documented, versioned JSON file whenever you want +- World clocks are IANA time zones, not a bespoke city list that goes stale. +- Your data exports to a documented, versioned JSON file whenever you want it, because data you can't take with you isn't really yours. ## Alarms that actually ring -An alarm app is only worth having if you can trust it, so Clockula treats that as -the feature rather than the plumbing. Alarms are scheduled as real alarm clocks — -doze-exempt, visible to the system — and re-resolved against your current time +An alarm app is only worth having if you can trust it, so Clockula treats reliability +as the main feature. Alarms are scheduled as real alarm clocks (doze-exempt and +visible to the system) and re-resolved against your current time zone whenever anything moves underneath them, so DST and travel don't silently shift them. When something on your device *would* stop an alarm ringing, Clockula tells you which setting it is and takes you there, instead of failing quietly at 7am. +## Install + +Betas are published as pre-releases on +[Codeberg](https://codeberg.org/jlmakiola/clockula/releases), with the signed +APK and a `.sha256` checksum attached. Download the APK there, or add +`https://codeberg.org/jlmakiola/clockula` to +[Obtainium](https://github.com/ImranR98/Obtainium) and switch on *Include +prereleases* for it so it finds the beta. A beta updates in place to later betas +and to the stable release. + +From the stable 1.0.0 on, every release also goes to the self-hosted F-Droid +repository that serves Calendula and Agendula. + ## Documentation Start with [`docs/`](docs/): [`PLAN.md`](docs/PLAN.md) is the spec and the @@ -75,4 +92,4 @@ in a (gitignored) `floret-kit/local.properties`. ## Licence -MIT — see [`LICENSE`](LICENSE). +MIT. See [`LICENSE`](LICENSE). diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6ba49cd..335d1ab 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -27,11 +27,13 @@ android { // These committed values ARE the source of truth for a release: merging // a bumped versionName into main triggers .gitea/workflows/release.yaml, // which builds this version and then creates the matching vX.Y.Z tag + - // release itself (versionCode is pinned to MAJOR*10000 + MINOR*100 + - // PATCH from versionName, e.g. 0.2.0 -> 200). The Gitea release is marked - // as a pre-release while MAJOR is 0. See docs/RELEASING.md. - versionCode = 100 - versionName = "0.1.0" + // release itself. A versionName of X.Y.Z-beta.N pushed to a release/* + // branch instead cuts a Codeberg-only pre-release (beta.yaml). + // versionCode is derived from versionName by scripts/version_info.sh + // (1.0.0-beta.1 -> 1000001, 1.0.0 -> 1000099), and CI fails if the + // committed one doesn't match. See docs/RELEASING.md. + versionCode = 1000001 + versionName = "1.0.0-beta.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt index ad1c53b..49c9c89 100644 --- a/fastlane/metadata/android/en-US/short_description.txt +++ b/fastlane/metadata/android/en-US/short_description.txt @@ -1 +1 @@ -Alarms, timers, stopwatch and world clock. Material 3 Expressive, open source, no account. +Alarms, timers, stopwatch and world clock. Open source, no account, no tracking.