From 55022a5880614ff6f048ab622160059a164e83b1 Mon Sep 17 00:00:00 2001 From: Sally Date: Tue, 15 Mar 2022 17:33:40 +0000 Subject: [PATCH] Fixes for a11y selection - TextView should request focus before performing selection on non-editable text, since selection requires focus. On the a11y service side, input focus should not matter when selecting non-editable text. - Remove clickable and long-clickable states, since these are present due to TextView#setTextIsSelectable. - Make UI controller changes consistent between SET_SiELECTION and SET_TEXT. Using SET_SELECTION means the action mode menu won't pop up and the left/right selection handles won't appear. Interactions between multiple EditTexts were previously inconsistent - Delete some obsolete javadoc Bug: 218357385 Test: builds and install: highlighting is consistent between SET actions. highlighting works when selecting text in TalkBack, without the service needing to purposely put input focus, atest AccessibilityTextTraverssalText Change-Id: I67986020aa112d45bd670f77b2b415617226c094 --- core/java/android/view/View.java | 9 ++++++ core/java/android/widget/TextView.java | 40 +++++++++++++++++++------- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1b8dc706371ec..febd2e2efdacf 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14649,6 +14649,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int selectionStart; int selectionEnd; if (extendSelection && isAccessibilitySelectionExtendable()) { + prepareForExtendedAccessibilitySelection(); selectionStart = getAccessibilitySelectionStart(); if (selectionStart == ACCESSIBILITY_CURSOR_POSITION_UNDEFINED) { selectionStart = forward ? segmentStart : segmentEnd; @@ -14687,6 +14688,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } + /** + * Prepare for extended selection. + * @hide + */ + public void prepareForExtendedAccessibilitySelection() { + return; + } + /** * @hide */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 3c8fcb978fbdd..6f83a45ce7c69 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12363,9 +12363,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // A view should not be exposed as clickable/long-clickable to a service because of a - // LinkMovementMethod. + // LinkMovementMethod or because it has selectable and non-editable text. if ((info.isClickable() || info.isLongClickable()) - && mMovement instanceof LinkMovementMethod) { + && (mMovement instanceof LinkMovementMethod + || (isTextSelectable() && !isTextEditable()))) { if (!hasOnClickListeners()) { info.setClickable(false); info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK); @@ -12597,11 +12598,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } if (start >= 0 && start <= end && end <= text.length()) { + requestFocusOnNonEditableSelectableText(); Selection.setSelection((Spannable) text, start, end); - // Make sure selection mode is engaged. - if (mEditor != null) { - mEditor.startSelectionActionModeAsync(false); - } + hideAccessibilitySelectionControllers(); return true; } } @@ -12695,6 +12694,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return handled; } + private void requestFocusOnNonEditableSelectableText() { + if (!isTextEditable() && isTextSelectable()) { + if (!isEnabled()) { + return; + } + + if (isFocusable() && !isFocused()) { + requestFocus(); + } + } + } + private boolean hasSpannableText() { return mText != null && mText instanceof Spannable; } @@ -12723,9 +12734,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Returns the text that should be exposed to accessibility services. *

- * This approximates what is displayed visually. If the user has specified - * that accessibility services should speak passwords, this method will - * bypass any password transformation method and return unobscured text. + * This approximates what is displayed visually. * * @return the text that should be exposed to accessibility services, may * be {@code null} if no text is set @@ -13703,6 +13712,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } + /** + * @hide + */ + public void prepareForExtendedAccessibilitySelection() { + requestFocusOnNonEditableSelectableText(); + } + /** * @hide */ @@ -13727,8 +13743,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Selection.removeSelection((Spannable) text); } // Hide all selection controllers used for adjusting selection - // since we are doing so explicitlty by other means and these + // since we are doing so explicitly by other means and these // controllers interact with how selection behaves. + hideAccessibilitySelectionControllers(); + } + + private void hideAccessibilitySelectionControllers() { if (mEditor != null) { mEditor.hideCursorAndSpanControllers(); mEditor.stopTextActionMode();