Commit Graph
16 Commits
Author SHA1 Message Date
makiolaj 19ee678a1b feat(widgets): let each widget pick its theme and wallpaper colours
Every widget instance can now follow the system or be pinned to Light or
Dark, and on Android 12+ turn the wallpaper's colours off in favour of
the app's own palette. Both are set in the widget's config screen and
stored in its own Glance state; an instance without them keeps today's
look.

Glance content takes them through WidgetTheme. The plain-RemoteViews
parts (the analog and digital faces, readouts and chronometers) go
through a new WidgetPalette, which resolves the widget_* palette under a
forced day/night and sets concrete colours, or day/night pairs, on the
views. The app's own tones are now widget_app_*, so they stay reachable
on API 31+. The analog and digital clocks are re-coloured on API 31+
only: below that AnalogClock's hands and a shape's background tint can't
be set through RemoteViews, so their config screens don't offer it.

The next-alarm and timer widgets gain a config screen for this
(configuration_optional, so placing one doesn't stop there). The analog,
stopwatch and quick-timer config screens become settings screens with an
OK, like the digital clock's, since they now hold more than one choice.

Background opacity is left out: most faces are a shape with text in its
container colour rather than a card, so there is no background to fade
without losing contrast against the wallpaper.

Closes #6
2026-10-05 19:48:59 +02:00
makiolaj 462f9f8c9f feat(widgets): open Alarms from the clock widgets, or a tab of your choice
Tapping the digital or analog clock widget now opens Alarms instead of
World clock, and both config screens gain an "On tap" setting (Alarms,
Timers, Stopwatch, World clock) stored per instance next to the style.
Instances placed before the setting existed open Alarms. The analog
config becomes a settings screen like the digital one, with the style
behind a row, to make room for it.

A widget tap sometimes showed whatever the app was last on instead of
its tab, for two reasons. The shell replayed a deep link as a tab tap
with restoreState, so it restored the tab's saved back stack, nested
screen and all (fixed by the pager shell, which pops to the tab). And
the widget intents carried only NEW_TASK, so with a picker or settings
page left on top of MainActivity the task was merely brought forward and
onNewIntent never ran. Every widget that opens the app now builds its
intent in one place with CLEAR_TOP and SINGLE_TOP as well.

Closes #8
2026-10-05 19:42:14 +02:00
makiolaj be526711d7 feat(shell): swipe between tabs
The four tabs are now pages of one HorizontalPager on a single NavHost
route, so a sideways swipe moves between Alarms, Timers, Stopwatch and
World clock as well as tapping the bar. The pager is the source of truth
for the selected tab; editors, the timer setup page and Settings stay
NavHost destinations pushed over it. A tab tap from one of them pops
back to the tabs, and a deep link always lands on its tab rather than on
whatever nested screen was last open there.

Swiping is on only under a navigation bar: beside a rail the tabs are
stacked vertically, and a horizontal swipe has no counterpart there.

Closes #2
2026-10-05 19:42:14 +02:00
makiolaj 45e5a51c98 feat(worldclock): show home time as text under the analog face
The World clock tab now prints the home time under the hero face in the
app's 12/24-hour format, reversing the earlier call to leave it out.
Reading the exact minute off a dial is slow, and that minute is what
most visits to the tab are for.

The face no longer has a content description, so TalkBack does not read
the time twice. The readout, "Home", the city and the zone name are one
merged node instead.

Closes #4
2026-10-05 19:36:31 +02:00
makiolaj cac5b2dfca refactor(settings): remove the unused default timer duration
Settings > Timers offered a "Default duration", but nothing that creates
a timer ever read it: the keypad starts empty. The row, its preference,
ClockDefaults.timerDuration and the backup field are gone, and the
screen's hint and the hub's subtitle now only speak of the sound.

Backups no longer write default_timer_duration_millis. Older files that
carry it still import, since the codec ignores unknown keys.

Closes #5
2026-10-05 19:28:31 +02:00
makiolaj f37c5f2f84 feat(ringtones): let a music app play the alarm
"Choose from a music app…" in the sound picker browses any app that shares
its library through Android's MediaBrowserService and stores the choice as a
clockula://media URI in the existing ringtone field — no schema change, no
new permission, no internet. The music app streams with its own.

At ring time the app is asked to play through its media session. Every wait
is bounded and any failure falls back to the ordinary alarm sound: the app
won't connect or start, stalls or buffers too long, or the media stream
might not be heard (headphones or a classic Bluetooth device connected, Do
Not Disturb muting media). The ramp drives the media stream and the user's
media volume is restored when the last media ring ends.

RingAudioPlayer.start no longer suspends: vibration starts up front, the
timer collector is not held up, and a generation token keeps late work from
reviving a stopped ring.
2026-10-02 20:44:58 +02:00
makiolaj 5d03c654bf docs: mark M10 done
ARCHITECTURE.md's package table gets the new domain/backup,
domain/selfcheck, domain/widget, data/backup, backup/, widget/,
ui/settings/ and ui/widget/ entries, and §9/§10 are brought up to date
with what M10 actually built (the exported-component count, the
permission list unchanged, which 'not built yet' rows are now done).
CHANGELOG and ROADMAP's current-state line follow.
2026-10-01 22:34:07 +02:00
makiolaj 9ab3927efe docs: the system boundary, and what it refuses
`ARCHITECTURE.md` gains §17: the door, the split between the Android-free
validator and the reader that guards the unparcel, the range rule (clamp what
is happening now, drop what would define a future ring), the candidate table
for "which alarm did you mean", and the three deliberate narrowings — including
why a link we cannot read dismisses nothing rather than everything.

§4 records schema v3 and its migration; §13 records the reading of `PLAN.md` §6
this milestone builds, since a malformed intent lands the user in an editor
over a row created from the valid extras, there being no "new alarm" sentinel.
2026-09-23 11:02:20 +02:00
makiolaj 36f6b356d0 docs: the world clock, its seam and its caches
`ARCHITECTURE.md` gains §16 for the tab — where the zone list comes from, why
ICU sits behind a seam, what `ZoneDirectory` caches and on what key, and why
every comparison is read at the instant rather than against a stored offset.
The module, package and rule tables count the new arrivals.
2026-09-22 13:05:24 +02:00
makiolaj cb35cc9c95 docs: the stopwatch, and why its reading is floored and banked
`ARCHITECTURE.md` gains §15 for the stopwatch and records the one correctness
find this slice turned up: a lap taken inside a segment a reboot later
discarded would have dragged the readout, the lap totals and the shade
backwards. The reading is floored at the last lap's total — and the floor is
banked, not merely displayed, or the shade's free-running chronometer walks
away from a tab that is standing still.

The package, module, receiver, service and permission tables all count one
more; §10 loses the two rows M7 closed.
2026-09-22 10:24:08 +02:00
makiolaj 319d176ae8 docs: the timers, and the counts checked against the gate
ARCHITECTURE gains §14 for timers and the ring package's new shape. The test
counts were written before the review's fixes landed; they now match what the
gate actually runs.
2026-09-12 16:49:05 +02:00
makiolajandClaude Opus 5 176626b33a docs: the alarms screen, and the numbers put right
ARCHITECTURE gains §13 for the alarms screen. The test counts in §8 were
written before the review's fixes landed, and M4's instrumentation count was
one short; both now match what the gate actually runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
2026-09-12 14:44:23 +02:00
makiolaj d4120d8e06 docs: the app shell, and what it earned
ARCHITECTURE gains §12 for the shell and loses the two rows M4 paid off; the
permissions row now says what M4 actually asks for and leaves the rest to
M10's self-check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wmy1BpCKi8KeSjaWhYuCPV
2026-09-12 13:07:53 +02:00
makiolajandClaude Opus 5 a522d68098 docs: the alarm engine, and the blind spot it closes
ARCHITECTURE gains a section 11 on the engine itself — the state machine, the
two AlarmManager slots, the DST table, and the chain that keeps a denied
permission from turning into a silent morning. Section 5's known blind spot is
amended rather than deleted: the boot id exists now, and the stopwatch repair
moved from M7 to here.

Section 9's "no permissions are declared yet, deliberately" is finally untrue,
so it is replaced with the real set and why each one is there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 16:05:31 +02:00
makiolajandClaude Opus 5 971ee4f7a3 docs: point the reboot repair at the milestone that actually owns it
The roadmap puts the BOOT_COMPLETED and TIME_SET receivers, the ringing
service and the full-screen intent in M3; ARCHITECTURE.md credited them
to M6 and M7, which are Timers and Stopwatch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 13:53:11 +02:00
makiolajandClaude Opus 5 1e6443c907 docs: the first ARCHITECTURE.md
How Clockula is built today: the layers and the data seam, the package
layout, the four tables column by column, dependency injection, and the
JVM-first testing posture.

The section on the two clocks is the one a future contributor will need
most — which aggregate resolves against which clock and why, the timer's
three anchors, and the reboot window where a dead anchor can still read
as live, stated as it actually behaves rather than as it was first
assumed to. Closing that window needs a persisted boot id; M3's
BOOT_COMPLETED receiver is where it belongs.

Also says plainly what is not built yet, and points each at its milestone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 13:52:33 +02:00