diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index a932949100b68c..90f629a40b6ed1 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -6516,7 +6516,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public fun addTextChangedListener (Landroid/text/TextWatcher;)V protected final fun applyTextAttributes ()V public final fun canUpdateWithEventCount (I)Z - public fun clearFocus ()V protected final fun finalize ()V public final fun getBorderColor (I)I protected final fun getContainsImages ()Z @@ -6553,7 +6552,6 @@ public class com/facebook/react/views/textinput/ReactEditText : androidx/appcomp public fun onTextContextMenuItem (I)Z public fun onTouchEvent (Landroid/view/MotionEvent;)Z public fun removeTextChangedListener (Landroid/text/TextWatcher;)V - public fun requestFocus (ILandroid/graphics/Rect;)Z public final fun requestFocusFromJS ()V public final fun setAllowFontScaling (Z)V public final fun setAutoFocus (Z)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt index 358306a829f089..318a7720298127 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt @@ -35,6 +35,7 @@ import android.view.Menu import android.view.MenuItem import android.view.MotionEvent import android.view.View +import android.view.ViewGroup import android.view.accessibility.AccessibilityNodeInfo import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputConnection @@ -359,28 +360,29 @@ public open class ReactEditText public constructor(context: Context) : AppCompat super.onTextContextMenuItem( if (id == android.R.id.paste) android.R.id.pasteAsPlainText else id) - override fun clearFocus() { - super.clearFocus() + internal fun clearFocusAndMaybeRefocus() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P || !isInTouchMode) { + super.clearFocus() + } else { + // Avoid refocusing to a new view on old versions of Android by default + // by preventing `requestFocus()` on the rootView from moving focus to any child. + // https://cs.android.com/android/_/android/platform/frameworks/base/+/bdc66cb5a0ef513f4306edf9156cc978b08e06e4 + val rootViewGroup = rootView as ViewGroup + val oldDescendantFocusability = rootViewGroup.descendantFocusability + rootViewGroup.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS + super.clearFocus() + rootViewGroup.descendantFocusability = oldDescendantFocusability + } hideSoftKeyboard() } - override fun requestFocus(direction: Int, previouslyFocusedRect: Rect?): Boolean { - // On some older versions of Android there is a bug where `clearFocus` will try to focus the - // first focusable View in the hierarchy after clearing focus. This is intended behavior, but - // only if you are not in touch mode per - // https://developer.android.com/reference/android/view/View#clearFocus(), yet this happens in - // both. Therefore, we are swallowing Android-based focus calls if we are in touch mode. - // If we are not in touch mode (using a hardware keyboard) then we will allow this to happen. - // Note this only happens for Android-origin focus calls, as opposed to JS-origin (like tapping) - // since those go through `requestFocusProgrammatically` - if (isInTouchMode) { - return isFocused - } - return super.requestFocus(direction, previouslyFocusedRect) + internal fun clearFocusFromJS() { + clearFocusAndMaybeRefocus() } // For cases like autoFocus, or ref.focus() where we request focus programmatically and not - // through interacting with the EditText directly (like clicking on it). We cannot use stock + // 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. This method will eventually replace requestFocusInternal() private fun requestFocusProgrammatically(): Boolean { @@ -612,10 +614,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat requestFocusProgrammatically() } - internal fun clearFocusFromJS() { - clearFocus() - } - public fun incrementAndGetEventCounter(): Int = ++nativeEventCount public fun maybeSetTextFromJS(reactTextUpdate: ReactTextUpdate) { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt index 004722ab09e335..74a07ccb992649 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt @@ -926,7 +926,7 @@ public open class ReactTextInputManager public constructor() : } if (shouldBlur) { - editText.clearFocus() + editText.clearFocusAndMaybeRefocus() } // Prevent default behavior except when we want it to insert a newline.