Skip to content

Commit

Permalink
Allow text inputs to handle native requestFocus calls (facebook#48547)
Browse files Browse the repository at this point in the history
Summary:

For some unknown reason, we have been swallowing [`requestFocus`](https://developer.android.com/reference/android/view/View#requestFocus(int) calls since `TextInput` is a controlled component - meaning you can control this components value and focus state from JS. This decision was originally made pre 2015 and I cannot find the reason why

I do not think this makes sense. We can still request focus from JS, while allowing the OS to request focus as well in certain cases and we would still be controlling this text input.

This is breaking keyboard navigation. Pressing tab or arrow keys will no-op if the next destination is a `TextInput`. This is because Android will call `requestFocus` from [here](https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/core/java/android/view/ViewRootImpl.java;l=7868?q=performFocusNavigation) when handling key events. Notably, Explore By Touch (TalkBack) swiping gestures WOULD focus `TextInputs` since they go through `ExploreByTouchHelper` methods which we override to call the proper `requestFocusInternal()` method.

**In this diff**: I move the logic in `requestFocusInternal()` into `requestFocus`.

Differential Revision: D67953398
  • Loading branch information
joevilches authored and facebook-github-bot committed Jan 9, 2025
1 parent cfec590 commit fcc7559
Showing 1 changed file with 4 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) {
// selection on accessibility click to undo that.
setSelection(length);
}
return requestFocusInternal();
return requestFocus(View.FOCUS_DOWN, null);
}
return super.performAccessibilityAction(host, action, args);
}
Expand Down Expand Up @@ -347,18 +347,8 @@ public void clearFocus() {

@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
// This is a no-op so that when the OS calls requestFocus(), nothing will happen. ReactEditText
// is a controlled component, which means its focus is controlled by JS, with two exceptions:
// autofocus when it's attached to the window, and responding to accessibility events. In both
// of these cases, we call requestFocusInternal() directly.
return isFocused();
}

private boolean requestFocusInternal() {
setFocusableInTouchMode(true);
// We must explicitly call this method on the super class; if we call requestFocus() without
// any arguments, it will call into the overridden requestFocus(int, Rect) above, which no-ops.
boolean focused = super.requestFocus(View.FOCUS_DOWN, null);
boolean focused = super.requestFocus(direction, previouslyFocusedRect);
if (getShowSoftInputOnFocus()) {
showSoftKeyboard();
}
Expand Down Expand Up @@ -634,7 +624,7 @@ public void maybeUpdateTypeface() {

// VisibleForTesting from {@link TextInputEventsTestCase}.
public void requestFocusFromJS() {
requestFocusInternal();
requestFocus(View.FOCUS_DOWN, null);
}

/* package */ void clearFocusFromJS() {
Expand Down Expand Up @@ -1087,7 +1077,7 @@ public void onAttachedToWindow() {
}

if (mAutoFocus && !mDidAttachToWindow) {
requestFocusInternal();
requestFocus(View.FOCUS_DOWN, null);
}

mDidAttachToWindow = true;
Expand Down

0 comments on commit fcc7559

Please sign in to comment.