Skip to content

Commit 1e6dec0

Browse files
joevilchesfacebook-github-bot
authored andcommitted
Remove useEditTextStockAndroidBehavior and resolve old android blur issue (#51338)
Summary: Pull Request resolved: #51338 There was some problems removing this feature flag earlier in that, on older android versions, we would try to focus the top most text input whenever any other text input would try to blur. This was ultimately and issue with how Android implements `clearFocus`. To fix this, lets block the focusability of all views while we clear the focus, then re-enable. Changelog: [Internal] Reviewed By: NickGerleman Differential Revision: D74760594
1 parent d6f20c4 commit 1e6dec0

23 files changed

+46
-194
lines changed

packages/react-native/ReactAndroid/api/ReactAndroid.api

-2
Original file line numberDiff line numberDiff line change
@@ -6516,7 +6516,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp
65166516
public fun addTextChangedListener (Landroid/text/TextWatcher;)V
65176517
protected final fun applyTextAttributes ()V
65186518
public final fun canUpdateWithEventCount (I)Z
6519-
public fun clearFocus ()V
65206519
protected final fun finalize ()V
65216520
public final fun getBorderColor (I)I
65226521
protected final fun getContainsImages ()Z
@@ -6553,7 +6552,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp
65536552
public fun onTextContextMenuItem (I)Z
65546553
public fun onTouchEvent (Landroid/view/MotionEvent;)Z
65556554
public fun removeTextChangedListener (Landroid/text/TextWatcher;)V
6556-
public fun requestFocus (ILandroid/graphics/Rect;)Z
65576555
public final fun requestFocusFromJS ()V
65586556
public final fun setAllowFontScaling (Z)V
65596557
public final fun setAutoFocus (Z)V

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

+1-7
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<<2c8ff840fe0597ee9f1845ec2e3fa629>>
7+
* @generated SignedSource<<e9a109fd77667dd0cb945ef6ef9737f2>>
88
*/
99

1010
/**
@@ -276,12 +276,6 @@ public object ReactNativeFeatureFlags {
276276
@JvmStatic
277277
public fun useAndroidTextLayoutWidthDirectly(): Boolean = accessor.useAndroidTextLayoutWidthDirectly()
278278

279-
/**
280-
* If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior.
281-
*/
282-
@JvmStatic
283-
public fun useEditTextStockAndroidFocusBehavior(): Boolean = accessor.useEditTextStockAndroidFocusBehavior()
284-
285279
/**
286280
* 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.
287281
*/

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

+1-11
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<<0a6e4183387e503158277e8b7e974bc5>>
7+
* @generated SignedSource<<1a5cd689229d4e0c31070123045e84da>>
88
*/
99

1010
/**
@@ -61,7 +61,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
6161
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
6262
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
6363
private var useAndroidTextLayoutWidthDirectlyCache: Boolean? = null
64-
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
6564
private var useFabricInteropCache: Boolean? = null
6665
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
6766
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
@@ -439,15 +438,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
439438
return cached
440439
}
441440

442-
override fun useEditTextStockAndroidFocusBehavior(): Boolean {
443-
var cached = useEditTextStockAndroidFocusBehaviorCache
444-
if (cached == null) {
445-
cached = ReactNativeFeatureFlagsCxxInterop.useEditTextStockAndroidFocusBehavior()
446-
useEditTextStockAndroidFocusBehaviorCache = cached
447-
}
448-
return cached
449-
}
450-
451441
override fun useFabricInterop(): Boolean {
452442
var cached = useFabricInteropCache
453443
if (cached == null) {

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

+1-3
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<<ef2a0c31e32310ba392680894467c2ca>>
7+
* @generated SignedSource<<c39d0c1834797b3309b4fc5ce814280c>>
88
*/
99

1010
/**
@@ -110,8 +110,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
110110

111111
@DoNotStrip @JvmStatic public external fun useAndroidTextLayoutWidthDirectly(): Boolean
112112

113-
@DoNotStrip @JvmStatic public external fun useEditTextStockAndroidFocusBehavior(): Boolean
114-
115113
@DoNotStrip @JvmStatic public external fun useFabricInterop(): Boolean
116114

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

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

+1-3
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<<c53f68e4ebac61f7a04133e6589adc1b>>
7+
* @generated SignedSource<<7f357475254104729cc7910c14e1c1fb>>
88
*/
99

1010
/**
@@ -105,8 +105,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
105105

106106
override fun useAndroidTextLayoutWidthDirectly(): Boolean = true
107107

108-
override fun useEditTextStockAndroidFocusBehavior(): Boolean = true
109-
110108
override fun useFabricInterop(): Boolean = true
111109

112110
override fun useNativeViewConfigsInBridgelessMode(): Boolean = false

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

+1-12
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<<9d94cb3f7378ef24346a66cb0da746e9>>
7+
* @generated SignedSource<<14cd1a58bd153dedda045a72c1494caa>>
88
*/
99

1010
/**
@@ -65,7 +65,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
6565
private var updateRuntimeShadowNodeReferencesOnCommitCache: Boolean? = null
6666
private var useAlwaysAvailableJSErrorHandlingCache: Boolean? = null
6767
private var useAndroidTextLayoutWidthDirectlyCache: Boolean? = null
68-
private var useEditTextStockAndroidFocusBehaviorCache: Boolean? = null
6968
private var useFabricInteropCache: Boolean? = null
7069
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
7170
private var useOptimizedEventBatchingOnAndroidCache: Boolean? = null
@@ -484,16 +483,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
484483
return cached
485484
}
486485

487-
override fun useEditTextStockAndroidFocusBehavior(): Boolean {
488-
var cached = useEditTextStockAndroidFocusBehaviorCache
489-
if (cached == null) {
490-
cached = currentProvider.useEditTextStockAndroidFocusBehavior()
491-
accessedFeatureFlags.add("useEditTextStockAndroidFocusBehavior")
492-
useEditTextStockAndroidFocusBehaviorCache = cached
493-
}
494-
return cached
495-
}
496-
497486
override fun useFabricInterop(): Boolean {
498487
var cached = useFabricInteropCache
499488
if (cached == null) {

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

+1-3
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<<85eb4d0a5e538bd028a4d33784fea741>>
7+
* @generated SignedSource<<593b1d64dc31038140032a6b0a439700>>
88
*/
99

1010
/**
@@ -105,8 +105,6 @@ public interface ReactNativeFeatureFlagsProvider {
105105

106106
@DoNotStrip public fun useAndroidTextLayoutWidthDirectly(): Boolean
107107

108-
@DoNotStrip public fun useEditTextStockAndroidFocusBehavior(): Boolean
109-
110108
@DoNotStrip public fun useFabricInterop(): Boolean
111109

112110
@DoNotStrip public fun useNativeViewConfigsInBridgelessMode(): Boolean

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

+18-48
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import android.view.Menu
3535
import android.view.MenuItem
3636
import android.view.MotionEvent
3737
import android.view.View
38+
import android.view.ViewGroup
3839
import android.view.accessibility.AccessibilityNodeInfo
3940
import android.view.inputmethod.EditorInfo
4041
import android.view.inputmethod.InputConnection
@@ -47,7 +48,6 @@ import com.facebook.react.bridge.ReactSoftExceptionLogger.logSoftException
4748
import com.facebook.react.common.ReactConstants
4849
import com.facebook.react.common.build.ReactBuildConfig
4950
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
50-
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags.useEditTextStockAndroidFocusBehavior
5151
import com.facebook.react.internal.featureflags.ReactNativeNewArchitectureFeatureFlags
5252
import com.facebook.react.uimanager.BackgroundStyleApplicator.clipToPaddingBox
5353
import com.facebook.react.uimanager.BackgroundStyleApplicator.getBackgroundColor
@@ -198,10 +198,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
198198
}
199199

200200
init {
201-
if (!useEditTextStockAndroidFocusBehavior()) {
202-
isFocusableInTouchMode = false
203-
}
204-
205201
inputMethodManager =
206202
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
207203
defaultGravityHorizontal =
@@ -240,8 +236,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
240236
// selection on accessibility click to undo that.
241237
setSelection(length)
242238
}
243-
return if (useEditTextStockAndroidFocusBehavior()) requestFocusProgrammatically()
244-
else requestFocusInternal()
239+
return requestFocusProgrammatically()
245240
}
246241
return super.performAccessibilityAction(host, action, args)
247242
}
@@ -365,37 +360,24 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
365360
super.onTextContextMenuItem(
366361
if (id == android.R.id.paste) android.R.id.pasteAsPlainText else id)
367362

368-
override fun clearFocus() {
369-
val useStockFocusBehavior = useEditTextStockAndroidFocusBehavior()
370-
if (!useStockFocusBehavior) {
371-
isFocusableInTouchMode = false
363+
internal fun clearFocusAndMaybeRefocus() {
364+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P || !isInTouchMode) {
365+
super.clearFocus()
366+
} else {
367+
// Avoid refocusing to a new view on old versions of Android by default
368+
// by preventing `requestFocus()` on the rootView from moving focus to any child.
369+
// https://cs.android.com/android/_/android/platform/frameworks/base/+/bdc66cb5a0ef513f4306edf9156cc978b08e06e4
370+
val rootViewGroup = rootView as ViewGroup
371+
val oldDescendantFocusability = rootViewGroup.descendantFocusability
372+
rootViewGroup.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS
373+
super.clearFocus()
374+
rootViewGroup.descendantFocusability = oldDescendantFocusability
372375
}
373-
super.clearFocus()
374376
hideSoftKeyboard()
375377
}
376378

377-
override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean =
378-
// This is a no-op so that when the OS calls requestFocus(), nothing will happen.
379-
// ReactEditText
380-
// is a controlled component, which means its focus is controlled by JS, with two exceptions:
381-
// autofocus when it's attached to the window, and responding to accessibility events. In both
382-
// of these cases, we call requestFocusInternal() directly.
383-
if (useEditTextStockAndroidFocusBehavior()) {
384-
super.requestFocus(direction, previouslyFocusedRect)
385-
} else {
386-
isFocused
387-
}
388-
389-
private fun requestFocusInternal(): Boolean {
390-
isFocusableInTouchMode = true
391-
// We must explicitly call this method on the super class; if we call requestFocus() without
392-
// any arguments, it will call into the overridden requestFocus(int, Rect) above, which no-ops.
393-
val focused = super.requestFocus(FOCUS_DOWN, null)
394-
if (showSoftInputOnFocus) {
395-
showSoftKeyboard()
396-
}
397-
398-
return focused
379+
internal fun clearFocusFromJS() {
380+
clearFocusAndMaybeRefocus()
399381
}
400382

401383
// For cases like autoFocus, or ref.focus() where we request focus programmatically and not
@@ -629,15 +611,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
629611
}
630612

631613
public fun requestFocusFromJS() {
632-
if (useEditTextStockAndroidFocusBehavior()) {
633-
requestFocusProgrammatically()
634-
} else {
635-
requestFocusInternal()
636-
}
637-
}
638-
639-
internal fun clearFocusFromJS() {
640-
clearFocus()
614+
requestFocusProgrammatically()
641615
}
642616

643617
public fun incrementAndGetEventCounter(): Int = ++nativeEventCount
@@ -983,11 +957,7 @@ public open class ReactEditText public constructor(context: Context) : AppCompat
983957
}
984958

985959
if (autoFocus && !didAttachToWindow) {
986-
if (useEditTextStockAndroidFocusBehavior()) {
987-
requestFocusProgrammatically()
988-
} else {
989-
requestFocusInternal()
990-
}
960+
requestFocusProgrammatically()
991961
}
992962

993963
didAttachToWindow = true

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,7 @@ public open class ReactTextInputManager public constructor() :
926926
}
927927

928928
if (shouldBlur) {
929-
editText.clearFocus()
929+
editText.clearFocusAndMaybeRefocus()
930930
}
931931

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

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

+1-15
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<<f7db897c6e1f59c494c8b7cebea7c942>>
7+
* @generated SignedSource<<d74dc8182a14fddbd7118149298515bd>>
88
*/
99

1010
/**
@@ -285,12 +285,6 @@ class ReactNativeFeatureFlagsJavaProvider
285285
return method(javaProvider_);
286286
}
287287

288-
bool useEditTextStockAndroidFocusBehavior() override {
289-
static const auto method =
290-
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useEditTextStockAndroidFocusBehavior");
291-
return method(javaProvider_);
292-
}
293-
294288
bool useFabricInterop() override {
295289
static const auto method =
296290
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("useFabricInterop");
@@ -542,11 +536,6 @@ bool JReactNativeFeatureFlagsCxxInterop::useAndroidTextLayoutWidthDirectly(
542536
return ReactNativeFeatureFlags::useAndroidTextLayoutWidthDirectly();
543537
}
544538

545-
bool JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior(
546-
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
547-
return ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior();
548-
}
549-
550539
bool JReactNativeFeatureFlagsCxxInterop::useFabricInterop(
551540
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
552541
return ReactNativeFeatureFlags::useFabricInterop();
@@ -736,9 +725,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
736725
makeNativeMethod(
737726
"useAndroidTextLayoutWidthDirectly",
738727
JReactNativeFeatureFlagsCxxInterop::useAndroidTextLayoutWidthDirectly),
739-
makeNativeMethod(
740-
"useEditTextStockAndroidFocusBehavior",
741-
JReactNativeFeatureFlagsCxxInterop::useEditTextStockAndroidFocusBehavior),
742728
makeNativeMethod(
743729
"useFabricInterop",
744730
JReactNativeFeatureFlagsCxxInterop::useFabricInterop),

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

+1-4
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<<69f0ab1339e848cb292cc6612e7776f3>>
7+
* @generated SignedSource<<a5ab0022f6a01bd6e929d36d9d87db10>>
88
*/
99

1010
/**
@@ -153,9 +153,6 @@ class JReactNativeFeatureFlagsCxxInterop
153153
static bool useAndroidTextLayoutWidthDirectly(
154154
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
155155

156-
static bool useEditTextStockAndroidFocusBehavior(
157-
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
158-
159156
static bool useFabricInterop(
160157
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
161158

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp

+1-5
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<<1a4a09ac92cba613c186ddfabcb14b31>>
7+
* @generated SignedSource<<88fdbea2f97f628187164a47a9737da0>>
88
*/
99

1010
/**
@@ -190,10 +190,6 @@ bool ReactNativeFeatureFlags::useAndroidTextLayoutWidthDirectly() {
190190
return getAccessor().useAndroidTextLayoutWidthDirectly();
191191
}
192192

193-
bool ReactNativeFeatureFlags::useEditTextStockAndroidFocusBehavior() {
194-
return getAccessor().useEditTextStockAndroidFocusBehavior();
195-
}
196-
197193
bool ReactNativeFeatureFlags::useFabricInterop() {
198194
return getAccessor().useFabricInterop();
199195
}

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.h

+1-6
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<<3e716502e96c4b39f70df4beca076acf>>
7+
* @generated SignedSource<<a4123bc6f44835c022a1a5238908674c>>
88
*/
99

1010
/**
@@ -244,11 +244,6 @@ class ReactNativeFeatureFlags {
244244
*/
245245
RN_EXPORT static bool useAndroidTextLayoutWidthDirectly();
246246

247-
/**
248-
* If true, focusing in ReactEditText will mainly use stock Android requestFocus() behavior. If false it will use legacy custom focus behavior.
249-
*/
250-
RN_EXPORT static bool useEditTextStockAndroidFocusBehavior();
251-
252247
/**
253248
* 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.
254249
*/

0 commit comments

Comments
 (0)