Skip to content

TalkBack, 1 of 10: Add accessible transient message live regions - #6157

Draft
eliotcougar wants to merge 14 commits into
2dust:masterfrom
eliotcougar:fix/accessibility-live-regions
Draft

eliotcougar wants to merge 14 commits into
2dust:masterfrom
eliotcougar:fix/accessibility-live-regions

Conversation

@eliotcougar

@eliotcougar eliotcougar commented Aug 29, 2026 •

Copy link
Copy Markdown
Contributor

Summary

First of ten focused TalkBack PRs: make transient feedback accessible without turning visual Toast/Snackbar messages into interactive accessibility targets.

Foreground messages use the existing Compose Snackbar host and mirror their text to a separate live region by default. Background feedback uses a regular Android notification, not a deprecated custom Toast fallback.

Foreground feedback

  • Keep the visual Snackbar accessibility-inert and without a click action.
  • Mirror all normal, success, error and info messages to a locale-tagged polite live region by default. Callers no longer need to opt in, and cannot accidentally disable the live-region mirror with a null priority.
  • Retain assertive feedback for service-start success, service-stop success, service-start failure and errors preventing a requested start.
  • Ordinary feedback, including a foreground "Starting service" message, uses the polite default.
  • Ignore blank messages and suppress only rapid duplicates with the same text and priority. A priority change is a new update.
  • Retain the short-lived, stable live-region node and the two-frame text-publication sequence; hide it from navigation when idle. No focus requests or handcrafted accessibility announcement events are used.
  • This is a live-region update mechanism, not a speech queue: the accessibility service controls interruptions and coalescing.

Subscription/config and asset update results use localized count plurals. Asset download-start feedback is also mirrored. All related resources cover the nine maintained locales.

Service identity

The daemon supplies the name of the profile whose configuration actually started in the success-event payload. The UI does not re-read the current selection, which may already point at another server.

The visible success Snackbar stays unchanged. Its accessible text adds "Now connected to [server name]". Background success notifications use the same enriched wording. Empty or missing names fall back to the ordinary success message, rather than guessing from the current selection.

Background fallback and channels

  • Send service results to the UI with acknowledgement; if no resumed consumer handles a result, post background feedback.
  • All transient fallback messages share one notification ID. Every new message replaces the previous message; "Starting service" has no special replacement policy.
  • Use an expandable, auto-cancel notification opening the main screen, with a ten-second platform timeout on Android 8/API 26 and later.
  • Respect notification permission and app/channel settings; do not bypass a user's decision to disable notifications.
  • Cancel stale fallback feedback when a new message is successfully delivered in the foreground.
  • Use dedicated localized channel titles, including "Connection checks" and "Other notifications". Rename existing channels when necessary without resetting user-controlled importance, sound or vibration.
  • Keep channel API calls inside a locally checked API-26 guard; no lint suppression is added.

Android 7/API 24-25 automatic expiry is intentionally not implemented in this PR; the notification remains replaceable and dismissible there.

Per-app information and PR boundaries

The per-app information action opens an OK-only modal containing the existing explanation, without a title. There is no remaining per-app info-toast call in this branch.

The identical modal is also present in #6164 so either focused PR can be merged independently. Its callback boundary is aligned to avoid a conflict with that PR's switch changes. No per-app row/toggle semantics are imported here.

The remaining accessibility work is split across TalkBack PRs 2–10. This PR does not implement their list, Settings, navigation or connection-testing semantics, suppress native control roles, or replace genuine toggle behavior.

Foreground lifecycle handoff gaps and broader stop/restart sequencing remain separate follow-up work; this PR does not claim to repair those lifecycle issues.

Validation

  • :app:testPlaystoreDebugUnitTest — 74 tests passed.
  • :app:compilePlaystoreDebugKotlin and :app:assemblePlaystoreDebug — passed.
  • Regression coverage includes default mirroring for every message type, blank/duplicate handling, priority changes, delivery fallback, notification permission/channel decisions, and formatting an immutable started-server name after selection changes.
  • :app:lintPlaystoreDebug — the new NotificationHelper API errors are resolved. Four pre-existing upstream API errors remain in RootManager, RootShell and the two base theme resources.
  • All 45 focused-PR pairs merge cleanly in both orders. Combining all ten PRs forward and backward produces the same tree.
  • The local combined result passes 86 unit tests and builds successfully.
  • Android 11 emulator, combined build: the per-app modal contains only its explanation and OK; Tab/Enter dismisses it. Existing lab subscription data was preserved.
  • git diff --check — passed.

Verified

  • Tested on a real device with TalkBack on.

Android references

@eliotcougar eliotcougar changed the title TalkBack, 1 of N: Add accessible transient message live regions TalkBack, 1 of 10: Add accessible transient message live regions Aug 30, 2026
eliotcougar added a commit to eliotcougar/v2rayNG that referenced this pull request Aug 30, 2026
…essibility

Retain the original button call site and name the existing modal-opening callback locally. This avoids an adjacent-hunk conflict between the independent modal replacement in 2dust#6157 and the switch semantics in this PR, without changing behavior or importing notification code.
@eliotcougar
eliotcougar marked this pull request as ready for review August 30, 2026 22:29
@eliotcougar

eliotcougar commented Aug 30, 2026 •

Copy link
Copy Markdown
Contributor Author

I tested the whole set on my Pixel 9 pro with Android 17. The AI tested it on Android 13 and 15 VMs.
The individual PRs have no intersections and can be merged in any order and combination.

This PR is the largest as it expands the Toast notification system and makes it compatible with accessibility mode. As a result, it touches every activity that can emit a Toast notification. Unlike the previously rejected PR, it doesn't use the deprecated direct announcements or other workarounds. Instead, it relies on a live-region and a regular notification channel fallback, and follows the official Android accessibility guidelines.

Other PRs are focused on individual Activities and are much smaller and easier to review.

@eliotcougar
eliotcougar marked this pull request as draft September 5, 2026 11:11
@eliotcougar

eliotcougar commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor Author

GPT-6 Astra worked the entire day making the accessibility PR set better and review-friendly as much as possible.
The new model seems to be less prone to overengineering and implementing useless protections against situations that don't happen.
Spent 65% of the weekly credit and a full phone battery.
I'm going to test all 10 combined (with 4,069 additions and 677 deletions across 82 files, including regtests) tomorrow after a good night sleep, and fix any remaining UI/UX problems.

There are several small general UI fixes here and there in the accessibility set. I'm not yet sure if I should separate them into tiny separate PRs... They are tiny...

Remove the custom Toast view used when no Compose Snackbar host is active.

Keep foreground feedback on the existing inert Snackbar and live-region path. When no host is available, post a low-importance, auto-canceling notification that replaces the previous transient message and opens the app when tapped. Respect notification permission and global notification settings instead of bypassing the user's choice.

Use localized notification-channel names and add regression coverage for foreground/background routing and notification eligibility.
Acknowledge service results only when an active UI consumer accepts them; otherwise let the daemon post the final success, stop, or failure through the ordinary notification fallback. Avoid replacing a startup failure with cleanup-only stop feedback.

Use one transient notification ID for every fallback message, reset its native ten-second timeout on replacement, and clear stale background feedback when a foreground Snackbar is delivered. Refresh localized channel names in place without changing channel IDs or user-controlled settings.

Validation: 69 Play Store JVM tests and x86_64 debug build passed with JDK 21. API 30 emulator checks covered background success/failure, UI-process-absent start/stop, replacement, expiry, and Russian channel names. TalkBack speech was not rerun. Native timeout is supported on API 26 and newer.
Name the channels Subscription updates, Connection checks, and Other notifications using their own resources in all nine supported locales. Leave the main service title and existing channel identifiers and behavior unchanged, so names refresh in place without resetting user preferences.

Add regression tests for dedicated title-resource mappings and stable channel identifiers. Focused notification tests, the full Play Store JVM suite, and ARM64 debug assembly passed.
Make every foreground message eligible for a polite live-region update by default, retaining assertive service feedback and the inert visual Snackbar. Replace the per-app explanation toast with the existing OK-only modal used by the per-app accessibility PR.

Carry the daemon snapshot of the successfully started profile name in the success event instead of reading a potentially newer UI selection. Keep channel configuration inside its API 26 guard, without lint suppression.

Validation: 74 Play Store debug unit tests, Kotlin compilation and debug assembly pass. The NotificationHelper lint errors are gone; lint still reports four pre-existing API errors in root process waits and base themes. Stop/restart lifecycle changes remain out of scope.
Extract the existing locale-aware live-region renderer without changing transient-message behavior. Host action feedback above screen content in AppTheme so list status updates are not occluded by a Scaffold.

Keep repeated identical messages as distinct events, expire only the current event, and hide the idle host from accessibility navigation. The same small host is shared by the independently mergeable list branches; it does not request focus or emit manual accessibility announcements.

Validation: 77 Play Store debug JVM tests, Kotlin compilation, app/test APK assembly, and native live-region event checks through the focused list and combined builds. Not run: listening verification in every locale.
Keep transient accessibility state in the existing window-owned snackbar controller. Serialize publication for each semantics lifetime, prioritize pending service results, and discard pending feedback when the host pauses. This prevents a burst of routine messages from cancelling an assertive result before its text is published; TalkBack still owns speech scheduling.

Remove redundant polite arguments, the unused custom-action feedback host from Part 1, and test-only delivery/notification abstractions. Keep the reusable renderer and leave custom-action feedback in its consuming focused branches. Restore the shared theme to upstream to preserve independent mergeability.

Replace trivial helper tests with seven state regressions and four Android checks covering native accessibility export, inert snackbar semantics, lifecycle clearing, fallback replacement/cancellation, and channel-setting preservation. Validation: 69 JVM tests, Kotlin compilation, Play Store debug and test APK assembly, four Pixel API 37.1 checks with TalkBack 17 enabled, and both merge orders with all nine other focused branches plus upstream.
Give the host a single explicit owner instead of combining a supplied SnackbarHostState with a composition-local controller. Remove the unused LocalAppSnackbar provider. Keep this behavior-preserving wiring change shared by the notification, subscription, routing and server-row PRs so they remain independently mergeable.
Drop the three androidTest files introduced during the Part 1 review and remove their dedicated Compose UI test dependency, as requested. Retain the existing JVM regression tests, upstream test configuration, and all production accessibility fixes. The removed checks remain recoverable from Git history.

Validation: Play Store debug assembly and git diff --check passed. No tests were rerun; production sources and existing JVM tests are unchanged.
Keep the renderer identical to the focused action-feedback branches. Hosts may start their presentation interval after text publication; existing notification callers omit the callback and retain their current behavior.
Restore FIFO publication so later assertive results cannot overtake earlier service-state changes. Pass the host controller explicitly and share the localized started-server message between foreground feedback and background notifications.

Retain current upstream asset validation, editor state, metadata and update-result plurals. Remove the obsolete alternate service-start formatter and its superseded test. Play Store debug compilation, manifest processing and JVM tests pass.
Drop the unused INFO toast type and color, together with the unrequested long-duration flag and forwarding. Keep the existing short snackbar duration and normal, success and error styling. Per-app help already uses a dialog.

This restores the reviewed notification surface without changing FIFO live-region publication, accessibility urgency or foreground/background message routing. Validation: LiveRegionMessageStateTest and the full Play Store debug JVM suite (86 tests), including compilation and manifest processing, passed. No device tests run.
@eliotcougar
eliotcougar force-pushed the fix/accessibility-live-regions branch from 9174748 to 4f2af77 Compare September 10, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant