feat(worldclock): add an option to display time with seconds
One switch in Settings > Clock adds seconds to the home readout and every city row, in the same 12/24-hour and locale form as before, and gives the analog face its second hand. Off by default. The tab now ticks on the wall-clock boundary through a new Ticker.alignedTicks: once a minute with seconds off, once a second only with them on, replacing the free-running 200 ms tick. The setting is in backups as world_clock_show_seconds; older files import with it off. Fixes #23
This commit is contained in:
1 parent
3f431a7f32
commit
785adf715b
30 files changed
+455
-65
No files matched your search
+35
-12
@@ -595,7 +595,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`, `world_clock_show_seconds` | `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` |
|
||||
@@ -1423,6 +1423,12 @@ immediately and then every period (one second for the pill, since the pill
|
||||
formats to whole seconds), so a fresh subscriber is never blank. `RealTicker` is
|
||||
nothing but `delay`, which makes it testable under `runTest`'s virtual clock.
|
||||
|
||||
`alignedTicks(unit)` is the same seam for a readout of whole wall-clock units:
|
||||
after the immediate emission it waits `WallBoundaries.untilNext(now, unit)`,
|
||||
re-measured against `WallClock` after every wake, so it fires on the second or
|
||||
minute boundary itself and a long `delay` or a set clock does not accumulate.
|
||||
The world clock is its one caller (§16, "The cadence").
|
||||
|
||||
### The dismiss challenge, and why it cannot strand anyone
|
||||
|
||||
`ChallengeGate` is a pure state machine whose single boolean `open` means
|
||||
@@ -2409,8 +2415,8 @@ ramp to 1 at 07:00, 1 until 17:00, a linear ramp back to 0 at 19:00. It needs no
|
||||
latitude, it has obvious tests, and the app never claims it is sunrise.
|
||||
|
||||
The rest is ordinary `Canvas` work over `AnalogFace`'s pure angles: twelve hour
|
||||
marks with the four quarters longer, three hands, a centre cap, all in scheme
|
||||
tokens. The morph is built once and the path re-derived into a reused
|
||||
marks with the four quarters longer, the hour and minute hands (and a second
|
||||
hand when seconds are on, below), a centre cap, all in scheme tokens. The morph is built once and the path re-derived into a reused
|
||||
`Path` per frame through `toPath(Morph, Float, Path)`, which is not
|
||||
`@Composable` and is therefore callable from a `DrawScope`. M8 left the digital
|
||||
time out from under the face on the grounds that an analog face that also
|
||||
@@ -2426,15 +2432,32 @@ refused for the live pill (§12).
|
||||
|
||||
### The cadence
|
||||
|
||||
`WorldClockDefaults.Tick = 200 ms`. `RealTicker` is a plain `delay` loop with no
|
||||
alignment to the wall second, so a one-second cadence would leave the second
|
||||
hand up to a full second behind the device clock, which is visible and not
|
||||
something a clock app ships. The cost is bounded by making the state **stable**:
|
||||
`HomeFaceState.time` is truncated to the second, `WorldClockRowState.time` to
|
||||
the minute, and the flow is `distinctUntilChanged`. The upstream emits five
|
||||
times a second, a new state is produced about once a second, and the rows' own
|
||||
values change once a minute. The period is a `Ticker` parameter, never a `delay`
|
||||
at a call site (§12).
|
||||
**Display time with seconds** (Settings > Clock, `world_clock_show_seconds`,
|
||||
off by default, #23) decides how often the tab wakes. Off, the face has an hour
|
||||
and a minute hand, the readout under it and every row show hours and minutes,
|
||||
and the tab ticks **once a minute**. On, the readout and the rows add seconds
|
||||
in the same 12/24-hour and locale form (`HH:mm:ss`, or the locale's medium
|
||||
time), the face gains its second hand, and the tab ticks **once a second**.
|
||||
The second hand belongs to the switch rather than being always there: a hand
|
||||
that steps every second is exactly what makes the tab wake every second, and
|
||||
the point of the switch is that nobody pays for that without asking.
|
||||
|
||||
Both cadences are `Ticker.alignedTicks` (§12), `WorldClockDefaults.MinuteTick`
|
||||
or `SecondTick`, so a tick lands on the wall-clock boundary itself rather than
|
||||
up to a period late; a free-running one-second `delay` could leave the second
|
||||
hand a full second behind the device clock. This replaces M8's 200 ms
|
||||
free-running tick, which got within a fifth of a second only by waking five
|
||||
times as often as anything changed. `HomeFaceState.time` and
|
||||
`WorldClockRowState.time` are truncated to the same unit the tab ticks in, and
|
||||
the flow stays `distinctUntilChanged`, which now only absorbs the odd early
|
||||
wake. The setting is read with the stored list and the home zone, so flipping
|
||||
it switches the cadence at once through the same `flatMapLatest`.
|
||||
|
||||
`TIME_SET` and `TIMEZONE_CHANGED` still self-heal within one tick, which with
|
||||
seconds off is now up to a minute. That is only reachable by changing the
|
||||
clock in system settings and returning within the five-second
|
||||
`WhileSubscribed` grace; any longer and the flow restarts and reads the new
|
||||
time at once.
|
||||
|
||||
### Twenty-four cities, and a reorder a screen reader can reach
|
||||
|
||||
|
||||
Reference in new issue
Block a user