Skip to content

Commit

Permalink
Allow text links to be navigatable via keyboard by default (facebook#…
Browse files Browse the repository at this point in the history
…48773)

Summary:

This diff contains 2 fixes with regards to clickable text accessibility on Android.

1. It allows nested `Text` links to be accessible via keyboard. The accessibility here is a bit unique. The clickable text is not focused in the classical since that Android provides on `View`s (after all, the part being focused is not even a `TextView`. Its a `Span` in a `TextView`). However, it is [selected](https://developer.android.com/reference/android/widget/TextView#setSelected(boolean)), and pressing enter while it is selected will press the text, so for all intents and purposes it is focused. Some quirks here, though, are that you cannot press tab to cycle through the links in text, you have to use arrow keys. And the arrow keys no longer let you move around to the next item (as they are stuck being used to navigate the links). I *could* override this behavior but I do not see much of a point as long as one can actually get to everything on screen in a semi-logical way. Anyway, the fix here is using [`LinkMovementMethod`](https://developer.android.com/reference/android/text/method/LinkMovementMethod) to navigate between clickable spans, which is exactly what that class exists for. Note that I override this class so that touching the links does not actually highlight them like you see in the videos.
2. In the case we state update and remove the "click-ability" of the text, this diff makes it so that Explore By Touch no longer can focus on the link. The code was in the same area so I decided to just lump that together :P

Changelog: [Android] [Fixed] - Allow text links to be navigable via keyboard by default

Differential Revision: D68306316
  • Loading branch information
joevilches authored and facebook-github-bot committed Jan 21, 2025
1 parent 9afad52 commit 0093b32
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.facebook.react.views.text

import android.text.Selection
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.view.MotionEvent
import android.widget.TextView

internal object ReactLinkMovementMethod : LinkMovementMethod() {
override fun onTouchEvent(widget: TextView, buffer: Spannable, event: MotionEvent): Boolean {
val result = super.onTouchEvent(widget, buffer, event)
Selection.removeSelection(buffer)
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ public void updateExtraData(ReactTextView view, Object extraData) {
if (update.containsImages()) {
TextInlineImageSpan.possiblyUpdateInlineImageSpans(spannable, view);
}
view.setText(update);

// If this text view contains any clickable spans, set a view tag and reset the accessibility
// delegate so that these can be picked up by the accessibility system.
Expand All @@ -106,7 +105,16 @@ public void updateExtraData(ReactTextView view, Object extraData) {
new ReactAccessibilityDelegate.AccessibilityLinks(clickableSpans, spannable));
ReactAccessibilityDelegate.resetDelegate(
view, view.isFocusable(), view.getImportantForAccessibility());
// To enable navigation via keyboard, which is distinct from accessibility navigation.
view.setMovementMethod(ReactLinkMovementMethod.INSTANCE);
} else {
view.setMovementMethod(null);
view.setTag(R.id.accessibility_links, null);
ReactAccessibilityDelegate.resetDelegate(
view, view.isFocusable(), view.getImportantForAccessibility());
}

view.setText(update);
}
}

Expand Down

0 comments on commit 0093b32

Please sign in to comment.