From cc02ea36b282ea78b959d2355b3c716cca3db267 Mon Sep 17 00:00:00 2001 From: sallyyuen Date: Mon, 10 Feb 2020 10:45:48 -0800 Subject: [PATCH] Implement ACTION_LONG_CLICK for accessibility Due to changes in R, the a11y framework no longer dispatches touch events for a long press. This prevents the activation of EditText's floating menu. We can re-enable it by implementing the proper a11y action ACTION_LONG_CLICK. The menu itself is diffult to access through TalkBack's linear navigation, but this is future work for a separate known issue. Start and stop the menu for editable TextViews, which includes EditTexts. Since touch events are no longer sent by a11y, separate the accessibility handling from the touch handling infrastructure for long clicks in Editor. We can't go through the main performLongClick code because it doesn't actually start the action mode but rather sets pending, which routes back to TextView. There's too little separation between the touch events and action logic. Whoever touches the performLongClick code may need to also make corresponding changes to the a11y path, but I suspect this won't happen often. Remove the onInitializeA11yNodeInfo override for EditText because this is handled by TextView. Bug: 148127445 Test: Tested text fields in various apps. ag/10602004. atest FrameworksCoreTests:TextViewActivityTest#testToolbarAppearsAccessibilityLongClick Change-Id: I3958e5b80e6156e03c99335e0d0b671438965ebb (cherry picked from commit 3f1203fb786a5edfe7ec191494edd887f3b419dd) Merged-In: I3958e5b80e6156e03c99335e0d0b671438965ebb --- core/java/android/widget/EditText.java | 10 --------- core/java/android/widget/Editor.java | 21 ++++++++++++++----- core/java/android/widget/TextView.java | 17 +++++++++++++++ .../android/widget/TextViewActivityTest.java | 16 ++++++++++++++ 4 files changed, 49 insertions(+), 15 deletions(-) diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java index 728824c2f0dc7..d661bc68ab06b 100644 --- a/core/java/android/widget/EditText.java +++ b/core/java/android/widget/EditText.java @@ -24,7 +24,6 @@ import android.text.TextUtils; import android.text.method.ArrowKeyMovementMethod; import android.text.method.MovementMethod; import android.util.AttributeSet; -import android.view.accessibility.AccessibilityNodeInfo; /* * This is supposed to be a *very* thin veneer over TextView. @@ -179,13 +178,4 @@ public class EditText extends TextView { protected boolean supportsAutoSizeText() { return false; } - - /** @hide */ - @Override - public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfoInternal(info); - if (isEnabled()) { - info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT); - } - } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 816612f1dcc7e..404c21c110248 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -317,6 +317,7 @@ public class Editor { private SelectionActionModeHelper mSelectionActionModeHelper; boolean mIsBeingLongClicked; + boolean mIsBeingLongClickedByAccessibility; private SuggestionsPopupWindow mSuggestionsPopupWindow; SuggestionRangeSpan mSuggestionRangeSpan; @@ -1312,6 +1313,12 @@ public class Editor { if (TextView.DEBUG_CURSOR) { logCursor("performLongClick", "handled=%s", handled); } + if (mIsBeingLongClickedByAccessibility) { + if (!handled) { + toggleInsertionActionMode(); + } + return true; + } // Long press in empty space moves cursor and starts the insertion action mode. if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY()) && !mTouchState.isOnHandle() && mInsertionControllerEnabled) { @@ -1359,6 +1366,14 @@ public class Editor { return handled; } + private void toggleInsertionActionMode() { + if (mTextActionMode != null) { + stopTextActionMode(); + } else { + startInsertionActionMode(); + } + } + float getLastUpPositionX() { return mTouchState.getLastUpX(); } @@ -5426,11 +5441,7 @@ public class Editor { config.getScaledTouchSlop()); if (isWithinTouchSlop) { // Tapping on the handle toggles the insertion action mode. - if (mTextActionMode != null) { - stopTextActionMode(); - } else { - startInsertionActionMode(); - } + toggleInsertionActionMode(); } } else { if (mTextActionMode != null) { diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2168018e12bef..e1783181457fb 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -12108,6 +12108,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener onEditorAction(getImeActionId()); } } return true; + case AccessibilityNodeInfo.ACTION_LONG_CLICK: { + if (isLongClickable()) { + boolean handled; + if (isEnabled() && (mBufferType == BufferType.EDITABLE)) { + mEditor.mIsBeingLongClickedByAccessibility = true; + try { + handled = performLongClick(); + } finally { + mEditor.mIsBeingLongClickedByAccessibility = false; + } + } else { + handled = performLongClick(); + } + return handled; + } + } + return false; default: { return super.performAccessibilityActionInternal(action, arguments); } diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index a72be25fedb5b..45d4b38d82aa5 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -65,6 +65,7 @@ import android.app.Activity; import android.app.Instrumentation; import android.content.ClipData; import android.content.ClipboardManager; +import android.os.Bundle; import android.support.test.uiautomator.UiDevice; import android.text.InputType; import android.text.Selection; @@ -75,6 +76,7 @@ import android.view.ActionMode; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.textclassifier.SelectionEvent; import android.view.textclassifier.TextClassificationManager; import android.view.textclassifier.TextClassifier; @@ -357,6 +359,20 @@ public class TextViewActivityTest { assertFalse(textView.hasSelection()); } + @Test + public void testToolbarAppearsAccessibilityLongClick() throws Throwable { + final String text = "Toolbar appears after performing accessibility's ACTION_LONG_CLICK."; + mActivityRule.runOnUiThread(() -> { + final TextView textView = mActivity.findViewById(R.id.textview); + final Bundle args = new Bundle(); + textView.performAccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, args); + }); + mInstrumentation.waitForIdleSync(); + + sleepForFloatingToolbarPopup(); + assertFloatingToolbarIsDisplayed(); + } + @Test public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable { final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);