Skip to content

Commit

Permalink
Android: fix ClassCastException in ReactRootView.java when software k…
Browse files Browse the repository at this point in the history
…eyboard is shown (#40755)

Summary:
Fixes #40754

Hi all!
We noticed that our app started to crash after bumping to RN v0.71.13, anyways after a deeper investigation we also found that the crash occurs in the latest version as well.

Crash log:
```
E  FATAL EXCEPTION: main
Process: com.nfl.fantasy.core.android.debug, PID: 6034
java.lang.ClassCastException: android.app.ContextImpl cannot be cast to android.app.Activity
at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.getActivity(ReactRootView.java:926)
at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.checkForKeyboardEvents(ReactRootView.java:946)
at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.onGlobalLayout(ReactRootView.java:912)
at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:1061)
```
The code which causes ClassCastException is following [here](https://github.com/facebook/react-native/blob/ea88fbe229e1d276753ee8e118184274fc872138/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java#L864).
In this code explicit type conversion to Activity is not safe because it's not guaranteed by the compiler that context will be compatible with Activity type.
The appropriate issue [has been filed](#40754).

## Changelog:

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[ANDROID] [FIXED] - Fixed crash occurring in certain native views when keyboard events are fired.

Pull Request resolved: #40755

Test Plan:
Tested it manually with the [reference application](https://github.com/kot331107/rnCrashReproducer).  Repro steps are as follows:

- Build and run the app on Android
- Tap the button "Open Modal"
- You should see the red popup fragment to the bottom of the screen
- Tap on the text input to open software keyboard
- Expected: it should show the keyboard and no crash happens.

Reviewed By: arushikesarwani94

Differential Revision: D50198424

Pulled By: NickGerleman

fbshipit-source-id: a5a6d86334856f4ffbe818150da5793380da4702
  • Loading branch information
kot331107 authored and facebook-github-bot committed Oct 14, 2023
1 parent b21cf6f commit 9497203
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
import static com.facebook.react.uimanager.common.UIManagerType.FABRIC;
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Point;
Expand Down Expand Up @@ -856,12 +854,18 @@ public void onGlobalLayout() {
checkForDeviceDimensionsChanges();
}

private Activity getActivity() {
Context context = getContext();
while (!(context instanceof Activity) && context instanceof ContextWrapper) {
context = ((ContextWrapper) context).getBaseContext();
private @Nullable WindowManager.LayoutParams getWindowLayoutParams() {
View view = ReactRootView.this;
if (view.getLayoutParams() instanceof WindowManager.LayoutParams) {
return (WindowManager.LayoutParams) view.getLayoutParams();
}
return (Activity) context;
while (view.getParent() instanceof View) {
view = (View) view.getParent();
if (view.getLayoutParams() instanceof WindowManager.LayoutParams) {
return (WindowManager.LayoutParams) view.getLayoutParams();
}
}
return null;
}

@RequiresApi(api = Build.VERSION_CODES.R)
Expand All @@ -881,7 +885,13 @@ private void checkForKeyboardEvents() {
Insets barInsets = rootInsets.getInsets(WindowInsets.Type.systemBars());
int height = imeInsets.bottom - barInsets.bottom;

int softInputMode = getActivity().getWindow().getAttributes().softInputMode;
int softInputMode;
WindowManager.LayoutParams windowLayoutParams = getWindowLayoutParams();
if (windowLayoutParams != null) {
softInputMode = windowLayoutParams.softInputMode;
} else {
return;
}
int screenY =
softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
? mVisibleViewArea.bottom - height
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import android.graphics.Insets
import android.graphics.Rect
import android.view.MotionEvent
import android.view.WindowInsets
import android.view.WindowManager
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.CatalystInstance
import com.facebook.react.bridge.JavaOnlyArray
Expand Down Expand Up @@ -211,9 +212,11 @@ class RootViewTest {
.setVisible(WindowInsets.Type.ime(), true)
.build()
}
val rootViewSpy = spy(rootView)
whenever(rootViewSpy.getLayoutParams()).thenReturn(WindowManager.LayoutParams())

rootView.startReactApplication(instanceManager, "")
rootView.simulateCheckForKeyboardForTesting()
rootViewSpy.startReactApplication(instanceManager, "")
rootViewSpy.simulateCheckForKeyboardForTesting()

val params = Arguments.createMap()
val endCoordinates = Arguments.createMap()
Expand Down

0 comments on commit 9497203

Please sign in to comment.