Merge branch 'feat/26-quick-settings-tiles' into release/v1.0.0

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/de/jeanlucmakiola/clockula/backup/BackupEngine.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/backup/BackupCodec.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/backup/BackupDto.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/data/prefs/SettingsPrefs.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/domain/backup/BackupContent.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/ui/common/AlarmTimeFormatter.kt
#	app/src/main/java/de/jeanlucmakiola/clockula/ui/widget/NextAlarmWidgetContent.kt
#	app/src/main/res/values/strings.xml
#	app/src/test/java/de/jeanlucmakiola/clockula/ManifestRulesTest.kt
#	app/src/test/java/de/jeanlucmakiola/clockula/data/backup/BackupCodecTest.kt
#	app/src/test/java/de/jeanlucmakiola/clockula/data/prefs/SettingsPrefsTest.kt
#	docs/ARCHITECTURE.md
#	docs/BACKUP.md
#	docs/ROADMAP.md
This commit is contained in:
makiolaj committed 2026-10-26 21:00:57 +01:00
commit df8be5e158
31 files changed
+998 -27

No files matched your search

+7 -3
View File
@@ -101,9 +101,10 @@ kit module: the kit's roadmap keeps schedulers app-local.
| `domain/bedtime/` | the bedtime schedule's pure half (issue #1): `BedtimeSettings`/`BedtimeEffects`/`BedtimeLimits` and `BedtimeSchedule` (the window a night resolves to, the active window, the due reminder, the next event). §18 |
| `domain/screensaver/` | the screen saver's pure half (issue #25): `ScreenSaverSettings` (style and night mode), `ScreenSaverDrift`, the once-a-minute burn-in position as fractions of the free space, derived from the minute alone, and `ScreenSaverAlarmDay`, which dates a next alarm a week or more away rather than naming its weekday alone |
| `domain/widget/` | the three widget presenters (M10), `NextAlarmWidgetModel`/`TimerWidgetModel`/`ClockWidgetModel`: selection and formatting only, so a widget's content is unit-tested without Glance |
| `domain/tile/` | `QuickTimerTileModel`/`QuickTimerTilePresenter` (#26): what the quick-timer Quick Settings tile shows and what a tap does, over the quick-timer widget's own selection. The next-alarm tile needs no presenter of its own; it reads `NextAlarmWidgetModel` |
| `data/db/` | `ClockulaDatabase` (`@Database` v4, `exportSchema = true`) and `Migrations` |
| `data/alarms/` | the alarm and ring-state entities, DAOs, mappers and repositories |
| `data/timers/` | `TimerEntity`, `TimerDao`, `TimerMapper`, `TimerRepository(+Impl)`, `TimerRingStateStore` (the ring session's one DataStore record) |
| `data/timers/` | `TimerEntity`, `TimerDao`, `TimerMapper`, `TimerRepository(+Impl)`, `TimerRingStateStore` (the ring session's one DataStore record), `QuickTileStateStore` (which timer the quick-timer tile started, #26) |
| `data/worldclocks/` | `WorldClockEntity`, `WorldClockDao`, `WorldClockMapper`, `WorldClockRepository(+Impl)`, unchanged since M2; M8 is the milestone that finally calls all of it |
| `data/zones/` | the ICU seam (M8): `ZoneNames`, `IcuZoneNames` (the only `android.icu` file in the app) and `ZoneDirectory`, the locale-keyed catalog cache and the DST-keyed display-name memo |
| `data/stopwatch/` | `LapEntity`, `LapDao` (one `COUNT(*)` added in M7), `LapMapper`, `StopwatchStateStore`, `StopwatchRepository(+Impl)` |
@@ -118,7 +119,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
| `alarm/ring/` | the alarm's ringing foreground service and its notifications. Its audio player and vibrator moved to `ring/` in M6, shared with the timer's ring |
| `alarm/di/` | `AlarmModule`: `@Binds` for the four seams |
| `ring/` | `RingAudioPlayer` and `RingVibrator`: one `MediaPlayer` wrapper and one vibrator for both ring paths. A build rule fails on a second one |
| `timer/` | `TimerEngine` and the three seams it talks to (`TimerScheduler`, `TimerServiceHandle`, `AlarmRingStatus` + `AlarmStateRingStatus`), plus `TimerIntents` |
| `timer/` | `TimerEngine` and the three seams it talks to (`TimerScheduler`, `TimerServiceHandle`, `AlarmRingStatus` + `AlarmStateRingStatus`), plus `TimerIntents`, and `QuickTileTimer` (#26), the quick-timer tile's model and tap, through the engine and with no Android type |
| `timer/android/` | the seams' Android implementations: the one elapsed-realtime AlarmManager slot, the service handle |
| `timer/receiver/` | `TimerExpiryReceiver` (the slot arriving), `TimerActionReceiver` (the notification's buttons) |
| `timer/service/` | `TimerService` (one foreground service for the countdown and the ring) and `TimerNotifications` |
@@ -132,7 +133,9 @@ kit module: the kit's roadmap keeps schedulers app-local.
| `bedtime/` | `BedtimeEngine` (issue #1) and its three seams, `BedtimeScheduler`, `BedtimeMode` and `BedtimeNotifier`, plus `BedtimeIntents`. `bedtime/android/` holds their implementations (the one wall-clock slot, the Do Not Disturb rule, the reminder), `bedtime/receiver/` the `BedtimeReceiver`, `bedtime/di/` the `BedtimeModule`. §18 |
| `system/` | `RebootRepair`, the boot-id gate |
| `backup/` | `BackupEngine` (M10), the orchestrator (a ringing alarm dismissed and running timers deleted before a replace, `AlarmEngine.reschedule()`/`TimerEngine.resync()` after it), plus `BackupFiles`, the SAF seam, with its Android implementation in `backup/android/` |
| `widget/` | the three `GlanceAppWidget`/`GlanceAppWidgetReceiver` pairs (M10) and `WidgetSync`, the event-driven collector (started from `ClockulaApp.onCreate`) that redraws all three on any change to the state they mirror. `widget/di/` holds the `@EntryPoint` Glance needs, since a `GlanceAppWidgetReceiver` is never `@AndroidEntryPoint`-constructed |
| `widget/` | the three `GlanceAppWidget`/`GlanceAppWidgetReceiver` pairs (M10) and `WidgetSync`, the event-driven collector (started from `ClockulaApp.onCreate`) that redraws all three, and since #26 the two Quick Settings tiles, on any change to the state they mirror. `widget/di/` holds the `@EntryPoint` Glance needs, since a `GlanceAppWidgetReceiver` is never `@AndroidEntryPoint`-constructed |
| `tile/` | the two Quick Settings tiles (#26): `NextAlarmTileService` and `QuickTimerTileService`, which only render and forward a click, and `TileKit` (opening the app, behind an unlock on a locked device, through the `PendingIntent` overload on API 34+; asking for a redraw). Both are *active* tiles, redrawn only when `WidgetSync` (on a change they mirror) or `SystemEventReceiver` (on a clock or zone change) asks the system to, never on every panel open |
| `ui/tile/` | `TilePreferencesActivity` (#26), what long-pressing a tile opens: the quick-timer tile's duration, through the quick-timer widget's own picker |
| `ui/theme/`, `ui/crash/` | the M0/M1 theme and the crash-report surface |
| `ui/shell/` | the navigation policy (`ShellNavigation`), the adaptive shell, the live pill and its source/ViewModel, the notification-permission ask. `EmptyTabScreen` is gone: its KDoc said it existed only until each of M5–M8 replaced its own tab, and the world clock was its last caller |
| `ui/common/` | `rememberLocalTimeFormatter`, the app's one 12/24-hour formatter, over a `java.time.LocalTime`, with `rememberAlarmTimeFormatter` delegating to it (M8 D17); and, since M6, the ringtone picker (`RingtonePickerState`, `RingtonePickerScreen`), which both editors need |
@@ -621,6 +624,7 @@ Six slices:
| Timers (M6) | `timer_presets`, `timer_ring_sounding_since_millis` | `TimerPrefs`: the presets surfaced as `SettingsPrefs.timerPresets`, the ring session's anchor behind `TimerRingStateStore` |
| Bedtime (issue #1) | `bedtime_enabled`, `bedtime_start_minutes`, `bedtime_wake_minutes`, `bedtime_days`, `bedtime_reminder_minutes`, `bedtime_effect_grayscale`, `bedtime_effect_dim_wallpaper`, `bedtime_effect_dark_theme`, `bedtime_effect_hide_aod`, `bedtime_applied_active`, `bedtime_rule_stale`, `bedtime_state_owed` | `BedtimePrefs`, surfaced as `SettingsPrefs.bedtime: Flow<BedtimeSettings>` and written as one record by `BedtimeEngine` only. Times clamp into a day, the reminder into 0–120 minutes, the days through `RepeatDays.fromMask`. The system rule's id is **not** stored (§18) |
| Screen saver (issue #25) | `screen_saver_style`, `screen_saver_night_mode` | `ScreenSaverPrefs`, surfaced as `SettingsPrefs.screenSaver: Flow<ScreenSaverSettings>` and carried in the backup |
| Quick-timer tile (#26) | `quick_tile_duration_millis`, `quick_tile_timer_id` | `TimerPrefs`: the duration (a backed-up setting) surfaced as `SettingsPrefs.quickTileDuration`, the transient timer the tile started (volatile, not backed up) behind `QuickTileStateStore` |
The stopwatch's *run* is a single record, so it lives in DataStore rather than
as a one-row table (`PLAN.md` §5); its *laps* are a list, so they live in Room.
+6 -1
View File
@@ -54,6 +54,10 @@ written here.
`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.
- A missing `quick_tile_duration_millis` (a file from before the Quick
Settings tiles), or one outside the quick-timer range of 1 second to 59:59,
keeps the quick-timer tile's default of five minutes — the same reading the
quick-timer widget gives its own stored duration.
- An enum field with a name this version does not recognise (a `dismiss_
challenge` or `theme_mode` from a future version, say) degrades to that
field's default, the same forgiving-read posture `AlarmMapper` already uses
@@ -174,7 +178,8 @@ device or after a reboot (`docs/PLAN.md` §5). Every imported timer lands
"hide_always_on_display": false
},
"screen_saver_style": "DIGITAL",
"screen_saver_night_mode": false
"screen_saver_night_mode": false,
"quick_tile_duration_millis": 300000
}
```
+7 -1
View File
@@ -616,7 +616,13 @@ Deferred deliberately, in rough priority order:
`ClockulaDreamService` plus `ScreenSaverActivity` for the overflow menu's
"Screen saver", digital or analog, with a dim night mode and a once-a-minute
drift against burn-in (`ARCHITECTURE.md` §3, §6).
- **Quick Settings tile**: timer control and next alarm from the shade.
- ✅ **Quick Settings tiles** (#26): next alarm and a quick timer from the
shade. "Next alarm" reads the next-alarm widget's model and opens the Alarms
tab (after an unlock, on a locked device); "Quick timer" is the quick-timer
widget's twin, with its own duration (long-press to choose; in the backup),
and works on a locked device as the timer notification does. Both are active
tiles, redrawn by `WidgetSync` on a write rather than bound on every panel
open. No new permission.
- 🚧 **Bedtime / sleep schedule** (issue #1): a first version exists as an
opt-in screen under Settings, not a fifth tab. Clockula's own Do Not Disturb
mode (a real mode in Settings › Modes on Android 15+, with grayscale, dimmed