Skip to content

Commit ff4ef2f

Browse files
andreicoman11bestander
authored andcommitted
Fix DisplayMetrics KeyboardListener dependency
Summary: public KeyboardListener needs DisplayMetrics to be initialized when it is attached. At the moment, this breaks easily whenever we change these components, since DisplayMetrics are intialized in a module and KeyboardListener is created eagerly in ReactRootView, whereas ReactRootView can exist without the instance. This changes to create DisplayMetrics as soon as possible, when the react instance is built. The KeyboardListener is created and attached after the ReactRootView is attached to an existing instance, point at which DisplayMetrics have to be initialized. Reviewed By: dmmiller Differential Revision: D2911351 fb-gh-sync-id: 64d1805c5d5b2f6876adb694b565a2df059b381d
1 parent d5ed811 commit ff4ef2f

File tree

11 files changed

+86
-77
lines changed

11 files changed

+86
-77
lines changed

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import javax.annotation.Nullable;
1313

14+
import java.lang.reflect.InvocationTargetException;
15+
import java.lang.reflect.Method;
1416
import java.util.ArrayList;
1517
import java.util.Collection;
1618
import java.util.List;
@@ -21,8 +23,12 @@
2123
import android.content.Context;
2224
import android.content.Intent;
2325
import android.os.AsyncTask;
26+
import android.os.Build;
2427
import android.os.Bundle;
28+
import android.util.DisplayMetrics;
29+
import android.view.Display;
2530
import android.view.View;
31+
import android.view.WindowManager;
2632

2733
import com.facebook.common.logging.FLog;
2834
import com.facebook.infer.annotation.Assertions;
@@ -58,6 +64,7 @@
5864
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
5965
import com.facebook.react.modules.core.DeviceEventManagerModule;
6066
import com.facebook.react.uimanager.AppRegistry;
67+
import com.facebook.react.uimanager.DisplayMetricsHolder;
6168
import com.facebook.react.uimanager.UIImplementationProvider;
6269
import com.facebook.react.uimanager.UIManagerModule;
6370
import com.facebook.react.uimanager.ViewManager;
@@ -248,6 +255,7 @@ public T get() throws Exception {
248255

249256
// TODO(9577825): remove this
250257
ApplicationHolder.setApplication((Application) applicationContext.getApplicationContext());
258+
setDisplayMetrics(applicationContext);
251259

252260
mApplicationContext = applicationContext;
253261
mJSBundleFile = jsBundleFile;
@@ -286,6 +294,38 @@ private static void initializeSoLoaderIfNecessary(Context applicationContext) {
286294
SoLoader.init(applicationContext, /* native exopackage */ false);
287295
}
288296

297+
private static void setDisplayMetrics(Context context) {
298+
DisplayMetrics displayMetrics = new DisplayMetrics();
299+
displayMetrics.setTo(context.getResources().getDisplayMetrics());
300+
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
301+
Display display = wm.getDefaultDisplay();
302+
303+
// Get the real display metrics if we are using API level 17 or higher.
304+
// The real metrics include system decor elements (e.g. soft menu bar).
305+
//
306+
// See: http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
307+
if (Build.VERSION.SDK_INT >= 17){
308+
display.getRealMetrics(displayMetrics);
309+
310+
} else {
311+
// For 14 <= API level <= 16, we need to invoke getRawHeight and getRawWidth to get the real dimensions.
312+
// Since react-native only supports API level 16+ we don't have to worry about other cases.
313+
//
314+
// Reflection exceptions are rethrown at runtime.
315+
//
316+
// See: http://stackoverflow.com/questions/14341041/how-to-get-real-screen-height-and-width/23861333#23861333
317+
try {
318+
Method mGetRawH = Display.class.getMethod("getRawHeight");
319+
Method mGetRawW = Display.class.getMethod("getRawWidth");
320+
displayMetrics.widthPixels = (Integer) mGetRawW.invoke(display);
321+
displayMetrics.heightPixels = (Integer) mGetRawH.invoke(display);
322+
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
323+
throw new RuntimeException("Error getting real dimensions for API level < 17", e);
324+
}
325+
}
326+
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
327+
}
328+
289329
/**
290330
* Trigger react context initialization asynchronously in a background async task. This enables
291331
* applications to pre-load the application JS, and execute global code before

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

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@
5555
*/
5656
public class ReactRootView extends SizeMonitoringFrameLayout implements RootView {
5757

58-
private final KeyboardListener mKeyboardListener = new KeyboardListener();
59-
6058
private @Nullable ReactInstanceManager mReactInstanceManager;
6159
private @Nullable String mJSModuleName;
6260
private @Nullable Bundle mLaunchOptions;
61+
private @Nullable KeyboardListener mKeyboardListener;
6362
private int mTargetTag = -1;
6463
private final float[] mTargetCoordinates = new float[2];
6564
private boolean mChildIsHandlingNativeGesture = false;
@@ -107,7 +106,7 @@ public void run() {
107106
Assertions.assertNotNull(mReactInstanceManager)
108107
.attachMeasuredRootView(ReactRootView.this);
109108
mIsAttachedToInstance = true;
110-
getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
109+
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
111110
}
112111
});
113112
}
@@ -298,7 +297,7 @@ protected void onDetachedFromWindow() {
298297
if (mReactInstanceManager != null && !mAttachScheduled) {
299298
mReactInstanceManager.detachRootView(this);
300299
mIsAttachedToInstance = false;
301-
getViewTreeObserver().removeOnGlobalLayoutListener(mKeyboardListener);
300+
getViewTreeObserver().removeOnGlobalLayoutListener(getKeyboardListener());
302301
}
303302
}
304303

@@ -356,7 +355,7 @@ public void startReactApplication(
356355
if (mWasMeasured && mIsAttachedToWindow) {
357356
mReactInstanceManager.attachMeasuredRootView(this);
358357
mIsAttachedToInstance = true;
359-
getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
358+
getViewTreeObserver().addOnGlobalLayoutListener(getKeyboardListener());
360359
} else {
361360
mAttachScheduled = true;
362361
}
@@ -381,9 +380,21 @@ public void startReactApplication(
381380
mWasMeasured = true;
382381
}
383382

383+
private KeyboardListener getKeyboardListener() {
384+
if (mKeyboardListener == null) {
385+
mKeyboardListener = new KeyboardListener();
386+
}
387+
return mKeyboardListener;
388+
}
389+
384390
private class KeyboardListener implements ViewTreeObserver.OnGlobalLayoutListener {
391+
private final Rect mVisibleViewArea;
392+
385393
private int mKeyboardHeight = 0;
386-
private final Rect mVisibleViewArea = new Rect();
394+
395+
/* package */ KeyboardListener() {
396+
mVisibleViewArea = new Rect();
397+
}
387398

388399
@Override
389400
public void onGlobalLayout() {

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModule.java

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,10 @@
1111

1212
import javax.annotation.Nullable;
1313

14-
import java.lang.reflect.InvocationTargetException;
15-
import java.lang.reflect.Method;
16-
import java.util.Arrays;
1714
import java.util.List;
1815
import java.util.Map;
1916

20-
import android.content.Context;
21-
import android.os.Build;
22-
import android.util.DisplayMetrics;
23-
import android.view.Display;
24-
import android.view.WindowManager;
25-
26-
import com.facebook.csslayout.CSSLayoutContext;
27-
import com.facebook.infer.annotation.Assertions;
2817
import com.facebook.react.animation.Animation;
29-
import com.facebook.react.bridge.Arguments;
3018
import com.facebook.react.bridge.Callback;
3119
import com.facebook.react.bridge.LifecycleEventListener;
3220
import com.facebook.react.bridge.OnBatchCompleteListener;
@@ -35,7 +23,6 @@
3523
import com.facebook.react.bridge.ReactMethod;
3624
import com.facebook.react.bridge.ReadableArray;
3725
import com.facebook.react.bridge.ReadableMap;
38-
import com.facebook.react.bridge.WritableArray;
3926
import com.facebook.react.uimanager.debug.NotThreadSafeViewHierarchyUpdateDebugListener;
4027
import com.facebook.react.uimanager.events.EventDispatcher;
4128
import com.facebook.systrace.Systrace;
@@ -90,11 +77,7 @@ public UIManagerModule(
9077
UIImplementation uiImplementation) {
9178
super(reactContext);
9279
mEventDispatcher = new EventDispatcher(reactContext);
93-
94-
DisplayMetrics displayMetrics = getDisplayMetrics();
95-
96-
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
97-
mModuleConstants = createConstants(displayMetrics, viewManagerList);
80+
mModuleConstants = createConstants(viewManagerList);
9881
mUIImplementation = uiImplementation;
9982

10083
reactContext.addLifecycleEventListener(this);
@@ -131,14 +114,10 @@ public void onCatalystInstanceDestroy() {
131114
mEventDispatcher.onCatalystInstanceDestroyed();
132115
}
133116

134-
private static Map<String, Object> createConstants(
135-
DisplayMetrics displayMetrics,
136-
List<ViewManager> viewManagerList) {
117+
private static Map<String, Object> createConstants(List<ViewManager> viewManagerList) {
137118
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "CreateUIManagerConstants");
138119
try {
139-
return UIManagerModuleConstantsHelper.createConstants(
140-
displayMetrics,
141-
viewManagerList);
120+
return UIManagerModuleConstantsHelper.createConstants(viewManagerList);
142121
} finally {
143122
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
144123
}
@@ -460,40 +439,4 @@ public EventDispatcher getEventDispatcher() {
460439
public void sendAccessibilityEvent(int tag, int eventType) {
461440
mUIImplementation.sendAccessibilityEvent(tag, eventType);
462441
}
463-
464-
private DisplayMetrics getDisplayMetrics() {
465-
Context context = getReactApplicationContext();
466-
467-
DisplayMetrics displayMetrics = new DisplayMetrics();
468-
displayMetrics.setTo(context.getResources().getDisplayMetrics());
469-
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
470-
Display display = wm.getDefaultDisplay();
471-
472-
// Get the real display metrics if we are using API level 17 or higher.
473-
// The real metrics include system decor elements (e.g. soft menu bar).
474-
//
475-
// See: http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)
476-
if (Build.VERSION.SDK_INT >= 17){
477-
display.getRealMetrics(displayMetrics);
478-
479-
} else {
480-
// For 14 <= API level <= 16, we need to invoke getRawHeight and getRawWidth to get the real dimensions.
481-
// Since react-native only supports API level 16+ we don't have to worry about other cases.
482-
//
483-
// Reflection exceptions are rethrown at runtime.
484-
//
485-
// See: http://stackoverflow.com/questions/14341041/how-to-get-real-screen-height-and-width/23861333#23861333
486-
try {
487-
Method mGetRawH = Display.class.getMethod("getRawHeight");
488-
Method mGetRawW = Display.class.getMethod("getRawWidth");
489-
displayMetrics.widthPixels = (Integer) mGetRawW.invoke(display);
490-
displayMetrics.heightPixels = (Integer) mGetRawH.invoke(display);
491-
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
492-
throw new RuntimeException("Error getting real dimensions for API level < 17", e);
493-
}
494-
}
495-
496-
return displayMetrics;
497-
}
498-
499442
}

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstants.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import java.util.HashMap;
1313
import java.util.Map;
1414

15-
import android.text.InputType;
1615
import android.util.DisplayMetrics;
1716
import android.view.accessibility.AccessibilityEvent;
1817
import android.widget.ImageView;
@@ -80,7 +79,7 @@
8079
.build();
8180
}
8281

83-
public static Map<String, Object> getConstants(DisplayMetrics displayMetrics) {
82+
public static Map<String, Object> getConstants() {
8483
HashMap<String, Object> constants = new HashMap<String, Object>();
8584
constants.put(
8685
"UIView",
@@ -92,6 +91,7 @@ public static Map<String, Object> getConstants(DisplayMetrics displayMetrics) {
9291
"ScaleAspectFill",
9392
ImageView.ScaleType.CENTER_CROP.ordinal())));
9493

94+
DisplayMetrics displayMetrics = DisplayMetricsHolder.getDisplayMetrics();
9595
constants.put(
9696
"Dimensions",
9797
MapBuilder.of(

ReactAndroid/src/main/java/com/facebook/react/uimanager/UIManagerModuleConstantsHelper.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99

1010
package com.facebook.react.uimanager;
1111

12-
import java.util.HashMap;
1312
import java.util.List;
1413
import java.util.Map;
1514

16-
import android.util.DisplayMetrics;
17-
1815
import com.facebook.react.common.MapBuilder;
1916

2017
/**
@@ -39,10 +36,8 @@
3936
* {@link UIManagerModuleConstants}.
4037
* TODO(6845124): Create a test for this
4138
*/
42-
/* package */ static Map<String, Object> createConstants(
43-
DisplayMetrics displayMetrics,
44-
List<ViewManager> viewManagers) {
45-
Map<String, Object> constants = UIManagerModuleConstants.getConstants(displayMetrics);
39+
/* package */ static Map<String, Object> createConstants(List<ViewManager> viewManagers) {
40+
Map<String, Object> constants = UIManagerModuleConstants.getConstants();
4641
Map bubblingEventTypesConstants = UIManagerModuleConstants.getBubblingEventTypeConstants();
4742
Map directEventTypesConstants = UIManagerModuleConstants.getDirectEventTypeConstants();
4843

ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,17 @@
2929
import android.widget.TextView;
3030

3131
import com.facebook.infer.annotation.Assertions;
32-
import com.facebook.react.bridge.JSApplicationCausedNativeException;
3332
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
3433
import com.facebook.react.bridge.ReactContext;
3534
import com.facebook.react.bridge.ReadableArray;
3635
import com.facebook.react.common.MapBuilder;
3736
import com.facebook.react.uimanager.BaseViewManager;
3837
import com.facebook.react.uimanager.PixelUtil;
39-
import com.facebook.react.uimanager.annotations.ReactProp;
4038
import com.facebook.react.uimanager.ThemedReactContext;
4139
import com.facebook.react.uimanager.UIManagerModule;
4240
import com.facebook.react.uimanager.ViewDefaults;
4341
import com.facebook.react.uimanager.ViewProps;
42+
import com.facebook.react.uimanager.annotations.ReactProp;
4443
import com.facebook.react.uimanager.events.EventDispatcher;
4544
import com.facebook.react.views.text.DefaultStyleValuesUtil;
4645
import com.facebook.react.views.text.ReactTextUpdate;

ReactAndroid/src/test/java/com/facebook/react/uimanager/ReactPropConstantsTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.List;
1414
import java.util.Map;
1515

16+
import android.util.DisplayMetrics;
1617
import android.view.View;
1718

1819
import com.facebook.react.bridge.ReactApplicationContext;
@@ -145,6 +146,8 @@ public void customIntGroupProp(View v, int index, Integer value) {
145146
public void testNativePropsIncludeCorrectTypes() {
146147
List<ViewManager> viewManagers = Arrays.<ViewManager>asList(new ViewManagerUnderTest());
147148
ReactApplicationContext reactContext = new ReactApplicationContext(RuntimeEnvironment.application);
149+
DisplayMetrics displayMetrics = reactContext.getResources().getDisplayMetrics();
150+
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
148151
UIManagerModule uiManagerModule = new UIManagerModule(
149152
reactContext,
150153
viewManagers,

ReactAndroid/src/test/java/com/facebook/react/uimanager/UIManagerModuleConstantsTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import java.util.List;
1414
import java.util.Map;
1515

16+
import android.util.DisplayMetrics;
17+
1618
import com.facebook.react.bridge.ReactApplicationContext;
1719
import com.facebook.react.common.MapBuilder;
1820

@@ -56,6 +58,9 @@ public class UIManagerModuleConstantsTest {
5658
public void setUp() {
5759
mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
5860
mUIImplementation = mock(UIImplementation.class);
61+
62+
DisplayMetrics displayMetrics = mReactContext.getResources().getDisplayMetrics();
63+
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
5964
}
6065

6166
@Test

ReactAndroid/src/test/java/com/facebook/react/uimanager/UIManagerModuleTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import java.util.List;
1515

1616
import android.graphics.Color;
17+
import android.util.DisplayMetrics;
1718
import android.view.Choreographer;
1819
import android.view.View;
1920
import android.view.ViewGroup;
@@ -107,9 +108,13 @@ public Object answer(InvocationOnMock invocation) throws Throwable {
107108
mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
108109
mReactContext.initializeWithInstance(mCatalystInstanceMock);
109110

111+
DisplayMetrics displayMetrics = mReactContext.getResources().getDisplayMetrics();
112+
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
113+
110114
UIManagerModule uiManagerModuleMock = mock(UIManagerModule.class);
111115
when(mCatalystInstanceMock.getNativeModule(UIManagerModule.class))
112116
.thenReturn(uiManagerModuleMock);
117+
113118
}
114119

115120
@Test

ReactAndroid/src/test/java/com/facebook/react/views/text/ReactTextTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import android.os.Build;
2222
import android.text.Spanned;
2323
import android.text.TextUtils;
24+
import android.util.DisplayMetrics;
2425
import android.text.style.AbsoluteSizeSpan;
2526
import android.view.Choreographer;
2627
import android.widget.TextView;
@@ -31,6 +32,7 @@
3132
import com.facebook.react.bridge.ReactApplicationContext;
3233
import com.facebook.react.bridge.SimpleArray;
3334
import com.facebook.react.bridge.SimpleMap;
35+
import com.facebook.react.uimanager.DisplayMetricsHolder;
3436
import com.facebook.react.uimanager.ReactChoreographer;
3537
import com.facebook.react.uimanager.UIImplementation;
3638
import com.facebook.react.uimanager.UIManagerModule;
@@ -371,6 +373,8 @@ private void executePendingChoreographerCallbacks() {
371373

372374
public UIManagerModule getUIManagerModule() {
373375
ReactApplicationContext reactContext = ReactTestHelper.createCatalystContextForTest();
376+
DisplayMetrics displayMetrics = reactContext.getResources().getDisplayMetrics();
377+
DisplayMetricsHolder.setDisplayMetrics(displayMetrics);
374378
List<ViewManager> viewManagers = Arrays.asList(
375379
new ViewManager[] {
376380
new ReactTextViewManager(),

0 commit comments

Comments
 (0)