From c46768676680284cdbe06d64cb0ff028c7fdf74b Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Mon, 13 Jan 2025 16:13:33 -0800 Subject: [PATCH] Allow text inputs to handle native requestFocus calls (#48547) 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 --- .../featureflags/ReactNativeFeatureFlags.kt | 8 +++- .../ReactNativeFeatureFlagsCxxAccessor.kt | 12 +++++- .../ReactNativeFeatureFlagsCxxInterop.kt | 4 +- .../ReactNativeFeatureFlagsDefaults.kt | 4 +- .../ReactNativeFeatureFlagsLocalAccessor.kt | 13 ++++++- .../ReactNativeFeatureFlagsProvider.kt | 4 +- .../react/views/textinput/ReactEditText.java | 32 ++++++++++++---- .../JReactNativeFeatureFlagsCxxInterop.cpp | 16 +++++++- .../JReactNativeFeatureFlagsCxxInterop.h | 5 ++- .../featureflags/ReactNativeFeatureFlags.cpp | 6 ++- .../featureflags/ReactNativeFeatureFlags.h | 7 +++- .../ReactNativeFeatureFlagsAccessor.cpp | 38 ++++++++++++++----- .../ReactNativeFeatureFlagsAccessor.h | 6 ++- .../ReactNativeFeatureFlagsDefaults.h | 6 ++- .../ReactNativeFeatureFlagsDynamicProvider.h | 11 +++++- .../ReactNativeFeatureFlagsProvider.h | 3 +- .../NativeReactNativeFeatureFlags.cpp | 7 +++- .../NativeReactNativeFeatureFlags.h | 4 +- .../ReactNativeFeatureFlags.config.js | 9 +++++ .../featureflags/ReactNativeFeatureFlags.js | 7 +++- .../specs/NativeReactNativeFeatureFlags.js | 3 +- 21 files changed, 168 insertions(+), 37 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt index 53c4bd6c186d09..ee749afde3cbeb 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3e10f8d2f623da3b7b502d8fa78f82a4>> + * @generated SignedSource<<2c321a7a8e811dc238a75f76180843b9>> */ /** @@ -244,6 +244,12 @@ public object ReactNativeFeatureFlags { @JvmStatic public fun useAlwaysAvailableJSErrorHandling(): Boolean = accessor.useAlwaysAvailableJSErrorHandling() + /** + * If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior. + */ + @JvmStatic + public fun useEditTextStockAndroidFocusBehavior(): Boolean = accessor.useEditTextStockAndroidFocusBehavior() + /** * 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. */ diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt index c25a7ad0a77d46..3a7fbd9185358c 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<497bbb23778fe0f9763e9bfa715ea3aa>> + * @generated SignedSource<> */ /** @@ -56,6 +56,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso private var loadVectorDrawablesOnImagesCache: Boolean? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null + private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null private var useFabricInteropCache: Boolean? = null private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null @@ -390,6 +391,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso return cached } + override fun useEditTextStockAndroidFocusBehavior(): Boolean { + var cached = useEditTextStockAndroidFocusBehaviorCache + if (cached == null) { + cached = ReactNativeFeatureFlagsCxxInterop.useEditTextStockAndroidFocusBehavior() + useEditTextStockAndroidFocusBehaviorCache = cached + } + return cached + } + override fun useFabricInterop(): Boolean { var cached = useFabricInteropCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt index 181c06d9c7cb03..d29a6979cabd9b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -100,6 +100,8 @@ public object ReactNativeFeatureFlagsCxxInterop { @DoNotStrip @JvmStatic public external fun useAlwaysAvailableJSErrorHandling(): Boolean + @DoNotStrip @JvmStatic public external fun useEditTextStockAndroidFocusBehavior(): Boolean + @DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean @DoNotStrip @JvmStatic public external fun useImmediateExecutorInAndroidBridgeless(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt index ca2265e22368bb..882730b504259b 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<5de2cfc00f486b7d07266939ce18a397>> + * @generated SignedSource<<85d40be44d053b58a74ad76467c8e5e9>> */ /** @@ -95,6 +95,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi override fun useAlwaysAvailableJSErrorHandling(): Boolean = false + override fun useEditTextStockAndroidFocusBehavior(): Boolean = true + override fun useFabricInterop(): Boolean = false override fun useImmediateExecutorInAndroidBridgeless(): Boolean = true diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt index 0200670573d136..f08720d444f56d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<18d5c2ffa66a36e364bc358a144534ec>> */ /** @@ -60,6 +60,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces private var loadVectorDrawablesOnImagesCache: Boolean? = null private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null + private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null private var useFabricInteropCache: Boolean? = null private var useImmediateExecutorInAndroidBridgelessCache: Boolean? = null private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null @@ -430,6 +431,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces return cached } + override fun useEditTextStockAndroidFocusBehavior(): Boolean { + var cached = useEditTextStockAndroidFocusBehaviorCache + if (cached == null) { + cached = currentProvider.useEditTextStockAndroidFocusBehavior() + accessedFeatureFlags.add("useEditTextStockAndroidFocusBehavior") + useEditTextStockAndroidFocusBehaviorCache = cached + } + return cached + } + override fun useFabricInterop(): Boolean { var cached = useFabricInteropCache if (cached == null) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt index e3fb3270f52fce..cbff3e1f929001 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3cd802bdd1d383ea0668e43319d53b3f>> + * @generated SignedSource<<4f90c47ea6f23c2ebfae1f35790c4af5>> */ /** @@ -95,6 +95,8 @@ public interface ReactNativeFeatureFlagsProvider { @DoNotStrip public fun useAlwaysAvailableJSErrorHandling(): Boolean + @DoNotStrip public fun useEditTextStockAndroidFocusBehavior(): Boolean + @DoNotStrip public fun useFabricInterop(): Boolean @DoNotStrip public fun useImmediateExecutorInAndroidBridgeless(): Boolean diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java index a0370775f5c288..becebec3dac12e 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.java @@ -50,6 +50,7 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.build.ReactBuildConfig; +import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags; import com.facebook.react.uimanager.BackgroundStyleApplicator; import com.facebook.react.uimanager.LengthPercentage; import com.facebook.react.uimanager.LengthPercentageType; @@ -145,7 +146,9 @@ public class ReactEditText extends AppCompatEditText { public ReactEditText(Context context) { super(context); - setFocusableInTouchMode(false); + if (!ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()) { + setFocusableInTouchMode(false); + } mInputMethodManager = (InputMethodManager) @@ -338,7 +341,9 @@ public boolean onTextContextMenuItem(int id) { @Override public void clearFocus() { - setFocusableInTouchMode(false); + if (!ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior()) { + setFocusableInTouchMode(false); + } super.clearFocus(); hideSoftKeyboard(); } @@ -349,17 +354,28 @@ public boolean requestFocus(int direction, Rect previouslyFocusedRect) { // 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(); + return ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior() + ? super.requestFocus(direction, previouslyFocusedRect) + : isFocused(); } + // For cases like autoFocus, or ref.focus() where we request focus programatically and not through + // interacting with the EditText directly (like clicking on it). We cannot use stock + // requestFocus() because it will not pop up the soft keyboard, only clicking the input will do + // that. 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 useStockFocusBehavior = ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior(); + if (!useStockFocusBehavior) { + setFocusableInTouchMode(true); + } + boolean focused = super.requestFocus(View.FOCUS_DOWN, null); - if (getShowSoftInputOnFocus()) { - showSoftKeyboard(); + if (useStockFocusBehavior && isInTouchMode() || !useStockFocusBehavior) { + if (getShowSoftInputOnFocus()) { + showSoftKeyboard(); + } } + return focused; } diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp index d3d0dc3ea1e040..97e7af5ae37ade 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7c3853858da56eb5f471abccf9dcbf55>> + * @generated SignedSource<<63ebe186ccd5ee30fc654aa8d90f27f9>> */ /** @@ -255,6 +255,12 @@ class ReactNativeFeatureFlagsProviderHolder return method(javaProvider_); } + bool useEditTextStockAndroidFocusBehavior() override { + static const auto method = + getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useEditTextStockAndroidFocusBehavior"); + return method(javaProvider_); + } + bool useFabricInterop() override { static const auto method = getReactNativeFeatureFlagsProviderJavaClass()->getMethod("useFabricInterop"); @@ -493,6 +499,11 @@ bool JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling( return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling(); } +bool JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior( + facebook::jni::alias_ref /*unused*/) { + return ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior(); +} + bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop( facebook::jni::alias_ref /*unused*/) { return ReactNativeFeatureFlags::useFabricInterop(); @@ -677,6 +688,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() { makeNativeMethod( "useAlwaysAvailableJSErrorHandling", JReactNativeFeatureFlagsCxxInterop::useAlwaysAvailableJSErrorHandling), + makeNativeMethod( + "useEditTextStockAndroidFocusBehavior", + JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior), makeNativeMethod( "useFabricInterop", JReactNativeFeatureFlagsCxxInterop::useFabricInterop), diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h index 80ef331dff8352..f8631b2e947bc4 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<77b4ed5aa33290ba9da1719544e974cb>> + * @generated SignedSource<> */ /** @@ -138,6 +138,9 @@ class JReactNativeFeatureFlagsCxxInterop static bool useAlwaysAvailableJSErrorHandling( facebook::jni::alias_ref); + static bool useEditTextStockAndroidFocusBehavior( + facebook::jni::alias_ref); + static bool useFabricInterop( facebook::jni::alias_ref); diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp index f71795970ae0c7..3a27b0604c156c 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7d301656072183649246db8fa738fc4d>> + * @generated SignedSource<<58c69846193598ce0c3666c37b8a185f>> */ /** @@ -170,6 +170,10 @@ bool ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling() { return getAccessor().useAlwaysAvailableJSErrorHandling(); } +bool ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior() { + return getAccessor().useEditTextStockAndroidFocusBehavior(); +} + bool ReactNativeFeatureFlags::useFabricInterop() { return getAccessor().useFabricInterop(); } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h index 4d075604aa7da4..68b5c50f09744b 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<1d578508c3cd69bbf9616a811508a03e>> */ /** @@ -219,6 +219,11 @@ class ReactNativeFeatureFlags { */ RN_EXPORT static bool useAlwaysAvailableJSErrorHandling(); + /** + * If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior. + */ + RN_EXPORT static bool useEditTextStockAndroidFocusBehavior(); + /** * 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. */ diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp index 4e528eff110880..8d1253b74d1bee 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<42e2ae5cbfb15c17a7d60a771128858a>> */ /** @@ -677,6 +677,24 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() { return flagValue.value(); } +bool ReactNativeFeatureFlagsAccessor::useEditTextStockAndroidFocusBehavior() { + auto flagValue = useEditTextStockAndroidFocusBehavior_.load(); + + if (!flagValue.has_value()) { + // This block is not exclusive but it is not necessary. + // If multiple threads try to initialize the feature flag, we would only + // be accessing the provider multiple times but the end state of this + // instance and the returned flag value would be the same. + + markFlagAsAccessed(36, "useEditTextStockAndroidFocusBehavior"); + + flagValue = currentProvider_->useEditTextStockAndroidFocusBehavior(); + useEditTextStockAndroidFocusBehavior_ = flagValue; + } + + return flagValue.value(); +} + bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { auto flagValue = useFabricInterop_.load(); @@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(36, "useFabricInterop"); + markFlagAsAccessed(37, "useFabricInterop"); flagValue = currentProvider_->useFabricInterop(); useFabricInterop_ = flagValue; @@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless() // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(37, "useImmediateExecutorInAndroidBridgeless"); + markFlagAsAccessed(38, "useImmediateExecutorInAndroidBridgeless"); flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless(); useImmediateExecutorInAndroidBridgeless_ = flagValue; @@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(38, "useNativeViewConfigsInBridgelessMode"); + markFlagAsAccessed(39, "useNativeViewConfigsInBridgelessMode"); flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode(); useNativeViewConfigsInBridgelessMode_ = flagValue; @@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(39, "useOptimisedViewPreallocationOnAndroid"); + markFlagAsAccessed(40, "useOptimisedViewPreallocationOnAndroid"); flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid(); useOptimisedViewPreallocationOnAndroid_ = flagValue; @@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(40, "useOptimizedEventBatchingOnAndroid"); + markFlagAsAccessed(41, "useOptimizedEventBatchingOnAndroid"); flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid(); useOptimizedEventBatchingOnAndroid_ = flagValue; @@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useRawPropsJsiValue() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(41, "useRawPropsJsiValue"); + markFlagAsAccessed(42, "useRawPropsJsiValue"); flagValue = currentProvider_->useRawPropsJsiValue(); useRawPropsJsiValue_ = flagValue; @@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(42, "useRuntimeShadowNodeReferenceUpdate"); + markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdate"); flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate(); useRuntimeShadowNodeReferenceUpdate_ = flagValue; @@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(43, "useTurboModuleInterop"); + markFlagAsAccessed(44, "useTurboModuleInterop"); flagValue = currentProvider_->useTurboModuleInterop(); useTurboModuleInterop_ = flagValue; @@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() { // be accessing the provider multiple times but the end state of this // instance and the returned flag value would be the same. - markFlagAsAccessed(44, "useTurboModules"); + markFlagAsAccessed(45, "useTurboModules"); flagValue = currentProvider_->useTurboModules(); useTurboModules_ = flagValue; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h index ac0c3e53a1dbd7..3c0468e8e04fa0 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsAccessor.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<8eb2b5d6dd367826ff7bc899afbdea60>> + * @generated SignedSource<<8423497c73f5315fdc3e8b4fa0bdc8c2>> */ /** @@ -68,6 +68,7 @@ class ReactNativeFeatureFlagsAccessor { bool loadVectorDrawablesOnImages(); bool traceTurboModulePromiseRejectionsOnAndroid(); bool useAlwaysAvailableJSErrorHandling(); + bool useEditTextStockAndroidFocusBehavior(); bool useFabricInterop(); bool useImmediateExecutorInAndroidBridgeless(); bool useNativeViewConfigsInBridgelessMode(); @@ -88,7 +89,7 @@ class ReactNativeFeatureFlagsAccessor { std::unique_ptr currentProvider_; bool wasOverridden_; - std::array, 45> accessedFeatureFlags_; + std::array, 46> accessedFeatureFlags_; std::atomic> commonTestFlag_; std::atomic> completeReactInstanceCreationOnBgThreadOnAndroid_; @@ -126,6 +127,7 @@ class ReactNativeFeatureFlagsAccessor { std::atomic> loadVectorDrawablesOnImages_; std::atomic> traceTurboModulePromiseRejectionsOnAndroid_; std::atomic> useAlwaysAvailableJSErrorHandling_; + std::atomic> useEditTextStockAndroidFocusBehavior_; std::atomic> useFabricInterop_; std::atomic> useImmediateExecutorInAndroidBridgeless_; std::atomic> useNativeViewConfigsInBridgelessMode_; diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h index aec774f80d20dc..8f0a2fabb42aac 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> */ /** @@ -171,6 +171,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider { return false; } + bool useEditTextStockAndroidFocusBehavior() override { + return true; + } + bool useFabricInterop() override { return false; } diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h index 5a6b8a82ae33f2..7fd97a9b561519 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDynamicProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<179524938d2e6148ef87835844c4135e>> */ /** @@ -369,6 +369,15 @@ class ReactNativeFeatureFlagsDynamicProvider : public ReactNativeFeatureFlagsDef return ReactNativeFeatureFlagsDefaults::useAlwaysAvailableJSErrorHandling(); } + bool useEditTextStockAndroidFocusBehavior() override { + auto value = values_["useEditTextStockAndroidFocusBehavior"]; + if (!value.isNull()) { + return value.getBool(); + } + + return ReactNativeFeatureFlagsDefaults::useEditTextStockAndroidFocusBehavior(); + } + bool useFabricInterop() override { auto value = values_["useFabricInterop"]; if (!value.isNull()) { diff --git a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h index c89d65c0d88b54..9975fc02014384 100644 --- a/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h +++ b/packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsProvider.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<36bfe0037e4dba3b6eb6e95075914fce>> + * @generated SignedSource<> */ /** @@ -61,6 +61,7 @@ class ReactNativeFeatureFlagsProvider { virtual bool loadVectorDrawablesOnImages() = 0; virtual bool traceTurboModulePromiseRejectionsOnAndroid() = 0; virtual bool useAlwaysAvailableJSErrorHandling() = 0; + virtual bool useEditTextStockAndroidFocusBehavior() = 0; virtual bool useFabricInterop() = 0; virtual bool useImmediateExecutorInAndroidBridgeless() = 0; virtual bool useNativeViewConfigsInBridgelessMode() = 0; diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp index f326fcd073b5f8..f8108b98f97470 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.cpp @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<81f3e20643ddb2612eaa9276bb75e70e>> */ /** @@ -224,6 +224,11 @@ bool NativeReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling( return ReactNativeFeatureFlags::useAlwaysAvailableJSErrorHandling(); } +bool NativeReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior( + jsi::Runtime& /*runtime*/) { + return ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior(); +} + bool NativeReactNativeFeatureFlags::useFabricInterop( jsi::Runtime& /*runtime*/) { return ReactNativeFeatureFlags::useFabricInterop(); diff --git a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h index 7f4364d355d522..b96b81b87853ef 100644 --- a/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h +++ b/packages/react-native/ReactCommon/react/nativemodule/featureflags/NativeReactNativeFeatureFlags.h @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<0852372c6b328f128319949a01aa8787>> */ /** @@ -109,6 +109,8 @@ class NativeReactNativeFeatureFlags bool useAlwaysAvailableJSErrorHandling(jsi::Runtime& runtime); + bool useEditTextStockAndroidFocusBehavior(jsi::Runtime& runtime); + bool useFabricInterop(jsi::Runtime& runtime); bool useImmediateExecutorInAndroidBridgeless(jsi::Runtime& runtime); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 744e112311a77e..eeef99ff35e039 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -390,6 +390,15 @@ const definitions: FeatureFlagDefinitions = { purpose: 'release', }, }, + useEditTextStockAndroidFocusBehavior: { + defaultValue: true, + metadata: { + description: + 'If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior.', + expectedReleaseValue: true, + purpose: 'release', + }, + }, useFabricInterop: { defaultValue: false, metadata: { diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index cebed3c31eafc8..cc60ebbcd6528b 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<3037cf1c938dae492b656333cec9633c>> + * @generated SignedSource<> * @flow strict */ @@ -87,6 +87,7 @@ export type ReactNativeFeatureFlags = $ReadOnly<{ loadVectorDrawablesOnImages: Getter, traceTurboModulePromiseRejectionsOnAndroid: Getter, useAlwaysAvailableJSErrorHandling: Getter, + useEditTextStockAndroidFocusBehavior: Getter, useFabricInterop: Getter, useImmediateExecutorInAndroidBridgeless: Getter, useNativeViewConfigsInBridgelessMode: Getter, @@ -331,6 +332,10 @@ export const traceTurboModulePromiseRejectionsOnAndroid: Getter = creat * In Bridgeless mode, use the always available javascript error reporting pipeline. */ export const useAlwaysAvailableJSErrorHandling: Getter = createNativeFlagGetter('useAlwaysAvailableJSErrorHandling', false); +/** + * If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior. + */ +export const useEditTextStockAndroidFocusBehavior: Getter = createNativeFlagGetter('useEditTextStockAndroidFocusBehavior', true); /** * 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. */ diff --git a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js index f9f4a5bc187baf..5a0c476acbb924 100644 --- a/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/specs/NativeReactNativeFeatureFlags.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<37e95652ef5d824bb05e78ebdb051e43>> + * @generated SignedSource<<9fd249e6c81d4aaa9793c003d986524c>> * @flow strict */ @@ -60,6 +60,7 @@ export interface Spec extends TurboModule { +loadVectorDrawablesOnImages?: () => boolean; +traceTurboModulePromiseRejectionsOnAndroid?: () => boolean; +useAlwaysAvailableJSErrorHandling?: () => boolean; + +useEditTextStockAndroidFocusBehavior?: () => boolean; +useFabricInterop?: () => boolean; +useImmediateExecutorInAndroidBridgeless?: () => boolean; +useNativeViewConfigsInBridgelessMode?: () => boolean;