ADR-0082: Explicit Activity state and one named settings switch row
Context
Three accessibility defects shipped together in the Warm ledger review (#373, #377, #386):
- A hidden Activity row applied a 0.45 alpha to the whole row. At normal text size the composited identity, metadata and money fell to roughly 1.8–3.6:1 against the row surface, below the 4.5:1 normal-text floor, and opacity was the only hidden-state signal.
- Notify on capture, Record transaction locations, Dynamic color, Biometric
unlock and Device credential fallback put an interactive
Switchinside a settings row that was not itself toggleable. TalkBack therefore met a second, separately focusable switch with no row name, while the row title did nothing. - A collapsed Activity search kept its query active but invisible; the
Uncategorised filter existed only as a route sentinel; the scope chip drew a
dropdown chevron although it toggles; Activity day headers and the unmatched
timestamp hardcoded field order and
HH:mm; the row's custom actions lived on an unfocusable wrapper; and the editor's read-only date field repeated the same date in its tap-target description.
Decision
- Hidden rows stay readable and labeled. A hidden row keeps
surfaceContainerHighestas its background but renders identity, metadata and money with their normalonSurface/onSurfaceVariant/primaryroles, with no whole-row alpha. A localized Hidden pill (the shared status-pill shape and status type role) sits in the supporting line, so state is visible and announced independently of colour. - One named switch row.
SettingsSwitchRowowns the singletoggleable(value, role = Role.Switch)node; its childSwitchis decorative (onCheckedChange = null) and mirrorschecked/enabled. All immediate settings use it, and the row is bound to confirmed persisted state, so a cancelled permission or security flow cannot flip it. - Disclose Activity state. A collapsed active query renders as a removable
chip beside the other active filters. The filter sheet offers Uncategorised
through the existing
UNCATEGORISED_CATEGORY_IDsentinel. The scope control is a constant All history filter chip (unselected = the selected month, selected = the whole ledger) with a swap icon, so its selected state never contradicts a relabeled "Month" chip and it never looks like a dropdown. The row's custom Hide/Delete actions move onto the named clickable row node. - Announce editor structure.
CollapsibleSectionCardexposes a localized Expanded/CollapsedstateDescriptionand an Expand/Collapse click label. The editor's read-only date field keeps the date announcement once; its overlay names the Change date and time action instead of repeating the value. - Locale and device time. Day headers and timestamps resolve their pattern
through
android.text.format.DateFormat.getBestDateTimePatternwith locale-neutral skeletons, so field order, month/weekday names and the device 12/24-hour preference are respected. Formatting stays purejava.timeand is JVM-tested; the Activity month scope still omits the year and all-history still adds it.
Consequences
- Hidden rows meet the 4.5:1 normal-text floor in the static Warm palettes and the device dynamic scheme in both light and dark (instrumented); the former 0.45 composite is asserted to fail so the fade cannot return.
- Every immediate setting has exactly one labeled switch node and a ≥48 dp target; disabled biometrics stay announced but cannot be toggled.
- Active queries, the uncategorised filter and row actions are reachable from the same visible/focusable surface as the list.
- No schema, dependency, money, draft, lock or route-contract change. The debug
gallery gains a Hidden transaction fixture and the
SettingsSwitchRowstates, and the new behaviour is covered by JVM contrast/formatting tests and instrumented semantics/layout tests.
Verification
HiddenRowContrastTest, DateTimeFormattingTest,
SettingsSwitchRowTest, CaptureSettingsTest, AppearanceSectionTest,
SecuritySectionTest, CollapsibleSectionCardTest,
HiddenTransactionRowTest, ActivityQueryAndFilterTest,
ActivityDateFormatTest, TransactionsContentTest and
TransactionDetailScreenTest cover the contracts above at the exact head.