Commit 97aa7ad
Fix ClassCastException in clearFocusAndMaybeRefocus when EditText is detached (#57423)
Summary:
On Android 9 and below (`SDK_INT <= P`) in touch mode, `ReactEditText.clearFocusAndMaybeRefocus()` unconditionally casts `rootView` to `ViewGroup`:
```kotlin
val rootViewGroup = rootView as ViewGroup
```
`View.getRootView()` returns the view **itself** when the view is detached from the window. An IME editor action is delivered asynchronously over Binder (`IInputConnectionWrapper`), so a submit-key press can arrive after the EditText has already been removed from the hierarchy (screen unmount/navigation racing the keyboard). When that happens the cast throws and kills the app:
```
java.lang.ClassCastException: com.facebook.react.views.textinput.ReactEditText cannot be cast to android.view.ViewGroup
at com.facebook.react.views.textinput.ReactEditText.clearFocusAndMaybeRefocus (ReactEditText.kt:379)
at com.facebook.react.views.textinput.ReactTextInputManager.addEventEmitters$lambda$3 (ReactTextInputManager.kt:933)
at android.widget.TextView.onEditorAction (TextView.java:6615)
at com.android.internal.widget.EditableInputConnection.performEditorAction (EditableInputConnection.java:138)
at android.view.inputmethod.InputConnectionWrapper.performEditorAction (InputConnectionWrapper.java:190)
at com.android.internal.view.IInputConnectionWrapper.executeMessage (IInputConnectionWrapper.java:360)
```
We see this steadily in production Crashlytics (RN 0.86, New Architecture): all events are on Android 7–9 devices (Samsung SM-J710GN / SM-G610F on 8.1.0, etc.), zero on Android 10+, because API > 28 takes the plain `super.clearFocus()` branch and never reaches the cast.
This change replaces the unchecked cast with a safe cast and falls back to a plain `clearFocus()` when the root is not a `ViewGroup`. That fallback is correct because the only reason the root isn't a `ViewGroup` is that the view is already detached — there is no surviving focus hierarchy to protect with the `descendantFocusability` workaround, and `hideSoftKeyboard()` still runs afterwards.
Behavior is unchanged on API > 28, in non-touch mode, and in the normal attached case on old Android.
## Changelog:
[ANDROID] [FIXED] - Fix ClassCastException crash on Android 9 and below when an IME submit action races the unmount of a TextInput
Pull Request resolved: #57423
Test Plan:
The race is timing-dependent, so it is exercised by the scenario rather than a unit test:
1. On an API 26–28 emulator/device, render a single-line `<TextInput>` with default `submitBehavior` (`blurAndSubmit`), focused, keyboard open.
2. Unmount the input (conditional render / navigation) in the same frame as pressing the keyboard's submit key. The IME action arrives over Binder after the view is detached.
3. Before this change: `getRootView()` returns the detached `ReactEditText` itself → `ClassCastException` (stack above). After this change: the safe cast falls back to `super.clearFocus()` + `hideSoftKeyboard()`, no crash, no behavioral difference otherwise.
Also verified:
- On the attached path (normal blur-on-submit on API ≤ 28), `rootView` is the DecorView, the safe cast succeeds, and the existing descendant-focusability logic runs exactly as before.
- On API > 28 the first branch is taken, unchanged.
Reviewed By: cortinico
Differential Revision: D110567654
Pulled By: javache
fbshipit-source-id: eb580aed7d0d029eb265b815ba7bed5e5ac85b4c1 parent c65845c commit 97aa7ad
1 file changed
Lines changed: 14 additions & 5 deletions
File tree
- packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput
Lines changed: 14 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
380 | 380 | | |
381 | 381 | | |
382 | 382 | | |
383 | | - | |
384 | | - | |
385 | | - | |
386 | | - | |
387 | | - | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
388 | 397 | | |
389 | 398 | | |
390 | 399 | | |
| |||
0 commit comments