From 4cdc2c48e826f5bb762085fc38954ed09df6ef12 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Thu, 19 Jan 2023 03:47:58 -0800 Subject: [PATCH] fix: ReactRootView checkForKeyboardEvents to check if rootInsets are set (#35869) Summary: react-native-navigation allows to register React components to be included in the navigation top bar as buttons, the way this work is by using the AppRegistry. When the ViewTreeObserver executes the `CustomGlobalLayout` we are checking for the RootWindowInsets in the `checkKeyboardEvents` which in the case for the top bar component it returns null and the **WindowInsetsCompat.toWindowInsetsCompat** function throws if the insets are null causing the app to crash. Interestingly in the function `checkForKeyboardEventsLegacy` the null value is being checked, so I guess it was overlooked in the newer function. ## Changelog [ANDROID] [FIXED] - Fix ReactRootView crash when root view window insets are null Pull Request resolved: https://github.com/facebook/react-native/pull/35869 Test Plan: The following videos show how the app crashes as soon as we attempt to pop a screen that contains a react component as a button in the navigation top bar and how it correctly pops to the previous screen after applying the fix | Crash | Fix | | -- | -- | | https://user-images.githubusercontent.com/6757047/213116971-fe693989-f978-438c-b8f9-fc56f2a477c8.mp4 | https://user-images.githubusercontent.com/6757047/213118352-fe258f28-07aa-4d17-98d2-97136464ffd5.mp4 | Reviewed By: cipolleschi Differential Revision: D42580156 Pulled By: cortinico fbshipit-source-id: 4dbd656d7c8148df67668a2a50913206bc35c07f --- .../com/facebook/react/ReactRootView.java | 66 ++++++++++--------- .../com/facebook/react/TaskConfiguration.kt | 12 ++-- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index e0c37c31a24e8f..8f4375ad29e1b1 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -923,38 +923,40 @@ public void onGlobalLayout() { private void checkForKeyboardEvents() { getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea); WindowInsets rootInsets = getRootView().getRootWindowInsets(); - WindowInsetsCompat compatRootInsets = WindowInsetsCompat.toWindowInsetsCompat(rootInsets); - - boolean keyboardIsVisible = compatRootInsets.isVisible(WindowInsetsCompat.Type.ime()); - if (keyboardIsVisible != mKeyboardIsVisible) { - mKeyboardIsVisible = keyboardIsVisible; - - if (keyboardIsVisible) { - Insets imeInsets = compatRootInsets.getInsets(WindowInsetsCompat.Type.ime()); - Insets barInsets = compatRootInsets.getInsets(WindowInsetsCompat.Type.systemBars()); - int height = imeInsets.bottom - barInsets.bottom; - - int softInputMode = ((Activity) getContext()).getWindow().getAttributes().softInputMode; - int screenY = - softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING - ? mVisibleViewArea.bottom - height - : mVisibleViewArea.bottom; - - sendEvent( - "keyboardDidShow", - createKeyboardEventPayload( - PixelUtil.toDIPFromPixel(screenY), - PixelUtil.toDIPFromPixel(mVisibleViewArea.left), - PixelUtil.toDIPFromPixel(mVisibleViewArea.width()), - PixelUtil.toDIPFromPixel(height))); - } else { - sendEvent( - "keyboardDidHide", - createKeyboardEventPayload( - PixelUtil.toDIPFromPixel(mLastHeight), - 0, - PixelUtil.toDIPFromPixel(mVisibleViewArea.width()), - 0)); + if (rootInsets != null) { + WindowInsetsCompat compatRootInsets = WindowInsetsCompat.toWindowInsetsCompat(rootInsets); + + boolean keyboardIsVisible = compatRootInsets.isVisible(WindowInsetsCompat.Type.ime()); + if (keyboardIsVisible != mKeyboardIsVisible) { + mKeyboardIsVisible = keyboardIsVisible; + + if (keyboardIsVisible) { + Insets imeInsets = compatRootInsets.getInsets(WindowInsetsCompat.Type.ime()); + Insets barInsets = compatRootInsets.getInsets(WindowInsetsCompat.Type.systemBars()); + int height = imeInsets.bottom - barInsets.bottom; + + int softInputMode = ((Activity) getContext()).getWindow().getAttributes().softInputMode; + int screenY = + softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING + ? mVisibleViewArea.bottom - height + : mVisibleViewArea.bottom; + + sendEvent( + "keyboardDidShow", + createKeyboardEventPayload( + PixelUtil.toDIPFromPixel(screenY), + PixelUtil.toDIPFromPixel(mVisibleViewArea.left), + PixelUtil.toDIPFromPixel(mVisibleViewArea.width()), + PixelUtil.toDIPFromPixel(height))); + } else { + sendEvent( + "keyboardDidHide", + createKeyboardEventPayload( + PixelUtil.toDIPFromPixel(mLastHeight), + 0, + PixelUtil.toDIPFromPixel(mVisibleViewArea.width()), + 0)); + } } } } diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt index 94ccb2bc14bf63..30bad252fd823b 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/TaskConfiguration.kt @@ -22,14 +22,16 @@ internal fun Project.configureReactTasks(variant: Variant, config: ReactExtensio val targetName = variant.name.replaceFirstChar { it.uppercase() } val targetPath = variant.name - // React js bundle directories + // Resources: generated/assets/react//index.android.bundle val resourcesDir = File(buildDir, "generated/res/react/$targetPath") - // Bundle: generated/assets/react/path/index.android.bundle + // Bundle: generated/assets/react//index.android.bundle val jsBundleDir = File(buildDir, "generated/assets/react/$targetPath") - // Sourcemap: generated/sourcemaps/react/path/index.android.bundle.map + // Sourcemap: generated/sourcemaps/react//index.android.bundle.map val jsSourceMapsDir = File(buildDir, "generated/sourcemaps/react/$targetPath") - // Intermediate packager: intermediates/sourcemaps/react/path/index.android.bundle.packager.map - // Intermediate compiler: intermediates/sourcemaps/react/path/index.android.bundle.compiler.map + // Intermediate packager: + // intermediates/sourcemaps/react//index.android.bundle.packager.map + // Intermediate compiler: + // intermediates/sourcemaps/react//index.android.bundle.compiler.map val jsIntermediateSourceMapsDir = File(buildDir, "intermediates/sourcemaps/react/$targetPath") // The location of the cli.js file for React Native