compose: Focus the message composer on entry for screen readers#6511
compose: Focus the message composer on entry for screen readers#6511andremion wants to merge 2 commits into
Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
|
@CodeRabbit review |
✅ Action performedReview finished.
|
WalkthroughIntroduces shared Compose accessibility utilities ( ChangesTalkBack Focus Management for Message Composer Entry
Sequence Diagram(s)sequenceDiagram
participant ChannelScreen
participant ComposerScreenReaderEntryFocus
participant rememberIsTouchExplorationEnabled
participant requestAccessibilityFocusForTestTag
participant semanticsNodeIdForTestTag
participant AndroidComposeView
ChannelScreen->>ComposerScreenReaderEntryFocus: composition (listViewModel)
ComposerScreenReaderEntryFocus->>rememberIsTouchExplorationEnabled: observe TalkBack state
rememberIsTouchExplorationEnabled-->>ComposerScreenReaderEntryFocus: isTouchExplorationEnabled=true
Note over ComposerScreenReaderEntryFocus: LaunchedEffect(viewKey, itemCount)
ComposerScreenReaderEntryFocus->>ComposerScreenReaderEntryFocus: withFrameNanos (defer 1 frame)
ComposerScreenReaderEntryFocus->>requestAccessibilityFocusForTestTag: ComposerInputTestTag
requestAccessibilityFocusForTestTag->>semanticsNodeIdForTestTag: ComposerInputTestTag
semanticsNodeIdForTestTag->>AndroidComposeView: reflect getSemanticsOwner()
AndroidComposeView-->>semanticsNodeIdForTestTag: SemanticsOwner
semanticsNodeIdForTestTag-->>requestAccessibilityFocusForTestTag: nodeId
requestAccessibilityFocusForTestTag->>AndroidComposeView: ACTION_ACCESSIBILITY_FOCUS(nodeId)
Note over ComposerScreenReaderEntryFocus: LaunchedEffect(viewKey) → delay(2000ms) → entryFocusSettled=true
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
SDK Size Comparison 📏
|
When a screen reader is active, move the reading cursor to the message composer input as a message view opens, without moving input focus, so the keyboard stays closed and the user double-taps to type. This uses ACTION_ACCESSIBILITY_FOCUS instead of FocusRequester (which would open the keyboard), and re-applies focus while the message list loads so the screen reader's own initial placement does not override it. Gated on touch exploration, so sighted users are unaffected. Compose exposes no public API to move accessibility focus, so the node is resolved reflectively via AndroidComposeView.getSemanticsOwner; a consumer ProGuard rule keeps that method for minified release builds, and the lookup fails safe otherwise. Share the composer input test tag as a single constant and reuse the touch-exploration helper across the composer and the Giphy preview.
eea0942 to
dfaa6c6
Compare
|



AND-1248
Goal
When a screen reader user opens a message view, the reading cursor should land on the message composer input, so they can start typing right away (WCAG 2.4.3 Focus Order, 1.3.1). Today the cursor lands somewhere in the middle of the message list, and where it lands varies with the content.
The root cause is that the message list uses
reverseLayout = true, which scrambles the accessibility traversal order (Compose bug b/232293357). So traversal-order solutions (traversalIndex/isTraversalGroup) cannot reliably hoist the composer to the top, and Compose exposes no public API to set initial accessibility focus.Implementation
ComposerScreenReaderEntryFocusinChannelScreenmoves the reading cursor onto the composer input when a message view opens. It only runs when an explore-by-touch service (TalkBack) is active, and it moves the reading cursor only, not input focus, so the keyboard stays closed and the user double-taps to type.ACTION_ACCESSIBILITY_FOCUSto the node tagged as the composer input. There is no public Compose API for this, so the node id is resolved reflectively viaAndroidComposeView.getSemanticsOwner(). The lookup fails safe: if anything cannot be resolved, focus is simply left untouched. Aconsumer-rules.prokeep-rule preserves that method in R8-minified release builds.parentMessageId), so opening a thread in place focuses the composer too.Shared accessibility helpers live in a new
AccessibilityUtils.kt. The existing private touch-exploration helper inGiphyMessageContentwas consolidated into it.Testing
Requires TalkBack on a device or emulator (with the soft keyboard enabled so it behaves like a physical device).
Automated coverage:
AccessibilityUtilsTest(touch-exploration state, semantics node lookup, fail-safe behavior) and the existingMessageComposerScreenTest/ChannelScreenTest.Summary by CodeRabbit
Release Notes
Bug Fixes
Refactor