feat(tiles): Quick Settings tiles for the next alarm and a quick timer
"Next alarm" reads the next-alarm widget's model and shows the weekday and time of the next alarm, or "No alarms"; a tap opens the Alarms tab, behind an unlock on a locked device, through the PendingIntent overload of startActivityAndCollapse on API 34+. "Quick timer" is the quick-timer widget's twin: at rest it shows its duration and a tap starts a transient timer through the timer engine; while that timer is active the tile is on, shows when it ends (or that it is paused or done), and a tap resets it. It works on a locked device, as the timer notification does. Its duration is a new backed-up setting, chosen by long-pressing the tile, with the widget's bounds and picker. Both are active tiles, redrawn by TileSync when the state they mirror changes rather than bound on every panel open. No new permission. Fixes #26
This commit is contained in:
1 parent
3f431a7f32
commit
da9854abf8
28 files changed
+988
-8
No files matched your search
@@ -99,9 +99,10 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `domain/backup/` | `BackupContent` (M10): the plain Kotlin model between `data/backup`'s JSON codec and the repository/engine; decoupled from the wire DTOs the same way the data layer is decoupled from Room |
|
||||
| `domain/selfcheck/` | `SelfCheck` (M10): the "why might my alarm not ring?" rule table: pure functions over a `DeviceState` snapshot, each producing a `CheckResult` with a status and an optional deep-link `SelfCheckFix` |
|
||||
| `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` v3, `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)` |
|
||||
@@ -116,7 +117,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` |
|
||||
@@ -130,6 +131,8 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `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 |
|
||||
| `tile/` | the two Quick Settings tiles (#26): `NextAlarmTileService` and `QuickTimerTileService`, which only render and forward a click, `TileKit` (opening the app, behind an unlock on a locked device, through the `PendingIntent` overload on API 34+), and `TileSync`, `WidgetSync`'s counterpart: both are *active* tiles, redrawn only when this 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 |
|
||||
@@ -599,6 +602,7 @@ Four slices:
|
||||
| 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` |
|
||||
| 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
@@ -44,6 +44,10 @@ written here.
|
||||
24 hours). Room has no clamp of its own, so this is where it happens.
|
||||
- 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
|
||||
@@ -136,7 +140,8 @@ device or after a reboot (`docs/PLAN.md` §5). Every imported timer lands
|
||||
"default_timer_ringtone_uri": null,
|
||||
"default_dismiss_challenge": "NONE",
|
||||
"home_zone_id": null,
|
||||
"timer_presets": [60000, 120000, 180000, 300000, 600000, 900000, 1800000, 3600000]
|
||||
"timer_presets": [60000, 120000, 180000, 300000, 600000, 900000, 1800000, 3600000],
|
||||
"quick_tile_duration_millis": 300000
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
+7
-1
@@ -610,7 +610,13 @@ Deferred deliberately, in rough priority order:
|
||||
|
||||
- **Dock / screensaver mode**: a `DreamService` full-screen clock. Cheapest big
|
||||
win, and the surface people look at most.
|
||||
- **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 `TileSync` on a write rather than bound on every panel
|
||||
open. No new permission.
|
||||
- **Bedtime / sleep schedule**: wind-down and wake, with DND handoff. A feature
|
||||
in its own right, realistically v2.
|
||||
- **`core-notification` in floret-kit**: revisit once Clockula's real
|
||||
|
||||
Reference in new issue
Block a user