Skip to content

Commit

Permalink
fix: ReactRootView checkForKeyboardEvents to check if rootInsets are …
Browse files Browse the repository at this point in the history
…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: #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
  • Loading branch information
enahum authored and Riccardo Cipolleschi committed Jan 19, 2023
1 parent 416463c commit 6734d92
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 37 deletions.
66 changes: 34 additions & 32 deletions ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/<variant>/index.android.bundle
val resourcesDir = File(buildDir, "generated/res/react/$targetPath")
// Bundle: generated/assets/react/path/index.android.bundle
// Bundle: generated/assets/react/<variant>/index.android.bundle
val jsBundleDir = File(buildDir, "generated/assets/react/$targetPath")
// Sourcemap: generated/sourcemaps/react/path/index.android.bundle.map
// Sourcemap: generated/sourcemaps/react/<variant>/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/<variant>/index.android.bundle.packager.map
// Intermediate compiler:
// intermediates/sourcemaps/react/<variant>/index.android.bundle.compiler.map
val jsIntermediateSourceMapsDir = File(buildDir, "intermediates/sourcemaps/react/$targetPath")

// The location of the cli.js file for React Native
Expand Down

0 comments on commit 6734d92

Please sign in to comment.