Skip to content

Commit

Permalink
Do not use WindowInsetsCompat for Keyboard Events (facebook#35897)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: facebook#35897

Fixes facebook#35894

Android 11 added native support for querying whether the IME is present along with its size, as part of the WindowInsets API. D38500859 (facebook@1e48274) 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
  • Loading branch information
NickGerleman authored and OlimpiaZurek committed May 22, 2023
1 parent f3696bb commit 19c9eea
Showing 1 changed file with 38 additions and 41 deletions.
79 changes: 38 additions & 41 deletions ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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));
}
}
}
Expand Down

0 comments on commit 19c9eea

Please sign in to comment.