Merge branch 'feat/18-volume-buttons' into release/v1.0.0

# Conflicts:
#	app/src/main/java/de/jeanlucmakiola/clockula/alarm/AlarmEngine.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/backup/BackupCodec.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/prefs/ClockPrefs.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/prefs/SettingsPrefs.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/domain/ClockDefaults.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/ui/settings/AlarmDefaultsScreen.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/ui/settings/SettingsViewModel.kt
#	app/src/test/java/de/jeanlucmakiola/clockula/data/backup/BackupCodecTest.kt
#	app/src/test/java/de/jeanlucmakiola/clockula/data/prefs/ClockPrefsTest.kt
#	app/src/test/java/de/jeanlucmakiola/clockula/data/prefs/SettingsPrefsTest.kt
#	docs/ARCHITECTURE.md
#	docs/BACKUP.md
This commit is contained in:
makiolaj committed 2026-10-26 20:49:43 +01:00
commit d81cd04b60
22 files changed
+592 -22

No files matched your search

+3 -2
View File
@@ -86,7 +86,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
| Package | Holds |
|---|---|
| `domain/` | `Alarm.kt`, `Timer.kt`, `WorldClock.kt`, `Stopwatch.kt`, `ClockDefaults.kt`, `Ringtone.kt` (the silent and bundled-tone sentinels), `RepeatSummary.kt`, `AlarmDefaults.kt`. Plain Kotlin, no Android |
| `domain/alarm/` | the alarm engine's pure half: occurrences, the resolver, the ring state, the presentation and scheduling policies, the challenge gate. The *shared* ring vocabulary moved out to `domain/ring/` in M6 |
| `domain/alarm/` | the alarm engine's pure half: occurrences, the resolver, the ring state, the presentation and scheduling policies, the challenge gate, `VolumeButtonPolicy` (#18). The *shared* ring vocabulary moved out to `domain/ring/` in M6 |
| `domain/ring/` | what both ring paths share: `RingAudio` (the ramp tick, the volume floor), `VolumeRamp`, `AudioSourcePolicy`/`RingtoneSourceKind`, `RingFallbackPolicy`, `VibrationPattern` |
| `domain/stopwatch/` | the stopwatch path's pure half: `StopwatchReadings` (the one reading the pill, the shade and the tab share), `LapStats` (best and worst), `StopwatchLaps` (the lap cap), `StopwatchNotificationPolicy` |
| `domain/timer/` | the timer path's pure half: `TimerReadings` (the app's one timer precedence), `TimerExpiry` (the sweep and the slot's value), `TimerRingPolicy`, `TimerNotificationPolicy`, `TimerPresets`, `TimerDurationEntry`, `TimerSettings`, `TimerRing` |
@@ -599,7 +599,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`, `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`, `volume_buttons_while_ringing` (#18; app-wide, no per-alarm override) | `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` (M7 is the first caller of the last, 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` |
@@ -1065,6 +1065,7 @@ M10 adds no `<uses-permission>`.
| Carrying `EXTRA_MESSAGE` into the timer setup panel when no length was given. There is no row to carry it on and the panel has no label field | not planned for v1 |
| Verifying a ringtone URI at the intent boundary. M5 locked "an unreadable sound is reported, never rewritten"; an unmounted card comes back, and the boundary opens no `ContentResolver` | not planned for v1 |
| An unlimited-snooze option, per-alarm auto-silence | not planned for v1 |
| The volume-button setting (#18) while the alarm rings only as a heads-up over another app, or with the screen turned off (the system keeps volume keys from a non-interactive device's windows). A background app sees no key events; the one route, a remote-playback `MediaSession` whose `VolumeProvider` takes the keys, is refused by `MediaSessionRecord.canHandleVolumeKey` on Android 13+ for an app with no MediaRouter2 routing session, loses to whichever app is in front or played last, and shows a cast-volume slider and a phantom media player while it works. AOSP DeskClock and BlackyHawky's Clock handle the keys in their alarm activity only, as `AlarmRingActivity` does | not planned |
| 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 |
+5 -1
View File
@@ -138,6 +138,7 @@ device or after a reboot (`docs/PLAN.md` §5). Every imported timer lands
"home_zone_id": null,
"default_alarm_silence_after_minutes": 10,
"default_timer_silence_after_minutes": 10,
"volume_buttons_while_ringing": "CONTROL_VOLUME",
"timer_presets": [60000, 120000, 180000, 300000, 600000, 900000, 1800000, 3600000],
"bedtime": {
"enabled": false,
@@ -166,7 +167,10 @@ the importing device creates its own (`docs/ARCHITECTURE.md` §18).
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.
other unknown field. `volume_buttons_while_ringing` is one of
`CONTROL_VOLUME`/`SNOOZE`/`DISMISS`/`NOTHING`; a file without it (any backup made
before the setting existed) or with a name this version does not know restores
`CONTROL_VOLUME`, the behaviour that file's device had.
`default_alarm_silence_after_minutes` and `default_timer_silence_after_minutes`
are how long a ringing alarm or a finished timer keeps going before it stops on