TalkBack: app accessibility combined implementation (temporary) - #6112
eliotcougar wants to merge 1 commit into
Conversation
|
Thanks for the accessibility improvements in Overall, the changes work well, and I did not notice any other major accessibility problems. I found only a few remaining issues:
All switches currently require two swipes/focus stops:
I think this could be simplified to a single accessible element, for example: “Setting name, On” There is no need for “is currently on/off” because simply announcing “On” or “Off” is enough. The current behavior also unnecessarily doubles the number of swipes needed to navigate through the settings. The good part is that after changing the switch, the new On/Off state is announced correctly, so that part works very well. The same behavior occurs in Per-app Proxy for the two switchable options. The application names and checkboxes in that screen work correctly, including announcing their checked/unchecked state.
Also, I noticed that the Delete action is available inside the Edit screen. I am not sure whether a Delete action is also supposed to be available directly on the rule row in the list, because TalkBack does not announce a Delete action there. I can only confirm that the Delete action is accessible after entering the Edit screen. If Delete is intended to be available directly from the list, it may be worth checking whether it is exposed correctly to screen readers. Apart from these points, the other areas I tested, including User Assets and the general Subscription/Routing navigation, worked well for me. Overall, |
|
This PR has grown quite a bit, but it now addresses everything except the Edit server/subscription group/routing rule/asset Activities, and the main server list that is covered separately by #6105. It is almost done as well. The PR may be big in terms of lines of code, but it does very similar things to all interactive elements in the main Activities. Namely, it constructs custom spoken strings for TalkBack to say and sometimes suppresses the stuff that is unimportant or intrusive. Normal navigation is unaffected. |
|
Thanks for the updates. I tested the latest version of #6112 on a real device with both TalkBack and Commentary, and overall the accessibility behavior is looking much better now. I went through the areas that were changed in this PR and I did not find any other significant accessibility problems. Here is what I found:
This is the only remaining issue I noticed. Each Switch now appears twice during swipe navigation, and both entries are independently clickable. On the first swipe, the screen reader announces the setting name, its description, and then the Switch. On the second swipe, it announces the setting name and its current state (for example, “is on” or “is off”), followed by the Switch. Both entries actually change the same Switch state when activated, and the state is announced immediately after changing it. So the functionality itself works correctly. However, having the same Switch exposed twice makes navigation unnecessarily long and confusing. Ideally, each Switch should appear only once in swipe navigation. The same behavior is also present with the two Switches at the top of the Per-app Proxy screen. One clarification: the “is on / is off” announcement itself is good and useful. I would keep that. The problem is the duplicate accessibility node/focus, not the state announcement.
The application list is much better now. The CheckBox descriptions are announced clearly, and I did not notice any problem there. The only issue remaining on this screen is the duplicate swipe behavior of the two Switches mentioned above.
I tested the Rule list and deletion flow again. Everything looks good now:
I tested the subscription list and deletion flow. The subscription name is announced correctly, and the deletion confirmation also includes the subscription name. I did not find any accessibility problem here.
The deletion flow is also working correctly. The asset name is included in the confirmation message.
I also checked several normal Settings items that are not Switches, as well as different screens/titles. I did not notice any other duplicate announcements, broken navigation, or missing accessibility information. I also did not notice any visual/UI changes that would negatively affect sighted users. Overall, I think the PR is in very good shape now. I tested it quite thoroughly with both TalkBack and Commentary, and the only issue I can still reproduce is the duplicate exposure of Switches during swipe navigation. So from my side, I would consider the current implementation very close to complete. Thank you for all the work on this PR! |
|
Ummm... What's the "swipe navigation"? Nevermind... I figured it out... Swipe navigation... |
|
By “swipe navigation”, I mean moving between accessibility elements using swipe gestures in TalkBack or Commentary. Yes, that sounds exactly like the issue I was seeing. The row and the Switch were exposed as two separate focusable elements even though they control the same setting. Your mergeDescendants approach sounds good to me. I’ll test it carefully with both TalkBack and Commentary once it’s implemented. Thanks! |
|
The first-order Activities' accessibility semantics are fully functional, but it's not pretty yet under the hood. Let me do a couple more cleanup passes before it's ready for merge. |
|
I tested the latest changes quite thoroughly, and overall the accessibility behavior is now very good. The Switch double-swipe issue is fixed, Per-app Proxy, Routing, Subscriptions, User Assets, and server focus/navigation all work well. I noticed two small things:
One small unrelated suggestion: in the main navigation drawer, TalkBack currently adds the element role “tab” to each item's spoken announcement, for example “Settings, tab” or “Per-app proxy, tab”. If possible, I think it would be cleaner if the spoken announcement contained only the item's name, e.g. simply “Settings” or “Per-app proxy”, without adding “tab”, while keeping the underlying accessibility semantics and navigation behavior intact. Apart from these points, everything I tested is working very well. The Switch behavior in particular is now much better. |
|
One more thing I noticed while testing the current versions, unrelated to the accessibility changes in this PR. The way ordinary messages are currently presented feels more intrusive than it used to. In 2.2.6, these messages felt much simpler and less intrusive, while in the versions after 2.2.6, including the current 2.3.5 pre-release, they are presented using the newer Snackbar-style UI, which appears more like a large notification/card. This is especially noticeable when using TalkBack. Would it be possible to simplify the presentation of these ordinary messages, perhaps by using a simple Toast-style notification instead of the current Snackbar presentation? I think this could make the overall experience cleaner and less distracting, especially for TalkBack users. If you think this is a reasonable approach, could you please consider implementing it? I would be happy to test the resulting build and report back on how it behaves with TalkBack. |
I already tried to do that, but for some reason TalkBack doesn't say the page title after the app name (the app name announcement seems to be strongly hardcoded). I may try to look into it one more time. A dirty fix would be to change the focusing order. Right now the uppermost button is focused by default, which is usually the "Menu" or "Back" button. The page title is usually displayed next to it in the current UI. I already started addressing the notification storm caused by simultaneous duplicated toasts and notification updates. My plan is to completely remove most toasts from the accessibility semantics (also they are very annoying because they tend to steal selection focus when they pop up) and replace them with explicit voice-only announcements instead. The rest is exactly the kinds of issues I plan to address during the cleanup. |
|
I tested the latest two commits with TalkBack, and everything is working very well.
Overall, these two commits look very good from a TalkBack user's perspective. Thank you! |
|
The refinement passes are complete. This PR should have no intersections with #6105. The two can be merged in any order, hopefully. It is better to test them merged together. As I said earlier, the PR is very large, but it is uniform. It is mostly the same kind of changes in every place. |
…esolve conflicts)
|
I tested PR Overall, most of the accessibility improvements work correctly. However, two important server-related announcements regress when the changes from the two PRs are combined.
In PR In PR "Connected, tap to check connection" The merged build of #6105 + #6112 has the same regression. The desired behavior is: Tap Test The "Testing..." announcement is important, rather than merely cosmetic. Without it, a screen-reader user receives no immediate auditory feedback that the test has actually started.
The actual server selection works correctly. For example, when I tap an unselected server, the server is successfully selected and becomes active. However, the accessibility announcement is not produced immediately after the selection. Currently, after selecting a server, TalkBack does not announce the new selected state unless I manually move the accessibility focus somewhere else and then return to the server. After doing that, it correctly announces something equivalent to: "Selected, " followed by the server information. The desired behavior is: Immediately after activating a different server, TalkBack should announce: "Selected, " without requiring the user to move the accessibility focus away and back again. The server name should of course be the actual name of the selected server; "France" is only an example.
When swiping between subscription groups before selecting a group, there is a noticeable delay before the next group is announced. The delay feels approximately 1–2 seconds, although I cannot measure the exact duration. During this interval, TalkBack is silent. For example: Group A Group B is eventually announced correctly, and selecting it works correctly. The issue is therefore mainly responsiveness during navigation rather than a failure to select or announce the destination group. With the previous subscription-group structure, navigation felt more responsive and did not have this noticeable delay.
The following areas were tested and did not show problems in the current combined build:
This area may need additional verification. At the moment there is no newer version available to test the "new version available" case. In the current test, when checking for updates, TalkBack did not announce either that an update was available or that the application was already up to date. I therefore do not want to classify this as a confirmed regression yet. It may simply require more specific testing of the available-result states.
I also performed a real merge of PR The merge produced conflicts in several localized strings.xml files. These conflicts were resolved manually with the help of AI, so the resulting localized string resources should also be taken into consideration when evaluating the combined behavior. After resolving the conflicts, the combined build was tested separately. Importantly, some of the accessibility behavior that worked correctly in However, I cannot determine with certainty whether these regressions were caused by the code changes themselves, the localized string resources, or the conflict resolution during the merge. Therefore, the interaction between the two PRs should be tested carefully after merging, including the resulting accessibility strings/resources, rather than assuming that the two PRs are behaviorally compatible simply because each PR can be tested independently.
The accessibility announcements were also tested in Persian with TalkBack. Persian TTS is not a limitation of this testing environment. The configured TTS engine can correctly handle the Persian accessibility announcements, so the reported issues are not caused by a lack of Persian TTS support. In summary, the accessibility improvements are largely working well. The main issues I would recommend addressing are:
The server selection itself is functioning correctly; the main problem is the timing and delivery of the accessibility announcements. |
|
Thank you very much for extensive testing.
I suggest merging it as is for now. It will be easier to address the issues separately.
|
|
Thank you for the detailed explanation. That makes sense. I agree with merging it as is for now and addressing these issues separately. Regarding the "Testing" announcement, I should mention that this behavior was actually working very well in PR #6112. Each time I tapped the test action, TalkBack announced "Testing..." immediately, followed by the testing information/result. I think it would be good to preserve that behavior when addressing this issue. For the selected-server announcement, I would prefer a clean and explicit solution rather than relying on the previous announcement storm behavior. The desired behavior is that immediately after tapping a server, TalkBack explicitly announces the newly selected server (for example, "Selected, France") without requiring the user to move the focus away and back. Regarding Persian, I understand that Persian/Farsi is not available in your TalkBack setup. On my device, Persian accessibility announcements are read correctly. If you ever want to test the Persian announcements yourself, eSpeak-NG can be installed as a speech engine and selected as the default TTS engine; it supports Persian and can be used to test these announcements directly. Thanks again for taking the time to investigate these issues. |
Fixing is right now together with the cleaner cross-PR resources split adjustments. UPD: The loss of "Testing..." announcement was the result of suppression of the bulk testing continuously updating string... Better fix is coming... |
|
Thank you very much for your work on these PRs. I have now tested the latest versions of PR For the accessibility changes covered by these PRs, I consider the work complete and I am satisfied with the current result. There are still other parts of the application that would benefit from a similar accessibility review. For example, the "Check for updates" screen currently does not seem to provide a clear accessibility announcement such as "You are up to date" when no update is available. Other screens should also be reviewed to make sure that useful status messages and announcements have not been removed or are not being exposed correctly to TalkBack. Unfortunately, I will be travelling for a while starting tomorrow and will not be able to perform further testing for some time. Therefore, I would suggest merging these PRs into the main project now, and we can investigate the remaining accessibility issues and improve other screens later when I am able to continue testing. Thank you again for the extensive work on the accessibility improvements. |
46a9b66 to
9f040af
Compare
9f040af to
ca85351
Compare
ca85351 to
ba01f0c
Compare
Accessibility implementation policy and current refactor statusI want to make the direction of this work explicit after the maintainer feedback. The accessibility layer in this PR will be implemented in accordance with the official Android and Jetpack Compose accessibility guidance, even where the sentence generated by a particular screen reader sounds less polished than the hand-crafted wording testers preferred. Tester feedback remains essential: it tells us when navigation is inefficient, a label lacks context, important information is missing, or a transient result is not communicated. However, the solution must preserve the semantic contract exposed to every accessibility service. We should provide the correct label, role, state, action, enabled/disabled status, selection state, grouping, and reading order, then let TalkBack, Commentary, Switch Access, Voice Access, and other consumers decide how to present those properties. In particular, the order in which TalkBack speaks those properties is not controlled by the app. A phrase such as “Setting name, on, switch”, with the role pronounced at the end, is normal service-generated output. Removing the The current guideline-alignment refactor is in What has already changed
What still needs to change before this is ready
Official guidance being followed
This push is therefore a preparatory correction, not a claim that the broad PR is ready to merge. The commitment going forward is: fix missing meaning and inefficient navigation, but do not fight the accessibility framework merely to force one preferred spoken sentence. |
ba01f0c to
17312c0
Compare
17312c0 to
71ebe5e
Compare
|
I have split the transient notification accessibility layer out of this PR into draft PR #6157. That focused PR now owns:
This branch has been rewritten to remove that layer completely. #6112 is now limited to the remaining row, control, dialog, and screen semantics instead of carrying a second notification implementation. Both branches are based directly on current upstream. They merge cleanly in either order, both orders produce the same resulting tree, and the exact merged result compiles and passes the full unit-test suite. Because #6112's single commit was narrowed in place, its remote branch was updated with a verified force-with-lease. This is the first step in breaking the larger accessibility bundle into small, independently reviewable PRs while keeping the tested behavior intact. |
|
The accessibility work has been refactored in accordance with the official Android accessibility guidelines and split into 10 separate non-intersecting PRs. |
|
I tested the combined accessibility changes on a real device with TalkBack enabled. Most of the changes work very well, and I found only a few issues that appear worth addressing:
“Not selected. 🇫🇮 FI. pt-play.mobileemdad.*** : 443. VLESS / reality. Radio button.” The selected server is announced as: “Selected. 🇩🇪 Test 1. crm.mobileemdad.*** : 3131. VLESS / xhttp / reality. Radio button.” The row actions themselves work correctly and I did not observe any accidental activation. However, announcing “Not selected” and “Radio button” for every unselected server is quite verbose and becomes annoying when navigating through many servers. Ideally, the normal server rows should remain as they were, while the selected server's state could be exposed if this can be done while preserving the proper accessibility semantics.
“Import sub. Last updated on 31 August 2026, 03:18.” However, removing the URL from accessibility information causes a problem when multiple subscriptions have no unique name. For example, a user can import several subscriptions and they may all appear as “Import sub”. In that situation, TalkBack gives identical information for multiple subscriptions, so the user cannot determine which subscription is which. The URL therefore needs to remain available to accessibility services when it is necessary to distinguish subscriptions that do not have a unique/more meaningful name. The visual URL does not necessarily need to change.
However, after the test completes, when navigating back to the test button, TalkBack only reads: “Connected.” The resulting connection status does not appear to be exposed/read as part of the button's accessible state in a sufficiently informative way. It would be useful for the final status to remain available through the accessibility semantics of the relevant UI, not only as the one-time transient announcement.
Also, the navigation items are announced with “Tab” at the end, for example: “Settings, tab” The navigation items otherwise work correctly. The main issue is the silent initial accessibility-focus target and whether the “Tab” role is actually appropriate for these drawer items.
However, after activating a switch and changing its state, TalkBack does not immediately report the new On/Off state. The user has to navigate away and return to the switch to hear the new state. Aside from these points, I tested the following areas without finding problems:
Overall, the accessibility improvements are working quite well. These are the remaining issues I found during real-device TalkBack testing. |
|
@aliRahimi1997 Thank you for the detailed real-device testing. I am GPT-5.6 Sol, the AI assistant working with @eliotcougar, and I am posting this response at their request. The rejected approach in this PR accumulated too much custom speech, focus manipulation, and replacement accessibility semantics. That was an implementation problem on our side, not a problem with your testing. The smaller replacement PRs should remove that complexity, not merely redistribute it. There is an important distinction between fixing missing accessibility information and making a screen reader pronounce a particular sentence. Android recommends short app-provided labels, with control type and state conveyed separately through semantics; the accessibility service then presents them. Its warning against putting “button” or “checked” into a label is not a recommendation to remove the actual role or checked state. Official guidance on labels, types, and states. Here is how that applies to your five points: 1. Server rows: “Not selected” and “Radio button” (#6167)Your qualification about preserving proper semantics is important. Selecting one active server is a single-choice interaction. Android's documented pattern exposes the selection group, the selected state of each option, and We should therefore retain the meaningful role and both selected/unselected states. Silencing those by erasing semantics or substituting a hand-written spoken sentence would trade structured information for one preferred TalkBack output. There is no app-side guarantee of the exact spoken wording while leaving the same semantics intact. TalkBack itself provides user-controlled verbosity settings. Consequently, suppressing the correct role/unselected state solely for brevity is not a change we should make. Redundant app-provided metadata or duplicate focus stops are a different matter and remain worth simplifying. This is a design conclusion from the guidelines, not a claim that Android mandates radio-button semantics for every selectable list. 2. Indistinguishable subscriptions (#6158)This is a valid accessibility concern, not something the guidelines justify leaving unresolved. Android explicitly calls for distinguishable labels for collection items. A URL is not redundant when it supplies otherwise missing identity. Collection labeling guidance. The newer blank-name fallback supplies “Unnamed.” plus the URL host and uses that identifier in associated actions and confirmations. However, it does not resolve repeated nonblank names such as “Import sub”, and a host alone cannot distinguish two subscriptions on the same host. That remaining case needs meaningful disambiguation. It does not necessarily require automatically speaking an entire subscription URL, including credentials or access tokens. 3. A connection-test result must remain accessible (#6168)Agreed. A one-time announcement is not a substitute for information the user can revisit. Updated semantic text/state and live-region notifications serve different purposes. Android's semantics guidance. There is now a follow-up commit in #6168 retaining the completed result on the status row. That implementation still deserves real TalkBack testing; I am not treating source inspection or semantics tests as proof of its spoken behavior on your device. 4. Drawer: silent initial target versus “Tab” (#6166)These are separate issues. The empty decorative header was a genuine problem: decorative content should not create a meaningless navigation stop. A follow-up in #6166 hides that container from accessibility without forcing focus elsewhere. Android's decorative-content guidance. The “Tab” role, however, comes from Material 3's 5. Switches not reporting the new state (#6161)This also remains a valid report. The intended implementation is one interactive row with an observable checked state and native toggle semantics, with the child switch not creating a second action target. That is the documented Compose pattern. Missing change feedback needs investigation; the guidelines do not excuse requiring users to leave and revisit the control. We should fix the state/event problem rather than mask it with an unconditional custom announcement after every tap. Why not restore the old announcement/focus machinery?Android warns that clearing semantics removes information from its consumers and should be used sparingly. It does allow justified custom semantics; even its custom-toggle example restores the role and toggle state. Merging and clearing guidance. Also, In short: we should decline speech-only suppression of correct roles/states, not decline genuine accessibility fixes. Your reports about identity, persistent results, empty focus targets, and state-change feedback remain useful and actionable. The goal is a smaller, standards-based implementation that works across accessibility services, rather than one that scripts a particular reader's output. |
Status
This draft has been rebuilt directly on current
upstream/master(13138ddd, v2.3.6). Its history is now one accessibility-only commit:17312c04.The obsolete pre-merge #6105/service-transition commits are no longer part of this branch. The merged #6105 group paging and #6107 server-row model architecture are inherited from upstream rather than replayed here.
Summary
TYPE_ANNOUNCEMENTdispatch. Important foreground transient results use a polite Compose live region while the visual Snackbar/Toast surface remains outside accessibility navigation.Upstream conflict resolution
ServerRowUiModeland row-action architecture; the delete action passes the existingProfileItemonly so the confirmation can retain the server name.Accessibility implementation policy
The branch now follows the official Android/Compose approach described in the detailed policy comment below: expose correct labels, roles, states, actions, grouping, and enabled/disabled status, then let the accessibility service choose spoken order. Native role announcements are retained even when TalkBack appends words such as “switch”, “radio button”, or “tab” at the end.
This remains a broad consolidation draft. The next publication step is to split its independently reviewable behavior into smaller focused PRs without reintroducing the discarded semantic workarounds.
Validation
:app:processPlaystoreDebugResources:app:compilePlaystoreDebugKotlin:app:testPlaystoreDebugUnitTestgit diff --checkaudits