Set the a11yNodeInfo unclickable if the TextView has a
LinkMovementMethod and there are no onClickListeners or
onLongClickListeners
A view with links shouldn't be exposing itself as clickable to
an a11yService if only the links are clickable. If a movement method is
set, the view turns both clickable and long-clickable. This has brought
a number of bugs where an a11yService will notify users the element is
clickable but performing the action does nothing.
We can't delete years-old code without breaking a lot of things, so try
to minimize the consequences by adding logic in onInitializeA11yNodeInfo.
Add checks so a view with links and no click listeners will be made un-clickable.
Bug: b/131758159
Test: Tested on several bugs with TalkBack, CtsAccessibilityTests, CtsTextTestCases,
CtsWidgetTestCases:TextViewTest
Change-Id: I53b695139ecea2c34d125e7077fd2077593adbe1
diff --git a/api/current.txt b/api/current.txt
index d3807fa..352638e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -50331,6 +50331,7 @@
method public boolean hasFocusable();
method public boolean hasNestedScrollingParent();
method public boolean hasOnClickListeners();
+ method public boolean hasOnLongClickListeners();
method @android.view.ViewDebug.ExportedProperty(category="drawing") public boolean hasOverlappingRendering();
method public boolean hasPointerCapture();
method @android.view.ViewDebug.ExportedProperty(category="layout") public boolean hasTransientState();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7eb3876..1d8b910 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7067,6 +7067,15 @@
}
/**
+ * Return whether this view has an attached OnLongClickListener. Returns
+ * true if there is a listener, false if there is none.
+ */
+ public boolean hasOnLongClickListeners() {
+ ListenerInfo li = mListenerInfo;
+ return (li != null && li.mOnLongClickListener != null);
+ }
+
+ /**
* Register a callback to be invoked when this view is context clicked. If the view is not
* context clickable, it becomes context clickable.
*
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cdbec29..4da334b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11735,6 +11735,20 @@
if (!isSingleLine()) {
info.setMultiLine(true);
}
+
+ // A view should not be exposed as clickable/long-clickable to a service because of a
+ // LinkMovementMethod.
+ if ((info.isClickable() || info.isLongClickable())
+ && mMovement instanceof LinkMovementMethod) {
+ if (!hasOnClickListeners()) {
+ info.setClickable(false);
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
+ }
+ if (!hasOnLongClickListeners()) {
+ info.setLongClickable(false);
+ info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+ }
+ }
}
@Override