feat(timers): give timers their own vibration and gradual volume

Timers vibrated by the alarm vibration default and never ramped. Both are
now the timers' own app-wide settings, next to the default timer sound in
Settings > Timers: a Vibrate switch and the same Gradual volume picker
alarms use. Timer.resolveSettings reads them instead of the alarm default
and the old TimerRing.RAMP_SECONDS constant.

A fresh install starts with timer vibration off, as Google Clock does, and
no ramp. An existing install keeps what its timers did: a one-shot DataStore
migration writes the new key from the alarm vibration default before the
first read, so nobody's kitchen timer silently stops buzzing on update. A
fresh install is told apart by an empty store (anything that has run once
has written its boot id); the migration writes the default there too, so it
never runs twice.

Backups carry both new fields. A file without them restores the alarm
vibration default as the timers' (what that install's timers did) and a
flat ring.

Silent plus vibration off still signals: Silent gives the player no
sources, and its onSilenced callback starts the vibrator whatever the
setting says. The settings row says so.

Fixes #22
This commit is contained in:
makiolaj committed 2026-10-21 18:49:45 +02:00
1 parent 3f431a7f32
commit fbc3a192bf
21 files changed
+375 -38

No files matched your search

+6
View File
@@ -42,6 +42,10 @@ written here.
are clamped at parse time to the same ranges as the defaults they
override, and a timer's `duration_millis` to the setup range (1 second to
24 hours). Room has no clamp of its own, so this is where it happens.
- A missing `default_timer_vibrate` (a file from before timers had their own
vibration setting) restores `default_vibrate` in its place: that is what
the exporting install's timers vibrated by. A missing
`default_timer_volume_ramp_seconds` restores 0, the ring timers had then.
- A missing `timer_presets` keeps the built-in presets. An empty list is a
user who removed them all, and is imported as such.
- An enum field with a name this version does not recognise (a `dismiss_
@@ -134,6 +138,8 @@ device or after a reboot (`docs/PLAN.md` §5). Every imported timer lands
"default_volume_ramp_seconds": 15,
"default_alarm_ringtone_uri": null,
"default_timer_ringtone_uri": null,
"default_timer_vibrate": false,
"default_timer_volume_ramp_seconds": 0,
"default_dismiss_challenge": "NONE",
"home_zone_id": null,
"timer_presets": [60000, 120000, 180000, 300000, 600000, 900000, 1800000, 3600000]