From f7e35d4ef7d68d06fba1439c0aa6d9ed05b58a7f Mon Sep 17 00:00:00 2001 From: aleqsio Date: Mon, 13 Feb 2023 11:55:05 -0800 Subject: [PATCH] Fix invalid context cast in ReactRootView (#36121) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Hi 👋 I'm one of the Expo contributors. When upgrading the Stripe dependency in Expo for SDK48 release on Android, we noticed the following error: ```java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity``` With Kudo we narrowed it down to an underlying issue in the following cast in `ReactRootView` done during checking for keyboard events: ```((Activity) getContext())``` The `getContext()` is actually a `ContextThemeWrapper` for `ExperienceActivity`, so we should not cast it to an `Activity` directly - instead, we unwrap it using `getBaseContext()`. Implementing the following fix into the Expo fork fixed the crash for us: https://github.com/expo/react-native/commit/0e2c9cada120a3709e8285ab4bcdaa1c5c446732 ## Changelog [ANDROID] [FIXED] - Fixed crash occurring in certain native views when handling keyboard events. Pull Request resolved: https://github.com/facebook/react-native/pull/36121 Test Plan: Tested manually by comparing two builds before and after this change. The main branch build crashes when the [Stripe 0.23.1](https://github.com/stripe/stripe-react-native) Card Element from the example app is mounted on screen. Applying the change fixed the issue. Happy to make a more isolated reproduction if necessary. --- Full stack trace: ``` AndroidRuntime D Shutting down VM E FATAL EXCEPTION: main E Process: host.exp.exponent, PID: 8849 E java.lang.ClassCastException: android.view.ContextThemeWrapper cannot be cast to android.app.Activity E at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.checkForKeyboardEvents(ReactRootView.java:937) E at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.onGlobalLayout(ReactRootView.java:913) E at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:1061) E at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3352) E at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2286) E at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8948) E at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231) E at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239) E at android.view.Choreographer.doCallbacks(Choreographer.java:899) E at android.view.Choreographer.doFrame(Choreographer.java:832) E at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214) E at android.os.Handler.handleCallback(Handler.java:942) E at android.os.Handler.dispatchMessage(Handler.java:99) E at android.os.Looper.loopOnce(Looper.java:201) E at android.os.Looper.loop(Looper.java:288) E at android.app.ActivityThread.main(ActivityThread.java:7898) E at java.lang.reflect.Method.invoke(Native Method) E at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) E at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) ``` Reviewed By: cortinico Differential Revision: D43186530 Pulled By: javache fbshipit-source-id: 2143495f6b0c71f342eba6d5abb2bfa4a529fbdd --- .../main/java/com/facebook/react/ReactRootView.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 1ac3df2b2c08f5..cb480570ef10c2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -14,6 +14,7 @@ import android.app.Activity; import android.content.Context; +import android.content.ContextWrapper; import android.graphics.Canvas; import android.graphics.Insets; import android.graphics.Point; @@ -918,6 +919,14 @@ public void onGlobalLayout() { checkForDeviceDimensionsChanges(); } + private Activity getActivity() { + Context context = getContext(); + while (!(context instanceof Activity) && context instanceof ContextWrapper) { + context = ((ContextWrapper) context).getBaseContext(); + } + return (Activity) context; + } + @RequiresApi(api = Build.VERSION_CODES.R) private void checkForKeyboardEvents() { getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea); @@ -935,7 +944,7 @@ private void checkForKeyboardEvents() { Insets barInsets = rootInsets.getInsets(WindowInsets.Type.systemBars()); int height = imeInsets.bottom - barInsets.bottom; - int softInputMode = ((Activity) getContext()).getWindow().getAttributes().softInputMode; + int softInputMode = getActivity().getWindow().getAttributes().softInputMode; int screenY = softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING ? mVisibleViewArea.bottom - height