Skip to content

Commit 748225d

Browse files
joevilchesfacebook-github-bot
authored andcommitted
a11y] Let links discovered by dataDetectorType be accessible (facebook#50914)
Summary: The URL spans generated by Linkify are not actually accessible because we do not update the delegate's virtual views. I also had to change how AccessibilityLinks get generated, since it does not work well if it includes spans that are not `ReactClickableSpans` Changelog: [Internal] Reviewed By: NickGerleman Differential Revision: D73612119
1 parent eadc298 commit 748225d

File tree

3 files changed

+9
-21
lines changed

3 files changed

+9
-21
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import com.facebook.react.uimanager.style.BorderStyle
3131
import com.facebook.react.uimanager.style.LogicalEdge
3232
import com.facebook.react.uimanager.style.Overflow
3333
import com.facebook.react.views.text.ReactTextViewAccessibilityDelegate.AccessibilityLinks
34-
import com.facebook.react.views.text.internal.span.ReactClickableSpan
3534
import java.util.HashMap
3635

3736
@ReactModule(name = PreparedLayoutTextViewManager.REACT_CLASS)
@@ -71,12 +70,10 @@ internal class PreparedLayoutTextViewManager :
7170
// delegate so that these can be picked up by the accessibility system.
7271
if (layout.text is Spanned) {
7372
val spannedText = layout.text as Spanned
74-
75-
val clickableSpans =
76-
spannedText.getSpans(0, layout.text.length, ReactClickableSpan::class.java)
73+
val accessibilityLinks = AccessibilityLinks(spannedText)
7774
view.setTag(
7875
R.id.accessibility_links,
79-
if (clickableSpans.size > 0) AccessibilityLinks(clickableSpans, spannedText) else null)
76+
if (accessibilityLinks.size() > 0) accessibilityLinks else null)
8077
ReactTextViewAccessibilityDelegate.resetDelegate(
8178
view, view.isFocusable, view.importantForAccessibility)
8279
}

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewAccessibilityDelegate.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,13 @@ internal class ReactTextViewAccessibilityDelegate : ReactAccessibilityDelegate {
269269
return null
270270
}
271271

272-
public class AccessibilityLinks(spans: Array<out ClickableSpan>, text: Spanned) {
272+
public class AccessibilityLinks(text: Spanned) {
273273
private val links: List<AccessibleLink>
274274

275275
init {
276276
val accessibleLinks = mutableListOf<AccessibleLink>()
277+
val spans = text.getSpans(0, text.length, ClickableSpan::class.java)
278+
spans.sortBy { text.getSpanStart(it) }
277279
for (i in spans.indices) {
278280
val span = spans[i]
279281
val start = text.getSpanStart(span)
@@ -287,14 +289,7 @@ internal class ReactTextViewAccessibilityDelegate : ReactAccessibilityDelegate {
287289
link.description = text.subSequence(start, end).toString()
288290
link.start = start
289291
link.end = end
290-
291-
// ID is the reverse of what is expected, since the ClickableSpans are returned in reverse
292-
// order due to being added in reverse order. If we don't do this, focus will move to the
293-
// last link first and move backwards.
294-
//
295-
// If this approach becomes unreliable, we should instead look at their start position and
296-
// order them manually.
297-
link.id = spans.size - 1 - i
292+
link.id = i
298293
accessibleLinks.add(link)
299294
}
300295
links = accessibleLinks

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.facebook.react.uimanager.StateWrapper;
2727
import com.facebook.react.uimanager.ThemedReactContext;
2828
import com.facebook.react.uimanager.annotations.ReactProp;
29-
import com.facebook.react.views.text.internal.span.ReactClickableSpan;
3029
import com.facebook.react.views.text.internal.span.TextInlineImageSpan;
3130
import com.facebook.yoga.YogaMeasureMode;
3231
import java.util.HashMap;
@@ -106,13 +105,10 @@ public void updateExtraData(ReactTextView view, Object extraData) {
106105

107106
// If this text view contains any clickable spans, set a view tag and reset the accessibility
108107
// delegate so that these can be picked up by the accessibility system.
109-
ReactClickableSpan[] clickableSpans =
110-
spannable.getSpans(0, update.getText().length(), ReactClickableSpan.class);
108+
ReactTextViewAccessibilityDelegate.AccessibilityLinks accessibilityLinks =
109+
new ReactTextViewAccessibilityDelegate.AccessibilityLinks(spannable);
111110
view.setTag(
112-
R.id.accessibility_links,
113-
clickableSpans.length > 0
114-
? new ReactTextViewAccessibilityDelegate.AccessibilityLinks(clickableSpans, spannable)
115-
: null);
111+
R.id.accessibility_links, accessibilityLinks.size() > 0 ? accessibilityLinks : null);
116112
ReactTextViewAccessibilityDelegate.Companion.resetDelegate(
117113
view, view.isFocusable(), view.getImportantForAccessibility());
118114
}

0 commit comments

Comments
 (0)