Skip to content

Dashboard: Stop repeating the same values on the charging card - #25

Merged
d4rken merged 1 commit into
mainfrom
worktree-charging-card-dedup
Jul 26, 2026
Merged

Dashboard: Stop repeating the same values on the charging card#25
d4rken merged 1 commit into
mainfrom
worktree-charging-card-dedup

Conversation

@d4rken

@d4rken d4rken commented Jul 25, 2026

Copy link
Copy Markdown
Member

What changed

The charging card on the dashboard kept saying the same thing over and over. On a phone parked at its charge limit it managed to state "80%" six separate times in one card, and the temperature three times.

  • The range row is gone. How long you have been charging now sits in the card's title row next to the arrow, where it costs no extra height.
  • The charge range moved into the graph's key, and it now describes the whole session. The card only keeps recent history for the graph, so on a long charge the old range quietly narrowed from something like "40→80%" to "79→80%" while it was still the same charge.
  • The compact graph no longer prints values at the ends of its lines. The line just above already shows the current level, power and temperature, and those end values trailed slightly behind it.
  • The graph no longer draws when nothing is moving. A phone holding at its limit produced flat lines that looked like a real trend but actually meant "there is nothing to plot".
  • Play Store screenshots are regenerated to match.

Technical Context

Why the range is stated by the host, not the chart. The dashboard's live curve is a bounded recent window (300 samples decimated to 60), so its own span is not the session's. StatsCurveChart.percentRangeLabel therefore defaults to the plotted span but accepts an explicit null meaning "no range" — without that distinction, one legend entry would mean two different things depending on whether the host had data. The hub teaser passes null because its headline already carries the range; the dashboard card passes the session range.

Why headerStatus is a String, not a slot. The header sits inside the card's own click target, so interactive content there would have to opt out of the surface tap; typing it as text makes that impossible rather than merely discouraged. AmplyCardHeader floats trailing content and reports only the title's height, which is why this costs nothing vertically. It gained a reserved minimum title width, and the status is weighted so the chevron is measured first — StatsFormat.duration never rolls over into days, so 123h 59m is reachable and would otherwise starve the title or the arrow.

Why the curve gate needs adjacent variation. LineChart breaks its path at nulls, so [40, null, 41] is two one-point segments that draw nothing — "two distinct values somewhere" would have passed. And flat series matter because a zero-range series self-normalizes to the canvas midpoint: several of them stack into a convincing-looking trend that is really the "no range" fallback.

Accessibility. Compact charts stop describing their endpoints too, not just hiding the labels — otherwise the same stale-by-one-tick contradiction just moves into the screen reader. The legend is real text and is already read aloud.

The shared clock. Both live surfaces now use one rememberLiveElapsedMillis. Its first tick aligns to the next whole session-minute, because a fixed minute-long delay starting at 2m59s would hold the curve back until 3m59s. It also reads the clock on composition instead of trusting the caller's captured value, so a card scrolling back into the lazy list does not re-enter holding a stale timestamp.

Screenshot fixtures. DashboardScreen gained an injectable clock so a fixture can render a live session at a believable age instead of "0m". The fixture curve was also made non-linear — three linear series self-normalize to identical shapes and drew exactly on top of each other.

Review guidance

The states with no chart — the first three minutes of a session, a flat limit hold, null-gapped data — deliberately show no range at all. That is the accepted cost of reclaiming the row, and there is a test pinning it rather than leaving it implicit.

Draft: unit tests, lint and both assemblies pass, but this has not had a device pass yet.

The card stated the same numbers up to six times: the level appeared in the
reading line, twice in a "40% -> 78%" row, as the curve's end label and twice
more in the chart legend. On a device held at its limit every span collapsed
to the same figure, so the card read as an echo of itself.

The middle row is gone. Elapsed time moves into the card header beside the
chevron via a new typed headerStatus on AmplyNavigationCard - the header
floats its trailing content and reports only the title's height, so this
costs no vertical space. It is a String rather than a composable slot on
purpose: the header sits inside the card's own tap target, so interactive
content there would have to opt out of the surface tap. A reserved minimum
title width plus a weighted status keep long values (duration never rolls
over into days, so "123h 59m" is reachable) from starving either the title
or the chevron.

The level range survives in the chart legend, stated as the session's range
rather than the plotted curve's span. The live curve is a bounded recent
window, so its own span quietly narrows from "40->80%" to "79->80%" while
the charge is still the same charge. percentRangeLabel defaults to the
curve's span, so an explicit null means "no range" instead of falling back
to the window - which is how the hub teaser, whose headline already carries
the range, keeps from stating it twice.

The compact chart drops its end labels, and no longer describes those
endpoints to screen readers either: its hosts render the current values one
line above, and the labels come from the last recorded sample, which lags by
a recorder tick. Dropping them also frees the right gutter for the curve.

It also refuses to draw without real variation. A device at its limit
produces flat series, and a zero-range series self-normalizes to the canvas
midpoint, so flat lines stacked into what looked like a plotted trend but
was really the "no range" fallback. Variation must come from an adjacent
non-null pair, because the path breaks at nulls: [40, null, 41] is two
one-point segments and draws nothing.

Both live surfaces now share one elapsed clock. Its first tick is aligned to
the next whole session-minute (a fixed minute delay starting at 2m59s would
hold the curve back to 3m59s), and it reads the clock on composition rather
than trusting a caller's captured snapshot, so a card scrolling back into a
lazy list doesn't re-enter holding a stale time.

Store screenshots are regenerated. Their fixture now pins "now" so a live
session has a believable age, and carries a real CC/CV curve - three linear
series self-normalize to identical shapes and drew on top of each other.
@d4rken d4rken added enhancement New feature or request Google Play Google Play flavor / distribution labels Jul 25, 2026
@github-actions github-actions Bot added Build/Deploy Build system / CI / release tooling and removed Google Play Google Play flavor / distribution labels Jul 25, 2026
@d4rken
d4rken marked this pull request as ready for review July 26, 2026 07:45
@d4rken
d4rken merged commit d00b9ef into main Jul 26, 2026
12 checks passed
@d4rken
d4rken deleted the worktree-charging-card-dedup branch July 26, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build/Deploy Build system / CI / release tooling enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant