Skip to content

Commit 81ed5e1

Browse files
committed
Revert "[0.79] Fix broken focus behavior on TextInput in older Android versions"
This reverts commit 1e0bbb8.
1 parent 69349eb commit 81ed5e1

22 files changed

+180
-47
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<fa641112b3a8888ba2b24cf8829e9382>>
7+
* @generated SignedSource<<b263bdcbc1258f7d5c56e69732ba9076>>
88
*/
99

1010
/**
@@ -250,6 +250,12 @@ public object ReactNativeFeatureFlags {
250250
@JvmStatic
251251
public fun useAlwaysAvailableJSErrorHandling(): Boolean = accessor.useAlwaysAvailableJSErrorHandling()
252252

253+
/**
254+
* If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior.
255+
*/
256+
@JvmStatic
257+
public fun useEditTextStockAndroidFocusBehavior(): Boolean = accessor.useEditTextStockAndroidFocusBehavior()
258+
253259
/**
254260
* Should this application enable the Fabric Interop Layer for Android? If yes, the application will behave so that it can accept non-Fabric components and render them on Fabric. This toggle is controlling extra logic such as custom event dispatching that are needed for the Fabric Interop Layer to work correctly.
255261
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9ecb711480b7d6c22bac380c28d035bc>>
7+
* @generated SignedSource<<0496ecf3d1e5d8a2e6d4d594aca806d0>>
88
*/
99

1010
/**
@@ -57,6 +57,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
5757
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
5858
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
5959
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
60+
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
6061
private var useFabricInteropCache: Boolean? = null
6162
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
6263
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
@@ -398,6 +399,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
398399
return cached
399400
}
400401

402+
override fun useEditTextStockAndroidFocusBehavior(): Boolean {
403+
var cached = useEditTextStockAndroidFocusBehaviorCache
404+
if (cached == null) {
405+
cached = ReactNativeFeatureFlagsCxxInterop.useEditTextStockAndroidFocusBehavior()
406+
useEditTextStockAndroidFocusBehaviorCache = cached
407+
}
408+
return cached
409+
}
410+
401411
override fun useFabricInterop(): Boolean {
402412
var cached = useFabricInteropCache
403413
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<2151e5ec5d04924e742f37b527dc23b9>>
7+
* @generated SignedSource<<c4f3b0cee8b9b4b9cebb589801e1dd15>>
88
*/
99

1010
/**
@@ -102,6 +102,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
102102

103103
@DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean
104104

105+
@DoNotStrip @JvmStatic public external fun useEditTextStockAndroidFocusBehavior(): Boolean
106+
105107
@DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean
106108

107109
@DoNotStrip @JvmStatic public external fun useNativeViewConfigsInBridgelessMode(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<56f86a3a0c0bbf453cf45a0db541ef54>>
7+
* @generated SignedSource<<a8900217ae0385947b619c8fa0834942>>
88
*/
99

1010
/**
@@ -97,6 +97,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
9797

9898
override fun useAlwaysAvailableJSErrorHandling(): Boolean = false
9999

100+
override fun useEditTextStockAndroidFocusBehavior(): Boolean = true
101+
100102
override fun useFabricInterop(): Boolean = false
101103

102104
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<5b016fd6298477856116736e37c37c6f>>
7+
* @generated SignedSource<<8f5180a0ef154c083ac38d28e650ee11>>
88
*/
99

1010
/**
@@ -61,6 +61,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
6161
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
6262
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
6363
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
64+
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
6465
private var useFabricInteropCache: Boolean? = null
6566
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
6667
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
@@ -439,6 +440,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
439440
return cached
440441
}
441442

443+
override fun useEditTextStockAndroidFocusBehavior(): Boolean {
444+
var cached = useEditTextStockAndroidFocusBehaviorCache
445+
if (cached == null) {
446+
cached = currentProvider.useEditTextStockAndroidFocusBehavior()
447+
accessedFeatureFlags.add("useEditTextStockAndroidFocusBehavior")
448+
useEditTextStockAndroidFocusBehaviorCache = cached
449+
}
450+
return cached
451+
}
452+
442453
override fun useFabricInterop(): Boolean {
443454
var cached = useFabricInteropCache
444455
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<b4d6157922f6182dd588d5ae5b54ead9>>
7+
* @generated SignedSource<<33571f99b1f78fbc62cecfca5f8351fa>>
88
*/
99

1010
/**
@@ -97,6 +97,8 @@ public interface ReactNativeFeatureFlagsProvider {
9797

9898
@DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean
9999

100+
@DoNotStrip public fun useEditTextStockAndroidFocusBehavior(): Boolean
101+
100102
@DoNotStrip public fun useFabricInterop(): Boolean
101103

102104
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import android.view.MenuItem;
3737
import android.view.MotionEvent;
3838
import android.view.View;
39-
import android.view.ViewGroup;
4039
import android.view.accessibility.AccessibilityNodeInfo;
4140
import android.view.inputmethod.EditorInfo;
4241
import android.view.inputmethod.InputConnection;
@@ -147,6 +146,9 @@ public class ReactEditText extends AppCompatEditText {
147146

148147
public ReactEditText(Context context) {
149148
super(context);
149+
if (!ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()) {
150+
setFocusableInTouchMode(false);
151+
}
150152

151153
mInputMethodManager =
152154
(InputMethodManager)
@@ -189,7 +191,9 @@ public boolean performAccessibilityAction(View host, int action, Bundle args) {
189191
// selection on accessibility click to undo that.
190192
setSelection(length);
191193
}
192-
return requestFocusProgramatically();
194+
return ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()
195+
? requestFocusProgramatically()
196+
: requestFocusInternal();
193197
}
194198
return super.performAccessibilityAction(host, action, args);
195199
}
@@ -337,28 +341,29 @@ public boolean onTextContextMenuItem(int id) {
337341
return super.onTextContextMenuItem(id);
338342
}
339343

340-
public void clearFocusAndMaybeRefocus() {
341-
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P || !isInTouchMode()) {
342-
super.clearFocus();
343-
} else {
344-
// Avoid refocusing to a new view on old versions of Android by default
345-
// by preventing `requestFocus()` on the rootView from moving focus to any child.
346-
// https://cs.android.com/android/_/android/platform/frameworks/base/+/bdc66cb5a0ef513f4306edf9156cc978b08e06e4
347-
ViewGroup rootViewGroup = (ViewGroup)getRootView();
348-
int oldDescendantFocusability = rootViewGroup.getDescendantFocusability();
349-
rootViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
350-
super.clearFocus();
351-
rootViewGroup.setDescendantFocusability(oldDescendantFocusability);
344+
@Override
345+
public void clearFocus() {
346+
boolean useStockFocusBehavior = ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior();
347+
if (!useStockFocusBehavior) {
348+
setFocusableInTouchMode(false);
352349
}
353-
350+
super.clearFocus();
354351
hideSoftKeyboard();
355352
}
356353

357-
/* package */ void clearFocusFromJS() {
358-
clearFocusAndMaybeRefocus();
354+
@Override
355+
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
356+
// This is a no-op so that when the OS calls requestFocus(), nothing will happen. ReactEditText
357+
// is a controlled component, which means its focus is controlled by JS, with two exceptions:
358+
// autofocus when it's attached to the window, and responding to accessibility events. In both
359+
// of these cases, we call requestFocusInternal() directly.
360+
return ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()
361+
? super.requestFocus(direction, previouslyFocusedRect)
362+
: isFocused();
359363
}
360364

361365
private boolean requestFocusInternal() {
366+
setFocusableInTouchMode(true);
362367
// We must explicitly call this method on the super class; if we call requestFocus() without
363368
// any arguments, it will call into the overridden requestFocus(int, Rect) above, which no-ops.
364369
boolean focused = super.requestFocus(View.FOCUS_DOWN, null);
@@ -651,7 +656,15 @@ public void maybeUpdateTypeface() {
651656

652657
// VisibleForTesting from {@link TextInputEventsTestCase}.
653658
public void requestFocusFromJS() {
654-
requestFocusProgramatically();
659+
if (ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()) {
660+
requestFocusProgramatically();
661+
} else {
662+
requestFocusInternal();
663+
}
664+
}
665+
666+
/* package */ void clearFocusFromJS() {
667+
clearFocus();
655668
}
656669

657670
// VisibleForTesting from {@link TextInputEventsTestCase}.
@@ -1094,7 +1107,11 @@ public void onAttachedToWindow() {
10941107
}
10951108

10961109
if (mAutoFocus && !mDidAttachToWindow) {
1097-
requestFocusProgramatically();
1110+
if (ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()) {
1111+
requestFocusProgramatically();
1112+
} else {
1113+
requestFocusInternal();
1114+
}
10981115
}
10991116

11001117
mDidAttachToWindow = true;

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ protected void addEventEmitters(
11641164
}
11651165

11661166
if (shouldBlur) {
1167-
editText.clearFocusAndMaybeRefocus();
1167+
editText.clearFocus();
11681168
}
11691169

11701170
// Prevent default behavior except when we want it to insert a newline.

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<cf0734c38bab916ecaf361bc557b8802>>
7+
* @generated SignedSource<<83b23039ed9fff5109ff3b532648baac>>
88
*/
99

1010
/**
@@ -261,6 +261,12 @@ class ReactNativeFeatureFlagsProviderHolder
261261
return method(javaProvider_);
262262
}
263263

264+
bool useEditTextStockAndroidFocusBehavior() override {
265+
static const auto method =
266+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useEditTextStockAndroidFocusBehavior");
267+
return method(javaProvider_);
268+
}
269+
264270
bool useFabricInterop() override {
265271
static const auto method =
266272
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useFabricInterop");
@@ -492,6 +498,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling(
492498
return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling();
493499
}
494500

501+
bool JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior(
502+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
503+
return ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior();
504+
}
505+
495506
bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop(
496507
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
497508
return ReactNativeFeatureFlags::useFabricInterop();
@@ -669,6 +680,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
669680
makeNativeMethod(
670681
"useAlwaysAvailableJSErrorHandling",
671682
JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling),
683+
makeNativeMethod(
684+
"useEditTextStockAndroidFocusBehavior",
685+
JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior),
672686
makeNativeMethod(
673687
"useFabricInterop",
674688
JReactNativeFeatureFlagsCxxInterop::useFabricInterop),

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<f295d109e9a81ce2d2040a5fc89e9ada>>
7+
* @generated SignedSource<<efb0288fd19fb35e4582522c835301b4>>
88
*/
99

1010
/**
@@ -141,6 +141,9 @@ class JReactNativeFeatureFlagsCxxInterop
141141
static bool useAlwaysAvailableJSErrorHandling(
142142
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
143143

144+
static bool useEditTextStockAndroidFocusBehavior(
145+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
146+
144147
static bool useFabricInterop(
145148
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
146149

0 commit comments

Comments
 (0)