From f0e80ae2292ebf7ce32666900007845724844fb5 Mon Sep 17 00:00:00 2001 From: Emily Janzer Date: Wed, 2 Sep 2020 16:13:35 -0700 Subject: [PATCH] Set selection to end of text input on accessibility click Summary: When we render a text input that already has a text value (), its selection (cursor) is automatically set to the end of the text. However, when you swipe to focus the text input with TalkBack, Android decides it needs to clear the selection, which moves the cursor back to the beginning of the text input. This is probably not what you want if you're editing some text that's already there. Ideally we would just keep the selection at the end, but I don't know how to prevent this from happening - it seems to be part of how TextView handles the accessibility focus event? So instead I'm just explicitly setting the selection to the end of the text in our handler for accessibility click. Changelog: [Android][Fixed] Move selection to the end of the text input on accessibility click Reviewed By: mdvacca Differential Revision: D23441077 fbshipit-source-id: 16964f5b106637e55a98c6b0ef0f0041e8e6215d --- .../react/tests/TextInputTestCase.java | 31 +++++++++++++++++++ .../react/views/textinput/ReactEditText.java | 8 +++++ 2 files changed, 39 insertions(+) diff --git a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java index 3d955e4b4e5fba..920ce021812192 100644 --- a/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java +++ b/ReactAndroid/src/androidTest/java/com/facebook/react/tests/TextInputTestCase.java @@ -199,6 +199,37 @@ public void run() { assertFalse(reactEditText.isFocused()); } + public void testAccessibilityFocus_notEmpty_selectionSetAtEnd() throws Throwable { + String testId = "textInput1"; + String text = "Testing"; + + final ReactEditText reactEditText = getViewByTestId(testId); + reactEditText.setText(text); + runTestOnUiThread( + new Runnable() { + @Override + public void run() { + reactEditText.clearFocus(); + } + }); + waitForBridgeAndUIIdle(); + assertFalse(reactEditText.isFocused()); + assertEquals(0, reactEditText.getSelectionStart()); + + runTestOnUiThread( + new Runnable() { + @Override + public void run() { + reactEditText.performAccessibilityAction( + AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); + reactEditText.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, null); + } + }); + waitForBridgeAndUIIdle(); + assertTrue(reactEditText.isFocused()); + assertEquals(text.length(), reactEditText.getSelectionStart()); + } + private void fireEditorActionAndCheckRecording( final ReactEditText reactEditText, final int actionId) throws Throwable { fireEditorActionAndCheckRecording(reactEditText, actionId, true); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index 8995949bb76dcb..79f35211152662 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -145,6 +145,14 @@ public ReactEditText(Context context) { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == AccessibilityNodeInfo.ACTION_CLICK) { + int length = getText().length(); + if (length > 0) { + // For some reason, when you swipe to focus on a text input that already has text in + // it, it clears the selection and resets the cursor to the beginning of the input. + // Since this is not typically (ever?) what you want, let's just explicitly set the + // selection on accessibility click to undo that. + setSelection(length); + } return requestFocusInternal(); } return super.performAccessibilityAction(host, action, args);