Commit Graph
12 Commits
Author SHA1 Message Date
makiolajandClaude Sonnet 5 d47220947b feat(timers): redo the add-timer flow as a full-screen calculator keypad
The Timers tab gets a full rewrite of how a timer is created, after Google
Clock as the interaction reference (PLAN.md §11):

- A dedicated TimerSetupScreen replaces the bottom-sheet/inline setup panel:
  a per-unit "00h 00m 00s" readout, a "00" key, and a big centred play
  button that never shifts position when Clear fades in beside it.
- The keypad grows into the thumb zone on its own screen (92dp keys, pushed
  toward the bottom), and both it and the readout read bold.
- Presets are dropped from the flow; TimerDurationEntry gains the matching
  domain support (hoursSegment/minutesSegment/secondsSegment,
  plusDoubleZero()).
- An empty timer list shows the same readout/keypad/Start inline, in place
  of a "tap + to add" card — the keypad is still the empty state, just the
  redesigned one.
- Timer cards are disconnected like the Alarms tab's own list, each with an
  instant, unconfirmed delete via a small corner button (re-creating a
  timer is faster than reading a confirmation dialog would be).
- Hero readouts go bold app-wide to match: stopwatch, alarm rows, world
  clock, the ring screen, and both editors' length/time displays
  (ClockulaReadoutDefaults.Hero and matching call sites).

Alongside: Android 16 Live Update support for the running-timer notification
(POST_PROMOTED_NOTIFICATIONS, ProgressStyle), the live pill restyled to a
plain tonal circle, the alarm dismiss-chooser dialog moved onto Floret's
OptionCard, and small fixes to addTime's zero/negative-result handling,
repeat-day selection and alarm routes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-01 17:06:54 +02:00
makiolaj 46a049f988 feat(alarms): ask which alarm, when the request names more than one
"Dismiss my alarm" with two alarms set is a question, not an instruction. When
the search matches more than one the app asks, in a dialog listing the
candidates; when it matches exactly one it acts without asking.

The Timers tab's setup effect no longer re-keys on a list that changes with
every readout tick.
2026-09-23 11:02:20 +02:00
makiolaj 74a6295543 feat(data): schema v3 — an alarm or a timer that deletes itself after use
The `AlarmClock` contract says twice that an alarm or a timer created with
`SKIP_UI` should be removed once it has been dismissed. Without somewhere to
record that, a voice user's alarm list becomes a graveyard of one-shots nobody
asked to keep.

One column on each of `alarms` and `timers`, defaulting to 0, and a migration
that adds them. Existing rows keep the behaviour they have always had.
2026-09-23 11:01:31 +02:00
makiolaj 26f1effb4f feat(worldclock): the tab, the picker, and the face that answers at a glance
A hero face for home over a list of cities, each carrying its time, its offset
and how many days apart it is from you. The dial morphs between a circle and a
sun with the hour *there*: the one showpiece M0 reserved, spent on information
rather than decoration — a glance says "it is the middle of the night for them"
with no sentence needed.

The picker searches the device's own zones by word prefix. The list reorders by
drag **and** by move-up/move-down actions, because a drag-only reorder is
unreachable by a screen reader; each row reads as one sentence rather than
three texts in child order. Home is set by hand or left following the device.
At the twenty-fourth city the refusal is written on the screen, not only in the
content description.
2026-09-22 13:05:18 +02:00
makiolaj 7b727e0d40 feat(stopwatch): the tab — the readout, the laps, and the fastest and slowest
A fixed readout over a lap list, newest first, the lap in progress counting
as its own row once there is a lap to compare it against. The fastest and
the slowest are marked in `primary` and `tertiary` — never `error`, and never
by colour alone: each carries a spoken marker so the emphasis survives being
read aloud.

The state rebuilds only what actually moves: the recorded rows and their
emphasis come off the lap table once, and the tick recomputes the hero
figure and the row in progress. Laps stop at 999 and the readout ticks only
while the stopwatch is running.
2026-09-22 10:23:54 +02:00
makiolaj b67142a727 feat(timers): the timers tab, and the pill that finally has something to show
Multiple concurrent timers with labels, presets, add, pause, reset and +1 min.
The setup panel is inline on an empty tab, so the first timer costs no
navigation.

The live pill has been waiting since M4 for something that could start a
timer. It now goes through the engine rather than the repository, because a
scheduler registration and a foreground service have to move with the state —
its own contract is unchanged.

Three new architecture rules keep it that way: no screen may name the
scheduler, the service or AlarmManager, and a second MediaPlayer anywhere
outside the ring path fails the build.
2026-09-12 16:48:36 +02:00
makiolaj 5430e0c778 refactor: lift the ring machinery out of the alarm package
The audio player, the vibrator, the volume ramp and the audio-source policy
were never alarm-specific — timers need the same ones, and a second
MediaPlayer in the app would be a bug. Moved and renamed, no behaviour
changed: the moved tests differ by their package line and one constant name.

The ringtone picker moves to ui/common for the same reason.
2026-09-12 16:48:07 +02:00
makiolajandClaude Opus 5 97ca617e6a feat(alarms): the list and the editor
The tab the app has been missing. Rows carry the time, the repeat summary and
the next-fire countdown, ordered by time of day rather than by when they fire
next — a row must not move under the thumb reaching for it, so the next alarm
is marked by colour instead. The editor covers time, repeat days, label,
ringtone, vibration and snooze, with the five per-alarm overrides as
three-state pickers: a switch cannot say "I have not chosen".

Two things that look like details and are not. A ringing alarm is dismissed
through the engine before it is disabled, edited or deleted — otherwise its
state is cleared while the service keeps sounding and the auto-silence backstop
returns early on its own guard, leaving the alarm ringing until the wake-lock
timeout. And clearing the last repeat day disarms a pending skip, because
otherwise the resolver disables the alarm and the control that would have
explained it is already hidden.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
2026-09-12 14:43:46 +02:00
makiolaj 959eb599ff feat(ring): the ring screen, over the lock screen
Shows over the lock screen, turns it on, keeps it on, and swallows back for
the whole lifetime of the window — including while the session is still
loading, which is where a back press used to finish the activity and leave
the alarm sounding with nothing on screen to stop it.

The optional dismiss challenge cannot strand the user: the standing wrong
answer clears the moment they start typing the next one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
2026-09-12 13:07:18 +02:00
makiolaj 83c5e9fe3f feat(shell): the navigation host, four tabs and the live pill
`NavigationSuiteScaffold` gives the bar on a phone and the rail on a wide
layout from one declaration. Each tab keeps its own back stack; re-selecting
a tab returns to its root, and predictive back is wired only where back
actually goes somewhere.

The pill is the running-state surface from PLAN §9: it floats above the bar,
speaks for whichever subject is closest to needing attention, and pauses or
stops it from any tab. Its callbacks are gated on a live subject, so a second
tap during its exit animation cannot restart a timer the user just reset.

The notification permission is asked once, and the flag is written from the
request's result rather than before it — a process death mid-dialog leaves
the ask due again rather than recorded and never made. Three tabs are empty
shells until M5 through M8 fill them.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
2026-09-12 13:07:18 +02:00
makiolajandClaude Opus 5 f4b7c1a16c feat(data): ring state in a new alarm_states table at schema v2
Snooze, the handled-occurrence watermark and the skip watermark need somewhere
to live that survives a process death. They go in their own table rather than
as columns on alarms: a missing row is the initial record, and the FK cascade
means deleting an alarm cannot leave orphaned ring state behind.

Schema v2 is exported and committed alongside v1, so MIGRATION_1_2 is
reviewable and testable rather than taken on faith.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:04:26 +02:00
makiolajandClaude Opus 5 778d08af24 feat(data): the Room database, its four tables and their DAOs
alarms, timers, world_clocks and stopwatch_laps, with the schema exported
to app/schemas and committed so migrations can be tested from v1 onwards.

The DAOs return Flows for reads and keep every multi-step write inside a
@Transaction, so a read-modify-write cannot lose a concurrent one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 13:52:11 +02:00