Skip to content

Commit

Permalink
Allow text inputs to handle native requestFocus calls
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 8, 2025
1 parent dc5535c commit 0edf74c
Showing 1 changed file with 5 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -347,24 +347,18 @@ 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();
}
return focused;
}

public boolean requestFocusInternal() {
return requestFocus(View.FOCUS_DOWN, null);
}

@Override
public void addTextChangedListener(TextWatcher watcher) {
if (mListeners == null) {
Expand Down

0 comments on commit 0edf74c

Please sign in to comment.