Skip to content

Commit 96b32d7

Browse files
Ats Jenkgrabbou
authored andcommitted
Separate window dimensions change from orientation
Summary: **Summary:** There was a bug with RN.Dimensions returning incorrect window dimensions. In certain cases when device was in portrait, window dimensions reported landscape dimensions and vice versa. This happened because in certain scenarios, after device orientation changed, dimensions update event from ReactRootView had incorrect dimensions. Was able to reproduce this when device was rotated during app launch. After rotation global layout listener callback gets invoked. Inside the callback current and previous orientations are compared. When a change is detected, orientation and dimension change events are sent to JS. It is assumed, when orientation changes, new dimensions are available immediately. This is not the case for window dimensions as they are retrieved from resources object which gets updated asynchronously after orientation change. In cases when app is doing a lot of work on the main thread, like app startup, it takes more time to update the resources object. And when orientation change is detected in global layout, resources object is not updated with new dimensions yet. This causes dimensions update to be sent to JS with old window dimensions. Global layout listener callback does get invoked a second time when resources object is finally updated with new dimensions, but since orientation no longer changes, no event is sent to JS. Fixed this by separating dimensions update from orientation update. Now RN keeps track of previous window and screen dimension values. When a change is detected, an event is sent to JS with updated dimensions. This ensures that whenever dimensions change, JS gets the updated values. This has a side effect of sending dimension update twice in some cases. One example is the case above where window dimensions take time to update, but screen dimensions are updated immediately. This will cause two events to be sent to JS. One for window dimensions and one for screen dimensions update. Other change is that initial value for both window and screen fields is empty. Which results in first change to trigger an event. Previously initial orientation value was 0 which meant when app started in normal portrait orientation, first layout did not trigger a dimension update event. Now even first layout sends the event. This should not be an issue as it is to make sure dimensions in JS side are correct. **Testing:** Verified with a sample app that correct dimensions are available when app launches. Verified that after orientation dimensions are updated. Verified that in the scenario described above where window dimensions are updated later, we get correct dimension values in JS. We have incorporated this fix into our app and have been testing it internally. Ats Jenk Microsoft Corp. <!-- Thank you for sending the PR! If you changed any code, please provide us with clear instructions on how you verified your changes work. In other words, a test plan is *required*. Bonus points for screenshots and videos! Please read the Contribution Guidelines at https://github.com/facebook/react-native/blob/master/CONTRIBUTING.md to learn more about contributing to React Native. Happy contributing! --> Closes #15181 Differential Revision: D5552195 Pulled By: shergin fbshipit-source-id: d1f190cb960090468886ff56cda58cac296745db
1 parent 4d572cd commit 96b32d7

1 file changed

Lines changed: 32 additions & 4 deletions

File tree

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

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import android.os.Build;
1717
import android.os.Bundle;
1818
import android.util.AttributeSet;
19+
import android.util.DisplayMetrics;
1920
import android.view.MotionEvent;
2021
import android.view.Surface;
2122
import android.view.View;
@@ -357,6 +358,8 @@ private class CustomGlobalLayoutListener implements ViewTreeObserver.OnGlobalLay
357358

358359
private int mKeyboardHeight = 0;
359360
private int mDeviceRotation = 0;
361+
private DisplayMetrics mWindowMetrics = new DisplayMetrics();
362+
private DisplayMetrics mScreenMetrics = new DisplayMetrics();
360363

361364
/* package */ CustomGlobalLayoutListener() {
362365
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(getContext().getApplicationContext());
@@ -372,6 +375,7 @@ public void onGlobalLayout() {
372375
}
373376
checkForKeyboardEvents();
374377
checkForDeviceOrientationChanges();
378+
checkForDeviceDimensionsChanges();
375379
}
376380

377381
private void checkForKeyboardEvents() {
@@ -404,13 +408,37 @@ private void checkForDeviceOrientationChanges() {
404408
return;
405409
}
406410
mDeviceRotation = rotation;
407-
// It's important to repopulate DisplayMetrics and export them before emitting the
408-
// orientation change event, so that the Dimensions object returns the correct new values.
409-
DisplayMetricsHolder.initDisplayMetrics(getContext());
410-
emitUpdateDimensionsEvent();
411411
emitOrientationChanged(rotation);
412412
}
413413

414+
private void checkForDeviceDimensionsChanges() {
415+
// Get current display metrics.
416+
DisplayMetricsHolder.initDisplayMetrics(getContext());
417+
// Check changes to both window and screen display metrics since they may not update at the same time.
418+
if (!areMetricsEqual(mWindowMetrics, DisplayMetricsHolder.getWindowDisplayMetrics()) ||
419+
!areMetricsEqual(mScreenMetrics, DisplayMetricsHolder.getScreenDisplayMetrics())) {
420+
mWindowMetrics.setTo(DisplayMetricsHolder.getWindowDisplayMetrics());
421+
mScreenMetrics.setTo(DisplayMetricsHolder.getScreenDisplayMetrics());
422+
emitUpdateDimensionsEvent();
423+
}
424+
}
425+
426+
private boolean areMetricsEqual(DisplayMetrics displayMetrics, DisplayMetrics otherMetrics) {
427+
if (Build.VERSION.SDK_INT >= 17) {
428+
return displayMetrics.equals(otherMetrics);
429+
} else {
430+
// DisplayMetrics didn't have an equals method before API 17.
431+
// Check all public fields manually.
432+
return displayMetrics.widthPixels == otherMetrics.widthPixels &&
433+
displayMetrics.heightPixels == otherMetrics.heightPixels &&
434+
displayMetrics.density == otherMetrics.density &&
435+
displayMetrics.densityDpi == otherMetrics.densityDpi &&
436+
displayMetrics.scaledDensity == otherMetrics.scaledDensity &&
437+
displayMetrics.xdpi == otherMetrics.xdpi &&
438+
displayMetrics.ydpi == otherMetrics.ydpi;
439+
}
440+
}
441+
414442
private void emitOrientationChanged(final int newRotation) {
415443
String name;
416444
double rotationDegrees;

0 commit comments

Comments
 (0)