feat(bedtime): add an opt-in bedtime mode under Settings
Clockula can now turn on a Do Not Disturb mode of its own at bedtime and off at the wake-up time, on the nights the user picks, with an optional reminder before it starts. Digital Wellbeing's Bedtime mode can't be driven by a third-party app, so this is a separate rule: a typed mode with grayscale, dimmed wallpaper, dark theme and hidden always-on display on Android 15+, and a plain DND schedule rule on Android 10-14. The rule's policy always allows alarms (and media). BedtimeEngine owns one AlarmManager slot of its own, on its own receiver and request code, and every bedtime call on the boot, time-change and launch paths runs after the alarm and timer passes, guarded. The rule id is never stored: the platform lists an app's own rules, so restores and reinstalls find or recreate it. Turning bedtime off removes the rule. The screen walks the user through granting DND access and explains the Wellbeing overlap. Settings are part of the JSON backup; older files restore bedtime as off. Refs #1
This commit is contained in:
1 parent
3f431a7f32
commit
69b436cffd
46 files changed
+2132
-16
No files matched your search
+125
-1
@@ -98,6 +98,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `domain/format/` | `ClockFormat` (`M:SS` / `H:MM:SS`, countdowns rounded up, elapsed truncated), `NextFire` (`NextFireLabel` + `NextFireFormat`), the alarm row's "in 9h 12m" as data, and `StopwatchFormat`, the hundredths readout, split into a major field and two digits so the fraction can be drawn smaller |
|
||||
| `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/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/widget/` | the three widget presenters (M10), `NextAlarmWidgetModel`/`TimerWidgetModel`/`ClockWidgetModel`: selection and formatting only, so a widget's content is unit-tested without Glance |
|
||||
| `data/db/` | `ClockulaDatabase` (`@Database` v3, `exportSchema = true`) and `Migrations` |
|
||||
| `data/alarms/` | the alarm and ring-state entities, DAOs, mappers and repositories |
|
||||
@@ -105,7 +106,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `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)` |
|
||||
| `data/prefs/` | `SettingsPrefs`, `ClockPrefs`, `StopwatchPrefs`, `TimerPrefs`, `UiPrefs` |
|
||||
| `data/prefs/` | `SettingsPrefs`, `ClockPrefs`, `StopwatchPrefs`, `TimerPrefs`, `UiPrefs`, `BedtimePrefs` (issue #1) |
|
||||
| `data/ringtones/` | the two ringtone seams, `RingtoneCatalog` (the device's alarm sounds, titles, playability, a SAF grant) and `RingtonePreviewer`, plus their `System*` implementations, and `MediaLibrary`/`SystemMediaLibrary`, the picker's view of other apps' `MediaBrowserService` libraries |
|
||||
| `data/time/` | `SystemWallClock`, `SystemElapsedRealtimeClock`, `SystemZoneProvider`, `AndroidBootIdProvider`, `RealTicker` |
|
||||
| `data/di/` | `DataModule`, `DatabaseModule`, `RepositoryModule`, `TimeModule`, `RingtoneModule`, `ZoneModule` (M8; one `@Binds`, `ZoneNames → IcuZoneNames`), `BackupModule` (M10) |
|
||||
@@ -127,6 +128,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `stopwatch/service/` | `StopwatchService` (the `specialUse` foreground service) and `StopwatchNotifications` |
|
||||
| `stopwatch/di/` | `StopwatchModule`: `@Binds` for the one seam |
|
||||
| `interop/` | the `AlarmClock` boundary's Android half (M9): `AlarmClockActivity` (the one exported, permission-guarded, window-less door), `IntentExtrasReader` (`Bundle` → `IntentExtras`), `AlarmClockHandler` (the orchestrator; no `android.*` import, ever), `InteropIntents` (the internal transport to `MainActivity`) and `InteropLaunch` |
|
||||
| `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 |
|
||||
@@ -139,6 +141,7 @@ kit module: the kit's roadmap keeps schedulers app-local.
|
||||
| `ui/worldclock/` | the real World clock tab (M8): the pure row builders (`WorldClockRows`, `ZonePickerRows`) and their state, `WorldClockSource`, the ViewModel, the tab, the analog face, the row and the zone picker, `WorldClockDefaults` |
|
||||
| `ui/ring/` | `AlarmRingActivity`, its ViewModel, `RingScreen` and the dismiss-challenge controls |
|
||||
| `ui/settings/` | the settings hub (M10): `SettingsScreen`/`SettingsViewModel` (appearance, alarm/timer/clock defaults, reliability, data, help), `SelfCheckScreen`/`SelfCheckViewModel`, `BackupScreen`/`BackupViewModel`, all reachable from a gear icon on every tab, with no owning tab of their own |
|
||||
| `ui/bedtime/` | `BedtimeScreen`/`BedtimeViewModel` (issue #1), a nested destination reached from the settings hub, the bedtime reminder and the mode's page in system Settings, never a tab |
|
||||
| `ui/widget/` | the three widgets' `@Composable` Glance content (M10), the only Compose this milestone adds outside a tab; the `GlanceAppWidget`/`GlanceAppWidgetReceiver` classes themselves live in `widget/`, since they are manifest components, not UI. `ui/widget/config/` holds each widget's config activity; every widget has one, if only for its per-instance colours (`domain/widget/WidgetColors.kt`: System/Light/Dark and, on API 31+, wallpaper colours on/off). Glance content takes those through `WidgetTheme`; the plain-`RemoteViews` faces, readouts and chronometers through `WidgetPalette.kt`, which resolves the `widget_*` palette under a forced day/night (the app's own tones are `widget_app_*`) and sets concrete colours, or day/night pairs, on the views. The analog and digital clocks are re-coloured on API 31+ only, since below that `AnalogClock`'s hands and a shape's background tint can't be set through `RemoteViews` |
|
||||
|
||||
---
|
||||
@@ -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` |
|
||||
| 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` | `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) |
|
||||
|
||||
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.
|
||||
@@ -668,6 +672,7 @@ Hilt, `SingletonComponent` throughout. Nine app modules plus the kit's:
|
||||
| `ZoneModule` | `@Binds` for the one ICU seam: `ZoneNames → IcuZoneNames` (M8). `ZoneDirectory` needs no binding; it is a `@Singleton` `@Inject` class |
|
||||
| `StopwatchModule` | `@Binds` for the stopwatch engine's one seam: `StopwatchServiceHandle` (the foreground service). It has no scheduler, because nothing the stopwatch does has a deadline |
|
||||
| `TimerModule` | `@Binds` for the timer engine's three seams: `TimerScheduler` (the one elapsed-realtime slot), `TimerServiceHandle` (the foreground service), `AlarmRingStatus` (read-only: "is an alarm ringing") |
|
||||
| `BedtimeModule` | `@Binds` for the bedtime engine's three seams (issue #1): `BedtimeScheduler`, `BedtimeMode`, `BedtimeNotifier` |
|
||||
|
||||
From floret-kit: `core-di`'s `CoroutinesModule` supplies `@IoDispatcher` and the
|
||||
process-lifetime `@ApplicationScope` the receivers launch on, and `core-prefs`
|
||||
@@ -968,6 +973,7 @@ declared because a specific path would otherwise fail, silently, at 07:00.
|
||||
| `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_SYSTEM_EXEMPTED` | the two ringing services: the alarm's, and (since M6) the timers' |
|
||||
| `WAKE_LOCK` | keep the CPU up for the length of a ring |
|
||||
| `VIBRATE` | an alarm and a timer both vibrate, and vibrate alone when no audio source opens |
|
||||
| `ACCESS_NOTIFICATION_POLICY` | issue #1's opt-in bedtime mode. Install-time, and on its own it does nothing: it lists Clockula under Special app access › Do Not Disturb, which the user grants by hand. With that grant Clockula adds one Do Not Disturb rule of its own, which always lets alarms through (§18) |
|
||||
|
||||
**M6 adds no permission at all.** A timer never takes over the screen (no
|
||||
full-screen intent, no ring activity, no `showWhenLocked`), because its user set
|
||||
@@ -2686,3 +2692,121 @@ app has shipped nine milestones with no toast anywhere, and formatting "Alarm se
|
||||
for 7:00" would need a second, non-composable 12/24-hour formatter, the thing
|
||||
M8 spent a milestone eliminating. The platform's own receipt is better than a
|
||||
toast and costs nothing.
|
||||
|
||||
---
|
||||
|
||||
## 18. Bedtime
|
||||
|
||||
Issue #1: an opt-in Do Not Disturb mode, after Google Clock's Bedtime tab,
|
||||
without a fifth tab. v1's four surfaces stay as they are; bedtime is a nested
|
||||
destination (`settings/bedtime`) reached from a row on the settings hub, from
|
||||
its own reminder notification, and from the mode's page in the system's
|
||||
Settings.
|
||||
|
||||
### Why a mode of Clockula's own
|
||||
|
||||
Digital Wellbeing's Bedtime mode is off limits. `NotificationManagerService`
|
||||
refuses an `AutomaticZenRule` of `TYPE_BEDTIME` from any package but the one in
|
||||
`config_systemWellbeing`, and no third-party app can trigger Wellbeing's mode or
|
||||
read its schedule. What Android does offer an app with Do Not Disturb access
|
||||
(`ACCESS_NOTIFICATION_POLICY`, granted by hand under Special app access) is an
|
||||
automatic zen rule of its own:
|
||||
|
||||
- **Android 15 and later**: a *mode*, built with `AutomaticZenRule.Builder`,
|
||||
typed `TYPE_SCHEDULE_TIME`, shown in Settings › Modes under Clockula's name and
|
||||
the `ic_bedtime` icon, with a trigger line ("23:00 to 07:00") and the public
|
||||
`ZenDeviceEffects` the screen offers: grayscale, dimmed wallpaper, dark theme
|
||||
and hiding the always-on display. The hidden effects (auto-brightness,
|
||||
tap/tilt to wake, doze, radios) are dropped by `ZenModeHelper` when an app
|
||||
sets them, so the screen does not offer them. Manual invocation is allowed,
|
||||
so the user can also switch the mode on from Settings or Quick Settings.
|
||||
- **Android 10 to 14** (minSdk 29): a plain Do Not Disturb schedule rule from
|
||||
the seven-argument constructor that takes a `ZenPolicy`. No type, no effects:
|
||||
the screen says so and hides the effect switches.
|
||||
|
||||
A rule needs an owner `ConditionProviderService` or a configuration activity.
|
||||
Clockula uses `MainActivity` as the configuration activity: it is already
|
||||
exported, so no new component is exposed, and when Settings sends it
|
||||
`NotificationManager.ACTION_AUTOMATIC_ZEN_RULE` the shell opens the bedtime
|
||||
screen (`ShellRequest.OpenBedtime`). There is no condition provider service:
|
||||
Clockula reports the rule's state itself with `setAutomaticZenRuleState`.
|
||||
|
||||
The two modes coexist. A Pixel user who also uses Wellbeing's Bedtime sees two
|
||||
modes in Settings › Modes; the screen says so and suggests using one.
|
||||
|
||||
### Alarms are never suppressed
|
||||
|
||||
The rule's `ZenPolicy` sets exactly two things, `allowAlarms(true)` and
|
||||
`allowMedia(true)` (a sleep sound keeps playing), and leaves every other field
|
||||
unset, so calls, messages and the rest follow the device's own Do Not Disturb
|
||||
defaults. Clockula's alarms and timers play with `USAGE_ALARM`, so the
|
||||
exemption applies. A user who later edits the mode in Settings and blocks alarms
|
||||
there has made that choice in the system's own UI; Android 15 keeps a user's
|
||||
edits over an app's updates, and Clockula does not fight them. The self-check
|
||||
(M10's `SelfCheck`) still reports total silence as a problem whatever caused it.
|
||||
|
||||
### The engine
|
||||
|
||||
`BedtimeEngine` is plain Kotlin behind three seams, JVM-tested like the other
|
||||
engines (`ArchitectureRulesTest` holds it to no `android.*` import):
|
||||
|
||||
- `BedtimeScheduler`, **one** AlarmManager slot of its own. Its PendingIntent
|
||||
targets `BedtimeReceiver` with its own action and request code, so arming or
|
||||
cancelling it cannot replace or cancel an alarm's or a timer's registration.
|
||||
It is `RTC_WAKEUP`, exact while idle when the platform allows it and
|
||||
`setAndAllowWhileIdle` otherwise, the timer slot's two branches. It never
|
||||
uses `setAlarmClock`: that slot is the status bar's next *alarm*. Alarms
|
||||
themselves use `setAlarmClock`, which the while-idle quota does not touch,
|
||||
and bedtime adds at most three fires a night.
|
||||
- `BedtimeMode`, the rule. Every call is guarded and a no-op without access.
|
||||
- `BedtimeNotifier`, the reminder, on its own `bedtime_reminder` channel.
|
||||
|
||||
Every entry point is an idempotent pass over "where does the schedule stand
|
||||
now", computed by `BedtimeSchedule`: a night's window runs from the bedtime on
|
||||
one of the chosen days to the first wake time after it (an earlier wake time is
|
||||
the next morning, an equal one a full day), resolved from local times and the
|
||||
zone like an alarm, so a DST night is as long as the wall clock says. The slot
|
||||
is armed for the earliest of the current window's end, the next window's
|
||||
reminder and the next window's start.
|
||||
|
||||
| Entry point | Called from | What it does |
|
||||
|---|---|---|
|
||||
| `update(transform)` | the bedtime screen | stores the settings, rewrites the rule from them, sets the mode to what the schedule says now (on at 23:30 for a 23:00 bedtime starts it at once), arms the slot. Off removes the rule, which ends the mode, cancels the slot and clears the reminder |
|
||||
| `onSlotFired()` | `BedtimeReceiver` | a reminder fire posts the reminder and **leaves the mode alone**; any other fire sets the mode on or off. Then arms the next event |
|
||||
| `resync(refreshRule)` | `SystemEventReceiver` (boot, app update, clock or zone change, exact-alarm permission), Do Not Disturb access changing, a backup restore | creates the rule if it is missing, sets the mode, arms the slot |
|
||||
| `rearm()` | `ClockulaApp.onCreate` | arms the slot only. A force stop clears it, but a launch is not a bedtime event, so a mode the user switched by hand stays as it is |
|
||||
|
||||
Every bedtime call on a shared path runs **after** the alarm, timer and
|
||||
stopwatch passes and inside `runCatching`, so nothing bedtime does can delay
|
||||
or break them.
|
||||
|
||||
### No stored rule id
|
||||
|
||||
`NotificationManager.getAutomaticZenRules()` returns only the calling app's
|
||||
rules, and the rule's condition id (`condition://<package>/bedtime`) marks
|
||||
Clockula's. So nothing about the rule is stored: a reinstall, a cleared app or
|
||||
a restore onto another device finds the rule or creates it, and a stray second
|
||||
one is removed. An update keeps the rule's own enabled switch, so a change on
|
||||
Clockula's screen does not re-enable a rule the user disabled in Settings.
|
||||
|
||||
### Access, granted and revoked
|
||||
|
||||
Do Not Disturb access cannot be requested with a dialog. The screen shows an
|
||||
onboarding row while it is missing, deep-linking to
|
||||
`Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS`, and re-reads access on
|
||||
every `ON_RESUME`; a change found there runs `resync(refreshRule = true)` at
|
||||
once, so coming back from granting it creates the mode without waiting for the
|
||||
system's `ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED` (which
|
||||
`BedtimeReceiver` also answers). Without access the schedule still runs for the
|
||||
reminder. Revoking access in Settings removes the app's rules with it (that is
|
||||
Settings' own confirmation dialog), and Clockula cannot remove a rule without
|
||||
access, so a rule left by a revoke from outside Settings is the platform's.
|
||||
Turning bedtime off removes the rule cleanly.
|
||||
|
||||
### Backup
|
||||
|
||||
The settings travel in the backup's `settings.bedtime` object
|
||||
(`docs/BACKUP.md`); a file from before issue #1 restores bedtime as off.
|
||||
`BackupEngine.import` resyncs the engine after the replace, or after rolling
|
||||
it back.
|
||||
|
||||
+21
-1
@@ -136,10 +136,30 @@ 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],
|
||||
"bedtime": {
|
||||
"enabled": false,
|
||||
"bedtime_minutes": 1350,
|
||||
"wake_minutes": 420,
|
||||
"days": 127,
|
||||
"reminder_minutes": 0,
|
||||
"grayscale": true,
|
||||
"dim_wallpaper": true,
|
||||
"dark_theme": true,
|
||||
"hide_always_on_display": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`bedtime` is the opt-in bedtime schedule (issue #1). Its times are minutes
|
||||
since local midnight, `days` is the same 7-bit mask as an alarm's
|
||||
`repeat_days` (the nights bedtime starts on), and `reminder_minutes` 0 means no
|
||||
reminder. A file without `bedtime` (any file written before it existed)
|
||||
restores bedtime as off with the default schedule. Like the other settings it
|
||||
is clamped rather than rejected: times into a day, the reminder into 0–120
|
||||
minutes. The id of the Do Not Disturb rule a device created is not part of it;
|
||||
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
|
||||
|
||||
+5
-3
@@ -162,9 +162,11 @@ notification whose content intent is the ring activity.
|
||||
A "**Why might my alarm not ring?**" screen that reports actual device state, not
|
||||
generic advice: exact-alarm permission, notification permission, full-screen-intent
|
||||
permission, battery-optimisation exemption, **whether DND currently blocks alarms**
|
||||
(read via `NotificationManager.getCurrentInterruptionFilter()`; Clockula never
|
||||
requests `ACCESS_NOTIFICATION_POLICY` or changes DND itself, it only reports what the
|
||||
user already has), whether the OEM has Clockula under an aggressive app-killer, and
|
||||
(read via `NotificationManager.getCurrentInterruptionFilter()`; the self-check never
|
||||
changes DND itself, it only reports what the user already has. The one place Clockula
|
||||
touches DND is the opt-in bedtime mode added after v1, which needs the user to grant
|
||||
`ACCESS_NOTIFICATION_POLICY` by hand and always lets alarms through; see
|
||||
`ARCHITECTURE.md` §18), whether the OEM has Clockula under an aggressive app-killer, and
|
||||
the next scheduled fire time as the system sees it (`getNextAlarmClock()`), compared
|
||||
against the next fire Clockula itself computed. Where possible each row deep-links to
|
||||
the exact settings page. This screen is a v1 feature, not a support afterthought.
|
||||
|
||||
+6
-2
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Privacy Policy — Clockula
|
||||
description: What Clockula does with your data. It collects nothing, sends nothing, and has no internet permission at all.
|
||||
updated: 2026-10-02
|
||||
updated: 2026-10-22
|
||||
---
|
||||
|
||||
<!--
|
||||
@@ -20,7 +20,7 @@ updated: 2026-10-02
|
||||
render on the published page.
|
||||
-->
|
||||
|
||||
**Last updated:** 2 October 2026
|
||||
**Last updated:** 22 October 2026
|
||||
Applies to the Android app **Clockula** (package `de.jeanlucmakiola.clockula`),
|
||||
all versions and all distribution channels.
|
||||
|
||||
@@ -167,6 +167,10 @@ on. Clockula only opens the address and sends no data of yours with it.
|
||||
stopwatch going while the app is in the background.
|
||||
- `WAKE_LOCK`: keep the phone awake while an alarm or timer rings.
|
||||
- `VIBRATE`: vibrate when an alarm or timer rings.
|
||||
- `ACCESS_NOTIFICATION_POLICY`: only used if you turn on Bedtime and allow
|
||||
Clockula Do Not Disturb access yourself. Clockula then adds one Do Not
|
||||
Disturb mode of its own and turns it on and off on your schedule. It does not
|
||||
read your notifications.
|
||||
- `ACCESS_NETWORK_STATE`: added by the Android library that updates the
|
||||
home-screen widgets (WorkManager). It lets an app see whether a network
|
||||
connection exists. It does not let it use one, and without `INTERNET` no
|
||||
|
||||
+9
-2
@@ -611,8 +611,15 @@ 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.
|
||||
- **Bedtime / sleep schedule**: wind-down and wake, with DND handoff. A feature
|
||||
in its own right, realistically v2.
|
||||
- 🚧 **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
|
||||
wallpaper, dark theme and hidden always-on display; a plain DND rule on 10–14),
|
||||
bedtime and wake-up times, nights, an optional reminder, and backup. Digital
|
||||
Wellbeing's Bedtime mode stays out of reach (the platform refuses
|
||||
`TYPE_BEDTIME` to third parties). Still open: a wind-down phase before
|
||||
bedtime, and whether the wake-up should tie into an alarm. See
|
||||
`ARCHITECTURE.md` §18.
|
||||
- **`core-notification` in floret-kit**: revisit once Clockula's real
|
||||
notification surface is known (`PLAN.md` §3).
|
||||
- **Migrate Calendula and Agendula onto `core-prefs` + `core-di`**: tracked in
|
||||
|
||||
Reference in new issue
Block a user