Preview tools¶
- Status: Implemented
- Date: 2026-09-28
- Branch:
feat/clock-ui-feedback
Goal¶
Provide a few extra controls on top of the clock to trigger transitions and effects on demand, without waiting for the real minute, the schedule, or the random effect timer. They serve three uses:
- Tuning: judging transitions, effects, levels, and sounds by eye and ear.
- Demonstrating: showing the clock to another person in the room.
- Recording: making a short video to share with a prospective user who has the same phone.
Shipping decision¶
Preview tools ship in every build, off by default, rather than only in debug builds. Nothing in them is sensitive:
- they shift the displayed time temporarily;
- they trigger the same effects and sounds the clock already makes;
- they relax one mute rule in memory.
They touch no network, credentials, files, or other apps. Shipping them in every build means the app being tested is the app being run, and it avoids build-variant stubs.
Anything purely for development that is added later, such as frame-timing readouts or verbose logging, stays in debug builds.
Turning it on and off¶
- A long press on the tap overlay's status line toggles Preview tools for the current session. A short toast confirms "Preview tools on" or "Preview tools off". There is no visible hint, so an ordinary tap never enables it.
- Preview tools turn themselves off after 15 minutes without a preview button press, and whenever the app restarts. The on state is never saved.
- While Preview tools are on, the preview panel appears at the top of the screen whenever the tap overlay is shown, and hides with it. It is never permanently visible, which avoids burn-in on the bedside phone. Any preview button restarts the overlay's auto-hide timer.
Showcase¶
Showcase makes recording a video easy and repeatable. It hides the overlay and the preview panel, waits 2 s, and then plays in order:
- a minute change in the current theme;
- each Nixie effect once, at medium strength and a few seconds apart;
- a theme crossfade to the other face;
- a minute and an hour change in that face;
- a crossfade back.
It ends by returning to real time and re-showing the controls. Any tap cancels it and restores real time. Every step uses the same production paths as the individual buttons, so sounds play if they are enabled and not muted.
Starting Showcase from a computer¶
Over USB debugging, Showcase can also start from a launch extra, so a screen recording and Showcase can begin together without a hand in the shot or a tap in the recording:
- No setup needed: the extra does not require Preview tools to be on, because running the command is already deliberate.
- Optional Demo level: adding
--ez demoLevel truealso enables Demo level for the run. - Single instance:
MainActivitybecomeslaunchMode="singleTask"and handles the extra in bothonCreateandonNewIntent. A running clock starts Showcase in place instead of stacking a second clock. - Played once: the extra is consumed after it is read, so recreating the Activity, for example after a configuration change, does not replay it.
- Any app could send it: the launcher Activity is exported, so any app on the phone could send the extra. The worst case is a 30-second harmless demo, so no protection is added.
Recording notes¶
- A camera captures audio; screen recording does not. Filming the phone
with another camera captures both the panel's real brightness and the
sound.
adb shell screenrecordon Android 5.1 captures video without audio. - Screen recordings look darker than the phone.
screenrecordcaptures the drawn pixel values, not the panel brightness, so night content levels look very dark in it. Demo level makes such recordings, and camera recordings, readable. - Night recordings. Sounds are muted from midnight to 6 AM unless Quiet hours is ignored for the recording.
Unobstructed viewing¶
Together, the bottom overlay and the top preview panel cover much of the clock, so they would hide the very transition being judged. Every button that triggers something visible therefore works in three steps:
- Hide: both the overlay and the preview panel disappear immediately.
- Pre-roll: after 300 ms, time for your eye to move from the buttons to the clock, the action fires.
- Re-show: when the action has finished, followed by a 600 ms settle, the overlay and panel reappear with a fresh auto-hide timer, ready for the next press.
"Finished" is reported by the clock rather than guessed:
- Effect: the Nixie view reports the effect's real end, including any postponement around a digit switch and the audio lead.
- +1 min, +1 h, and Now: the active face reports when its digit switch or flip has finished, and a theme crossfade reports when its 1.5 s fade ends. The panel waits for all of the transitions that were triggered.
- Safety net: if nothing reports within 5 s, for example when effects are off and the action changes nothing visible, the panel re-shows anyway.
A tap on the clock during the hidden period behaves as usual and brings the overlay back early. Strength and Quiet hours change settings only, so they leave the panel in place.
| Control | Action |
|---|---|
| +1 min | Shows the next minute immediately, which plays the Nixie digit switch or the split-flap flip and its sound. |
| +1 h | Advances an hour, which exercises the hour card, 12→1 and AM/PM, and any theme or quiet-hours boundary crossed (with the crossfade). |
| Now | Clears the preview offset and returns to the real time at once. |
| Effect | Fires the next Nixie effect in the rotation buzz → brownout → stutter → hum, with its sound. It is disabled in the split-flap theme. |
| Strength | Cycles the next effect's intensity: subtle (0.2), medium (0.6), strong (1.0). |
| Showcase | Plays a hands-free sequence of about 30 s with every control hidden (see below). |
| Demo level | Temporarily raises the content level and window brightness to the daytime split-flap levels, so the face records well on camera. It switches itself off with Preview tools. |
| Quiet hours | Ignores quiet hours for sound so sounds can be heard during night testing. It is in memory only and switches itself off after 10 minutes or when Preview tools turn off. |
A one-line status under the buttons shows the real and displayed time, the preview offset and when it expires, the active theme, and why sound is muted if it is. Examples:
Real 14:37 · Shown 14:39 (+2 min until 14:39) · Nixie · Sound: onSound muted: ringer
Implementation notes¶
- Re-show detection polls the clock every 100 ms:
ClockFace.isAnimating()for both faces (a digit switch, an active effect, a planned effect due within 1 s, or a flip) plus the theme crossfade, after a 500 ms grace, requiring 600 ms of idle, with the 5 s cap. This replaced the planned callbacks and needs no extra listener plumbing. - The preview panel and overlay together exceeded the 360 dp screen height and hid the long-press target, so while the panel shows the overlay shrinks to 48 dp buttons and 16 sp text, and the panel uses 48 dp buttons; the normal overlay keeps 64 dp buttons.
- Showcase holds the current face and pins the time preview without saving a theme override; the real minute can still advance during a run because the pinned offset is relative to real time. Started from the button it re-shows the controls at the end; started from the launch extra it leaves them hidden so a recording ends on the clock.
- Overrides expire only against the real time. A preview (+1 h, or Showcase's +1 h) can pass a saved override's end, and the display follows the previewed schedule. The saved override is kept, and after the preview or Showcase the clock returns to the face the user chose. An earlier build deleted the override in that case; a resolver test documents the scenario.
- Quiet hours for sound use the app's clock in the device time zone, so a time preview also previews quiet hours.
- Verified on the XT1058: the panel layout, +1 min hide/re-show with the shifted status, an Effect with its sound (audio_flinger writes), and a launch-extra Showcase recorded with screenrecord showing the minute and hour advances, both crossfades, and the return to real time.
Time preview¶
The displayed time is the real time plus a preview offset. The offset starts at zero, and each +1 min or +1 h adds to it.
The offset does not last. It clears automatically at the first real minute boundary at least 20 seconds after the last button press, or immediately with Now, and it is never saved, so a restart always shows real time.
- +1 min, then wait: at the next real minute the real time catches up with the preview, so the display does not change and nothing jumps.
- Several presses ahead: when the offset clears, the display returns to real time with one ordinary transition. The split-flap cards do not queue intermediate values; each card flips once to the new time.
- The 20-second minimum keeps a press just before a minute boundary from being undone almost immediately.
The offset drives everything derived from the app's clock: the digits, theme resolution and crossfades, quiet hours and display levels, burn-in drift, and the sound mute policy. The only exception is manual theme overrides, which still record the real time, so a preview can never leave a stored override dated in the future.
Effect trigger¶
Effect does not draw effects directly. It asks the Nixie view to plan the chosen effect 400 ms from now, replacing any effect that is already planned. From there it follows exactly the production path:
- the planned-effect callback synthesizes the matching sound;
- the effect starts after the audio lead;
- every frame passes through
LuminanceGuard; - the effect is postponed if it would overlap a digit switch.
Because of this, pressing Effect repeatedly cannot exceed the flash limit, and what you hear is what the scheduled effects will sound like. The effects and sounds toggles still apply. When a toggle blocks the result, the status line says so; Preview tools do not override the user settings.
Architecture¶
clock/AdjustableClock(pure Java): ajava.time.Clockover a base clock with a preview offset and an expiry rule. Normal use runs it with a zero offset. It is fully unit tested.preview/PreviewSession(pure Java): whether Preview tools are on, with the 15-minute inactivity timeout and the 10-minute quiet-hours override timeout, driven by an injected clock. It is unit tested.preview/PreviewHost: a small interface thatMainActivityimplements:- advance and reset the preview;
- trigger an effect with a type and intensity;
- ignore quiet hours;
- report status.
preview/PreviewPanel: builds the panel from its layout and callsPreviewHost. Its strings are ordinary resources in English and Colombian Spanish, with content descriptions, like the rest of the UI.- Hooks in existing classes (small, production-safe):
NixieClockView.scheduleEffectSoon(type, intensity, targetCell);- an
onEffectFinishedcallback on the existing effect listener, and a smallClockFaceidle notification (switch or flip finished) with the crossfade end, for the re-show step; ClockSounds.muteReason()for the status line;- an in-memory
ClockSounds.setIgnoreQuietHours(boolean); MainActivityusesAdjustableClock, ticks immediately when the offset changes, and turns Preview tools on or off from a long press on the status line.
Plan¶
- Time and session rules (subagent, pure Java), with tests:
AdjustableClock:- offset arithmetic;
- expiry at the first real minute boundary at least 20 s after the last change;
reset;- zone handling;
- behavior across a DST change.
PreviewSession: enabling and disabling, the inactivity timeout, and the quiet-hours override timeout.- Hooks (me):
MainActivityadoptsAdjustableClock, and overrides record real time;- the long-press toggle;
PreviewHost;scheduleEffectSoonandonEffectFinished;- face idle notifications and the crossfade end;
ClockSounds.muteReason()andsetIgnoreQuietHours.- Preview panel (subagent for the layout and English and Spanish strings; me for the panel logic):
- the panel and its overlay integration;
- the hide, pre-roll, and re-show sequence, with its 5 s safety net;
- a status line that updates on every tick and after each press;
- Demo level, which temporarily overrides
DisplayPolicylevels. - Showcase (subagent for the pure step sequence and its tests; me for
running it on the main thread with cancellation on tap, the
showcaseanddemoLevellaunch extras, andsingleTaskwithonNewIntent). - Validation:
spotlessCheck testDebugUnitTest lintDebug assembleDebug;- a device run through every button in both themes, in English and Spanish;
- the timeouts, using the preview itself to shorten waits where possible;
- a Showcase run started with the
showcaselaunch extra and recorded withscreenrecord, to check that it reads well on video. - Docs (subagent):
- the README Android section: turning Preview tools on and using them,
and a recording recipe that starts
screenrecordand Showcase together overadband pulls the video; - this document's status;
- the clock-themes interaction section.
Then you try it on the phone and we address your feedback.
Open questions (defaults chosen)¶
- Should previews also offer −1 min? Default: no; Now covers going back.
- Should Effect let you choose all cells or one cell? Default: it alternates between all cells and a random single cell on successive presses, so both are exercised.