Skip to content

Commit 8bef3b1

Browse files
fabOnReactfacebook-github-bot
authored andcommitted
compute correct Keyboard Height with Notch (#30919)
Summary: fixes #27089 fixes #30191 fixes #26296 fixes #24353 Related #30052 #28004 #26536 The keyboard height of event keyboardDidShow is computed as the difference of two variables: - The screen height excluding the Android Notch DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels returns the screen height excluding the Android Notch - The Visible Area excluding the Keyboard, but including the Android Notch getWindowVisibleDisplayFrame() which returns the visible area including the Android Notch The computation of the keyboard height is wrong when the device has an Android Notch. This pr adds the Android Notch computation for API levels 28+ More info at #27089 (comment) ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [Android] [Fixed] - Compute Android Notch in keyboardDidShow height calculation API 28+ Pull Request resolved: #30919 Test Plan: adding a ReactRootViewTest for keyboardDidShow verifying correct functionality on API < 28 **<details><summary>TEST CASE - BEFORE FIX</summary>** <p> **WITHOUT NOTCH** - The black view on the bottom is visible - The keyboard height is 282 | **Full Screen** | **Keyboard Did Show** | |:-------------------------:|:-------------------------:| | <img src="https://user-images.githubusercontent.com/24992535/107212700-a1fd9d00-6a07-11eb-9248-26f9c4d92ae3.png" width="300" height="" /> | <img src="https://user-images.githubusercontent.com/24992535/107212590-7975a300-6a07-11eb-89f4-891a37a7c406.png" width="300" height="" /> | **WITH NOTCH** - The black view on the bottom is **not** visible. The black view is not visible because keyboardDidHide is sending the wrong keyboard height value. - The keyboard height changes to 234. The keyboard height is the same from the previous test, but the value sent from keyboardDidHide changed for the Notch. | **Full Screen** | **Keyboard Did Show** | |:-------------------------:|:-------------------------:| | <img src="https://user-images.githubusercontent.com/24992535/107212619-81cdde00-6a07-11eb-9630-7e7c8c34d798.png" width="300" height="" /> | <img src="https://user-images.githubusercontent.com/24992535/107212707-a4f88d80-6a07-11eb-9134-f077059c83a6.png" width="300" height="" /> | </p> </details> **<details><summary>TEST CASE - AFTER FIX</summary>** <p> **WITH NOTCH** - The black view on the bottom is visible - The keyboard height is 282 | **Full Screen** | **Keyboard Did Show** | |:-------------------------:|:-------------------------:| | <img src="https://user-images.githubusercontent.com/24992535/107212619-81cdde00-6a07-11eb-9630-7e7c8c34d798.png" width="300" height="" /> | <img src="https://user-images.githubusercontent.com/24992535/107349053-0d0ea880-6ac8-11eb-9695-33128080b6b8.png" width="300" height="" /> | </p> </details> Reviewed By: ShikaSD Differential Revision: D31207989 Pulled By: cortinico fbshipit-source-id: 0955a3884201122166c5c0ae2aca988a0ed4af53
1 parent ea3e244 commit 8bef3b1

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

ReactAndroid/src/main/java/com/facebook/react/ReactRootView.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616
import android.graphics.Canvas;
1717
import android.graphics.Point;
1818
import android.graphics.Rect;
19+
import android.os.Build;
1920
import android.os.Bundle;
2021
import android.util.AttributeSet;
22+
import android.view.DisplayCutout;
2123
import android.view.KeyEvent;
2224
import android.view.MotionEvent;
2325
import android.view.Surface;
@@ -647,6 +649,11 @@ public void runApplication() {
647649
mJSTouchDispatcher = new JSTouchDispatcher(this);
648650
}
649651

652+
@VisibleForTesting
653+
/* package */ void simulateCheckForKeyboardForTesting() {
654+
getCustomGlobalLayoutListener().checkForKeyboardEvents();
655+
}
656+
650657
private CustomGlobalLayoutListener getCustomGlobalLayoutListener() {
651658
if (mCustomGlobalLayoutListener == null) {
652659
mCustomGlobalLayoutListener = new CustomGlobalLayoutListener();
@@ -766,8 +773,17 @@ public void onGlobalLayout() {
766773

767774
private void checkForKeyboardEvents() {
768775
getRootView().getWindowVisibleDisplayFrame(mVisibleViewArea);
776+
int notchHeight = 0;
777+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
778+
DisplayCutout displayCutout = getRootView().getRootWindowInsets().getDisplayCutout();
779+
if (displayCutout != null) {
780+
notchHeight = displayCutout.getSafeInsetTop();
781+
}
782+
}
769783
final int heightDiff =
770-
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels - mVisibleViewArea.bottom;
784+
DisplayMetricsHolder.getWindowDisplayMetrics().heightPixels
785+
- mVisibleViewArea.bottom
786+
+ notchHeight;
771787

772788
boolean isKeyboardShowingOrKeyboardHeightChanged =
773789
mKeyboardHeight != heightDiff && heightDiff > mMinKeyboardHeightDetected;

ReactAndroid/src/test/java/com/facebook/react/RootViewTest.java

+43
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static org.mockito.Mockito.verifyNoMoreInteractions;
1717
import static org.mockito.Mockito.when;
1818

19+
import android.graphics.Rect;
1920
import android.view.MotionEvent;
2021
import com.facebook.react.bridge.Arguments;
2122
import com.facebook.react.bridge.CatalystInstance;
@@ -25,7 +26,9 @@
2526
import com.facebook.react.bridge.ReactContext;
2627
import com.facebook.react.bridge.ReactTestHelper;
2728
import com.facebook.react.bridge.WritableArray;
29+
import com.facebook.react.bridge.WritableMap;
2830
import com.facebook.react.common.SystemClock;
31+
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
2932
import com.facebook.react.uimanager.DisplayMetricsHolder;
3033
import com.facebook.react.uimanager.UIManagerModule;
3134
import com.facebook.react.uimanager.events.Event;
@@ -37,6 +40,7 @@
3740
import org.junit.Test;
3841
import org.junit.runner.RunWith;
3942
import org.mockito.ArgumentCaptor;
43+
import org.mockito.Mockito;
4044
import org.mockito.invocation.InvocationOnMock;
4145
import org.mockito.stubbing.Answer;
4246
import org.powermock.api.mockito.PowerMockito;
@@ -209,4 +213,43 @@ public void testRemountApplication() {
209213
rootView.unmountReactApplication();
210214
rootView.startReactApplication(instanceManager, "");
211215
}
216+
217+
@Test
218+
public void testCheckForKeyboardEvents() {
219+
ReactInstanceManager instanceManager = mock(ReactInstanceManager.class);
220+
RCTDeviceEventEmitter eventEmitterModuleMock = mock(RCTDeviceEventEmitter.class);
221+
222+
when(instanceManager.getCurrentReactContext()).thenReturn(mReactContext);
223+
when(mReactContext.getJSModule(RCTDeviceEventEmitter.class)).thenReturn(eventEmitterModuleMock);
224+
225+
ReactRootView rootView =
226+
new ReactRootView(mReactContext) {
227+
@Override
228+
public void getWindowVisibleDisplayFrame(Rect outRect) {
229+
if (outRect.bottom == 0) {
230+
outRect.bottom += 100;
231+
outRect.right += 370;
232+
} else {
233+
outRect.bottom += 370;
234+
}
235+
}
236+
};
237+
238+
rootView.startReactApplication(instanceManager, "");
239+
rootView.simulateCheckForKeyboardForTesting();
240+
241+
WritableMap params = Arguments.createMap();
242+
WritableMap endCoordinates = Arguments.createMap();
243+
double screenHeight = 470.0;
244+
double keyboardHeight = 100.0;
245+
params.putDouble("duration", 0.0);
246+
endCoordinates.putDouble("width", screenHeight - keyboardHeight);
247+
endCoordinates.putDouble("screenX", 0.0);
248+
endCoordinates.putDouble("height", screenHeight - keyboardHeight);
249+
endCoordinates.putDouble("screenY", keyboardHeight);
250+
params.putMap("endCoordinates", endCoordinates);
251+
params.putString("easing", "keyboard");
252+
253+
verify(eventEmitterModuleMock, Mockito.times(1)).emit("keyboardDidShow", params);
254+
}
212255
}

0 commit comments

Comments
 (0)