refactor(settings): remove the unused default timer duration

Settings > Timers offered a "Default duration", but nothing that creates
a timer ever read it: the keypad starts empty. The row, its preference,
ClockDefaults.timerDuration and the backup field are gone, and the
screen's hint and the hub's subtitle now only speak of the sound.

Backups no longer write default_timer_duration_millis. Older files that
carry it still import, since the codec ignores unknown keys.

Closes #5
This commit is contained in:
2026-10-05 19:28:31 +02:00
parent face2edb8d
commit cac5b2dfca
18 changed files with 55 additions and 101 deletions
+9 -8
View File
@@ -597,7 +597,7 @@ Four slices:
| Slice | Keys | Owner |
|---|---|---|
| Appearance (M1) | `theme_mode`, `dynamic_color` | `AppearancePrefs` in `core-prefs` — the family's shared key names |
| Clock defaults (M2) | `default_snooze_minutes`, `default_snooze_limit`, `default_vibrate`, `default_volume_ramp_seconds`, `default_alarm_ringtone_uri`, `default_timer_ringtone_uri`, `default_dismiss_challenge`, `default_timer_duration_millis`, `home_zone_id` | `ClockPrefs`, surfaced as `SettingsPrefs.defaults: Flow<ClockDefaults>` |
| Clock defaults (M2) | `default_snooze_minutes`, `default_snooze_limit`, `default_vibrate`, `default_volume_ramp_seconds`, `default_alarm_ringtone_uri`, `default_timer_ringtone_uri`, `default_dismiss_challenge`, `home_zone_id` | `ClockPrefs`, surfaced as `SettingsPrefs.defaults: Flow<ClockDefaults>` |
| Stopwatch run record (M2) | `stopwatch_state`, `stopwatch_started_elapsed_millis`, `stopwatch_accumulated_millis`, `stopwatch_last_lap_cumulative_millis` — the last of which M7 is the first caller of, for the in-progress lap's split | `StopwatchPrefs`, behind `StopwatchStateStore` |
| System facts (M3) | `last_boot_id` | `SystemPrefs`, behind `BootStateStore` — the persisted half of the reboot gate |
| Timers (M6) | `timer_presets`, `timer_ring_sounding_since_millis` | `TimerPrefs` — the presets surfaced as `SettingsPrefs.timerPresets`, the ring session's anchor behind `TimerRingStateStore` |
@@ -612,7 +612,7 @@ process killed mid-write — ever sees `RUNNING` without its start anchor.
**Clamp on read as well as on write.** Every bounded value goes through
`Pref.map`, which applies the same clamp in both directions: snooze 1–60
minutes, snooze limit 0–10, volume ramp 0–60 s, timer duration 1 s–24 h. A
minutes, snooze limit 0–10, volume ramp 0–60 s. A
hand-edited file or a restore from a future version therefore cannot produce a
value the user could never have chosen. Unknown enum names degrade to their
default; a `home_zone_id` the device's tzdata no longer knows reads back as
@@ -1063,7 +1063,7 @@ M10 adds no `<uses-permission>`.
| Reordering timers by hand. `sort_order` and `TimerRepository.reorder` exist and stay caller-less: the list is in storage order, M5 gave the same answer for alarms, and a drag surface would also mean abandoning the `LazyColumn` | not planned for v1 |
| Per-timer vibrate, volume-ramp or dismiss-challenge overrides. `timers` has exactly one nullable settings column, its ringtone; four more would be a schema change for settings the roadmap does not ask for | not planned for v1 |
| A "stop all" for several expired timers, and undo after a timer delete. Each expired timer is acknowledged on its own, and the delete confirmation asks before rather than after | not planned for v1 |
| A presets management screen (add/remove the quick-start durations themselves). M10's settings screen edits `default_timer_duration_millis` and `default_timer_ringtone_uri`, but the preset list is still only editable from the two-gesture surface on the setup panel | not planned for v1 |
| A presets management screen (add/remove the quick-start durations themselves). M10's settings screen edits `default_timer_ringtone_uri`, but the preset list is still only editable from the two-gesture surface on the setup panel | not planned for v1 |
| A timer that ramps its volume, a dismiss challenge or a snooze for a timer | not planned for v1 |
| Surfacing `TimerSnapshot.anchorIsStale` in the UI. The boot repair makes it vanishingly rare and the value is already honest; a row does not say "estimated" in v1 | not planned for v1 |
| Lap export, sharing, deletion or editing, and any "previous run" history. A run's laps belong to the run that produced them; keeping old runs is a different feature with its own table | not planned for v1 |
@@ -2018,16 +2018,17 @@ death; the pure `TimerDurationEntry` is derived from the saved digit string, and
digits in from the right and reads them as `h*3600 + m*60 + s` **without
clamping minutes or seconds to 59**, so `0:00:99` is 99 seconds — Google Clock's
forgiving behaviour, and a pure function with obvious tests. `canStart` is false
outside 1 s…24 h, which is `ClockPrefs`' own clamp, so nothing the user can
choose is clamped away on the next read.
outside 1 s…24 h.
Presets are **app-wide** quick-start durations in DataStore (§6), not a property
of one row: a `timers` row already *is* its own preset, because it persists and
`reset` returns it to its configured duration. The whole management surface is
two gestures on the setup panel — a Save chip when the typed duration is not
already one, and each chip's own remove affordance. A new timer's pre-filled
duration is `ClockDefaults.timerDuration`, the `default_timer_duration_millis`
preference M2 built and nothing had called until now.
already one, and each chip's own remove affordance. The keypad starts empty;
there is no default timer length. (M2 built a `default_timer_duration_millis`
preference and M10 gave it a settings row, but nothing that creates a timer
ever read it, so it was removed. A backup file that still carries the key
imports with it ignored.)
A timer has exactly **one** per-setting override, its ringtone, because `timers`
has exactly one nullable settings column. Vibrate and the (zero) ramp come from
+5 -3
View File
@@ -33,7 +33,7 @@ written here.
defines — `format`, `version`, an alarm's `hour`/`minute`, a world clock's
`zone_id`, a timer's `duration_millis` — rejects the **whole file** with a
specific reason. Nothing is imported half-way.
- Settings values (snooze minutes, volume ramp, timer duration, …) are
- Settings values (snooze minutes, volume ramp, …) are
**not** range-checked at parse time; they pass through the same clamps
`ClockPrefs`/`SettingsPrefs` already apply on every write, so a value from
a future, more permissive version degrades to the nearest valid one instead
@@ -118,13 +118,15 @@ device or after a reboot (`docs/PLAN.md` §5). Every imported timer lands
"default_alarm_ringtone_uri": null,
"default_timer_ringtone_uri": null,
"default_dismiss_challenge": "NONE",
"default_timer_duration_millis": 300000,
"home_zone_id": null,
"timer_presets": [60000, 120000, 180000, 300000, 600000, 900000, 1800000, 3600000]
}
```
Field names mirror `ClockPrefs`'/`SettingsPrefs`' own on-disk keys. Every
Field names mirror `ClockPrefs`'/`SettingsPrefs`' own on-disk keys. Older
files may also carry `default_timer_duration_millis`, from a default timer
length setting that has since been removed; it is ignored on import like any
other unknown field. Every
numeric/duration value is clamped on *apply*, through the existing pref
setters — never on parse.