Repository navigation
Android home-screen widget: the HR and battery line has no accessible label #1799
Description
The problem
NoopGlanceWidget — the standard Android home-screen widget — renders live HR and strap
battery as text with an inline glyph character rather than a labelled image:
// android/app/src/main/java/com/noop/widget/NoopGlanceWidget.kt:153-164
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = snap.heartRate?.let { "♥ $it" } ?: "♥ - ", …)
Spacer(modifier = GlanceModifier.width(10.dp))
Text(text = snap.batteryPct?.let { "⚡ $it%" } ?: "⚡ - ", …)
}There is no contentDescription and no semantics anywhere in the file, so TalkBack reads the
raw string. The ♥ and ⚡ are literal characters inside the text, which a screen reader either
announces by their Unicode names or skips entirely — either way the user gets a bare number with
no metric name and no unit. "58" and "84%" arrive as two anonymous figures.
What makes it clearly a defect rather than a gap
The sibling widget in the same package already does it correctly:
NoopCompactGlanceWidget.kt:140— battery drawn asImage(ImageProvider(R.drawable.ic_widget_strap_battery), contentDescription = uiString(R.string.l10n_noop_compact_glance_widget_strap_battery_a6c7f09c))NoopCompactGlanceWidget.kt:177— the generic metric cell passescontentDescription = label
So the compact widget is accessible and the standard one is not. Within NoopGlanceWidget
itself the three score cells are fine — ScoreCell(label = uiString(R.string.l10n_noop_glance_widget_rest_…))
gives Rest, Charge and Effort real localised word labels. It is only the HR/battery row that is
unnamed.
Suggested shape
Either give the row proper semantics/contentDescription, or switch HR and battery to
Image(ImageProvider(…), contentDescription = …) the way the compact widget already does — the
second keeps the two widgets consistent and reuses the existing drawables
(ic_widget_strap_battery, ic_stat_heart).
Whatever is added must be localised through uiString(R.string.…), not a bare literal: the
widget's other copy already is, and #571 recorded that the i18n audit cannot see
contentDescription set inside a semantics {} lambda, so a literal there would pass CI and
ship English to every locale.
Scope notes
Not a twin of the Apple-side change in #1798, which fixes an inverted HR/HRV icon pairing plus
the same class of missing VoiceOver naming in vitalsFooter. That icon bug has no Android
counterpart at all: android/…/widget/WidgetSnapshot.kt carries no hrv or restingHr field,
so there is no HR/HRV pair to invert. Only the accessibility half has an Android analogue, and
it is a different mechanism (inline glyph text vs. an unlabelled SF Symbol).
Separately and already known: "Connected" and "Open NOOP to connect" in this same file are
bare English literals sitting beside localised uiString(…) calls. Both are in
Tools/i18n_audit_baseline.json, so the audit passes by design — worth folding into the same
pass if someone is in here anyway, but not a hidden CI hole.
Activity
widget: let the home-screen HR and battery line say what it is (#1800)
Make the widget say something to TalkBack instead of reading its own …
widget: heart rate with the recent trace (Android) (#1958)
ryanbr commentedon Sep 9, 2026
Landed in
b9b04c814(#1800): the compact widget's heart-rate line now carries acontentDescription, so TalkBack reads a name and a unit instead of a bare number. The battery image beside it already had one.Shipped in testing build 439 onward, and in every build since.