From 32f54877ff788240d24528d208c704ee78e4e761 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Thu, 19 Jan 2023 06:43:27 -0800 Subject: [PATCH] Do not use WindowInsetsCompat for Keyboard Events (#35897) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/35897 Fixes https://github.com/facebook/react-native/issues/35894 Android 11 added native support for querying whether the IME is present along with its size, as part of the WindowInsets API. D38500859 (https://github.com/facebook/react-native/commit/1e48274223ee647ac4fc2c21822b5240f3c47e4c) changed our logic for Android keyboard events to use it when available, fixing a longstanding issues where we could not reliably tell where the keyboard was open depending on softInputMode. An androidx library WindowInsetsCompat aimed to backport some of the functionality to older versions of Android, with the same API, documenting IME queries to work down to API level 23 (Android 6). I used this, so that we would be able to remove our own logic for detecting keyboard insets once we supported 23+. From an issue report, WindowInsetsCompat is not accurately returning whether the IME is open on at least Android 9. So this change makes it so we only use WindowInsets methods when they are provided by the OS (a tested golden path), and otherwise use the previously working heuristics on anything older. Changelog: [Android][Fixed] - Do not use WindowInsetsCompat for Keyboard Events Reviewed By: christophpurrer Differential Revision: D42604176 fbshipit-source-id: da6a0bbc34c36f8e6d4e4ac07bc96da048fd6aa8 --- .../com/facebook/react/ReactRootView.java | 79 +++++++++---------- 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java index 8f4375ad29e1b1..5ac4cd77d23006 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java @@ -15,6 +15,7 @@ import android.app.Activity; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; import android.os.Build; @@ -33,8 +34,6 @@ import android.widget.FrameLayout; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.core.graphics.Insets; -import androidx.core.view.WindowInsetsCompat; import com.facebook.common.logging.FLog; import com.facebook.infer.annotation.Assertions; import com.facebook.infer.annotation.ThreadConfined; @@ -776,7 +775,7 @@ public void runApplication() { @VisibleForTesting /* package */ void simulateCheckForKeyboardForTesting() { - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { getCustomGlobalLayoutListener().checkForKeyboardEvents(); } else { getCustomGlobalLayoutListener().checkForKeyboardEventsLegacy(); @@ -907,9 +906,7 @@ public void onGlobalLayout() { return; } - // WindowInsetsCompat IME measurement is reliable for API level 23+. - // https://developer.android.com/jetpack/androidx/releases/core#1.5.0-alpha02 - if (Build.VERSION.SDK_INT >= 23) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { checkForKeyboardEvents(); } else { checkForKeyboardEventsLegacy(); @@ -919,44 +916,44 @@ public void onGlobalLayout() { checkForDeviceDimensionsChanges(); } - @RequiresApi(api = Build.VERSION_CODES.M) + @RequiresApi(api = Build.VERSION_CODES.R) private void checkForKeyboardEvents() { getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea); WindowInsets rootInsets = getRootView().getRootWindowInsets(); - 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)); - } + if (rootInsets == null) { + return; + } + + boolean keyboardIsVisible = rootInsets.isVisible(WindowInsets.Type.ime()); + if (keyboardIsVisible != mKeyboardIsVisible) { + mKeyboardIsVisible = keyboardIsVisible; + + if (keyboardIsVisible) { + Insets imeInsets = rootInsets.getInsets(WindowInsets.Type.ime()); + Insets barInsets = rootInsets.getInsets(WindowInsets.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)); } } }