Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use getRealMetrics for display metrics. Closes #4934 #4935

Closed
wants to merge 1 commit into from

Conversation

jaysoo
Copy link
Contributor

@jaysoo jaysoo commented Dec 23, 2015

Fixes #4934.

Since API level 17, there has a Display.getRealMetrics method. This allows us to get the actual sizes of the screen (including soft menu bar and other system decor elements).

See: http://developer.android.com/reference/android/view/Display.html#getRealMetrics(android.util.DisplayMetrics)

I'm not sure if there is a good way to write unit or integration tests for this. Please let me know if there are any suggestions or concerns.

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @mkonicek, @astreet and @andreicoman11 to be potential reviewers.

@facebook-github-bot facebook-github-bot added GH Review: review-needed CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. labels Dec 23, 2015
@jaysoo jaysoo closed this Dec 23, 2015
@jaysoo jaysoo reopened this Dec 23, 2015

try {
Display display = wm.getDefaultDisplay();
Display.class.getMethod("getRealMetrics", DisplayMetrics.class).invoke(display, displayMetrics);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not display.getRealMetrics?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case the method is missing (API < 17). As far as I can tell Rn supports 15+?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jaysoo Yes, but you're already checking Build.VERSION.SDK_INT >= 17

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from af5906c to 0c65ffa Compare December 23, 2015 14:35
@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@jaysoo
Copy link
Contributor Author

jaysoo commented Dec 23, 2015

@satya164 Updated the PR to not silently catch exceptions.

Reflection exceptions are logged using FLog, and the invocation exception is rethrown as runtime exception.

Display display = wm.getDefaultDisplay();

try {
Display.class.getMethod("getRealMetrics", DisplayMetrics.class).invoke(display, displayMetrics);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use Reflection if you're already checking Build.VERSION.SDK_INT >= 17?

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from 0c65ffa to f86a428 Compare December 23, 2015 19:33
@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from f86a428 to 897c930 Compare December 23, 2015 20:25
@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@jaysoo
Copy link
Contributor Author

jaysoo commented Dec 23, 2015

@satya164 Removed reflection usage. I was worried about compiling it on API 16. Tried it using emulator, and it seems like there's no problem.

This also removes the try-catch.

WindowManager wm = (WindowManager)reactContext.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
display.getRealMetrics(displayMetrics);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If any of this fails (getSystemService returns null, the result is not of type WindowManager etc.) we probably don't want to crash the whole UIManagerModule. Can you wrap all of this in a try-catch block and log the exception using FLog (check other call sites of FLog for examples).

@mkonicek
Copy link
Contributor

Looks legit, thanks a lot for the PR! Note that almost the whole team is on vacation until the end of the year so we'll be very slow on code review until the beginning of January.

@jaysoo
Copy link
Contributor Author

jaysoo commented Dec 24, 2015

@mkonicek Thanks for the feedback. I'll make the changes tonight. :)

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from 897c930 to 4e62be9 Compare December 24, 2015 01:34
@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from 4e62be9 to f664a4c Compare December 24, 2015 01:39
@jaysoo
Copy link
Contributor Author

jaysoo commented Dec 24, 2015

@mkonicek Updated the PR:

  1. Used reactContext.getSystemService instead of reactContext.getApplicationContext().getSystemService.
  2. Wrapped the entire block with try-catch, with a FLog.e for the caught exception.

@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@satya164 satya164 changed the title Use getRealMetrics for display metrics Use getRealMetrics for display metrics. Closes #4934 Dec 24, 2015
@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to https://our.intern.facebook.com/intern/opensource/github/pull_request/884116028375399/int_phab to review.

@satya164
Copy link
Contributor

ping @bestander :)

@bestander
Copy link
Contributor

bumped @astreet, he controls the ship

@mkonicek
Copy link
Contributor

Looks like internal integration tests are failing but can't tell if it's related. Open sourcing them will help.

@bestander
Copy link
Contributor

on it now

@bestander
Copy link
Contributor

Hi @jaysoo, I figured out what the problem was.
We have quite a few internal tests uimanager class and this mGetRawW.invoke(display); throws an error in tests environment.

First I tried to put a fix in your PR to make the dimensions mockable but failed to find a quick way to do it.
As a matter of fact we are working on open sourcing a big bunch of tests to make contribution experience super productive and pleasant.
Tomorrow I am going to push to master all the tests related to uimanager.
Could you rebase on master after that and see an easy way to mock the Display dimensions for tests?

@jaysoo
Copy link
Contributor Author

jaysoo commented Jan 26, 2016

@bestander Sure, I'd be happy to. :)

Is there anything I should keep in mind for the tests?

@bestander
Copy link
Contributor

@jaysoo, nothing too special.
The tests are creating UIManagerModule and run in JVM environment with Android SDK emulated by robolectric.

Here is a sample I am importing:

package com.facebook.react.uimanager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.graphics.Color;
import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.facebook.react.ReactRootView;
import com.facebook.react.animation.Animation;
import com.facebook.react.animation.AnimationPropertyUpdater;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.SimpleArray;
import com.facebook.react.bridge.SimpleMap;
import com.facebook.react.views.text.ReactRawTextManager;
import com.facebook.react.views.text.ReactTextShadowNode;
import com.facebook.react.views.text.ReactTextViewManager;
import com.facebook.react.views.view.ReactViewGroup;
import com.facebook.react.views.view.ReactViewManager;
import com.facebook.react.bridge.ReactTestHelper;

import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.junit.Rule;
import org.robolectric.RobolectricTestRunner;

import org.robolectric.RuntimeEnvironment;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.Robolectric;

import static org.fest.assertions.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * Tests for {@link UIManagerModule}.
 */
@PrepareForTest({Arguments.class, ReactChoreographer.class})
@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
public class UIManagerModuleTest {

  @Rule
  public PowerMockRule rule = new PowerMockRule();

  private ReactApplicationContext mReactContext;
  private CatalystInstance mCatalystInstanceMock;
  private ArrayList<Choreographer.FrameCallback> mPendingChoreographerCallbacks;

  @Before
  public void setUp() {
    PowerMockito.mockStatic(Arguments.class, ReactChoreographer.class);

    ReactChoreographer choreographerMock = mock(ReactChoreographer.class);
    PowerMockito.when(Arguments.createArray()).thenAnswer(new Answer<Object>() {
      @Override
      public Object answer(InvocationOnMock invocation) throws Throwable {
        return new SimpleArray();
      }
    });
    PowerMockito.when(Arguments.createMap()).thenAnswer(new Answer<Object>() {
      @Override
      public Object answer(InvocationOnMock invocation) throws Throwable {
        return new SimpleMap();
      }
    });
    PowerMockito.when(ReactChoreographer.getInstance()).thenReturn(choreographerMock);

    mPendingChoreographerCallbacks = new ArrayList<>();
    doAnswer(new Answer() {
      @Override
      public Object answer(InvocationOnMock invocation) throws Throwable {
        mPendingChoreographerCallbacks
            .add((Choreographer.FrameCallback) invocation.getArguments()[1]);
        return null;
      }
    }).when(choreographerMock).postFrameCallback(
        any(ReactChoreographer.CallbackType.class),
        any(Choreographer.FrameCallback.class));

    mCatalystInstanceMock = ReactTestHelper.createMockCatalystInstance();
    mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
    mReactContext.initializeWithInstance(mCatalystInstanceMock);

    UIManagerModule uiManagerModuleMock = mock(UIManagerModule.class);
    when(mCatalystInstanceMock.getNativeModule(UIManagerModule.class))
        .thenReturn(uiManagerModuleMock);
  }

  @Test
  public void testCreateSimpleHierarchy() {
    UIManagerModule uiManager = getUIManagerModule();

    ViewGroup rootView = createSimpleTextHierarchy(uiManager, "Some text");

    assertThat(rootView.getChildCount()).isEqualTo(1);

    View firstChild = rootView.getChildAt(0);
    assertThat(firstChild).isInstanceOf(TextView.class);
    assertThat(((TextView) firstChild).getText().toString()).isEqualTo("Some text");
  }

private void executePendingChoreographerCallbacks() {
    ArrayList<Choreographer.FrameCallback> callbacks =
        new ArrayList<>(mPendingChoreographerCallbacks);
    mPendingChoreographerCallbacks.clear();
    for (Choreographer.FrameCallback frameCallback : callbacks) {
      frameCallback.doFrame(0);
    }
  }

  private UIManagerModule getUIManagerModule() {
    List<ViewManager> viewManagers = Arrays.<ViewManager>asList(
        new ReactViewManager(),
        new ReactTextViewManager(),
        new ReactRawTextManager());
    UIManagerModule uiManagerModule =  new UIManagerModule(
        mReactContext,
        viewManagers,
        new UIImplementation(mReactContext, viewManagers));
    uiManagerModule.onHostResume();
    return uiManagerModule;
  }
}

@bestander
Copy link
Contributor

@jaysoo the tests are in master branch, could you rebase?
To run tests locally:
gradlew :ReactAndroid:testDebugUnitTest

CI will report the tests properly in about an hour when #5582 lands

@jaysoo jaysoo force-pushed the 4934-android-window-height branch from 50fd080 to e09f29e Compare January 28, 2016 18:33
@facebook-github-bot
Copy link
Contributor

@jaysoo updated the pull request.

@jaysoo
Copy link
Contributor Author

jaysoo commented Jan 28, 2016

@bestander taking a quick look now. Sorry for delay.

@jaysoo
Copy link
Contributor Author

jaysoo commented Jan 28, 2016

@bestander Ran gradlew :ReactAndroid:testDebugUnitTest and it completed successfully. Anything else I should do or check?

@bestander
Copy link
Contributor

Now it feels strange 0_o

@bestander
Copy link
Contributor

I was quite sure that tests would fail in circleCI but they passed just fine.
Must be something in the internal FB environment that runs the tests...
Let me try merging it again and maybe do the necessary changes against the internal CI.

@bestander
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot
Copy link
Contributor

Thanks for importing. If you are an FB employee go to https://our.intern.facebook.com/intern/opensource/github/pull_request/884116028375399/int_phab to review.

@bestander
Copy link
Contributor

@jaysoo just a heads up, I found the issue with our internal test runners.
We are running some tests with robolectric 2 which caused the problem.
It will be easy for me to migrate the remaining tests to OSS and robolectric 3.
Sorry for the delay, I'll deal with the rest and get this PR merged early next week

@ghost ghost closed this in 8e60394 Feb 1, 2016
@bestander
Copy link
Contributor

2832137_o

@jaysoo
Copy link
Contributor Author

jaysoo commented Feb 1, 2016

@bestander You sir are awesome! Thanks for the help! And everyone else on this issue. 💯

@ide
Copy link
Contributor

ide commented Feb 4, 2016

The real metrics probably should be exposed via Dimensions.get('screen') instead of Dimensions.get('window') (on iPhone these two would be equal). The common case for apps is that the software nav buttons on Android are ever-present and Dimensions.get('window') should account for them. Otherwise this is going to break a lot of cross-platform apps that use Dimensions.

ghost pushed a commit that referenced this pull request Feb 11, 2016
Summary:
public
#4935 changed the window dimensions for android by replacing them with the actual screen dimensions. This changes the window dimensions back to their original values and adds `Dimensions.get('screen')` for the actual screen dimensions of the device.

Reviewed By: astreet

Differential Revision: D2921584

fb-gh-sync-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
shipit-source-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
bestander pushed a commit that referenced this pull request Feb 15, 2016
Summary:
public
#4935 changed the window dimensions for android by replacing them with the actual screen dimensions. This changes the window dimensions back to their original values and adds `Dimensions.get('screen')` for the actual screen dimensions of the device.

Reviewed By: astreet

Differential Revision: D2921584

fb-gh-sync-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
shipit-source-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
pglotov pushed a commit to pglotov/react-native that referenced this pull request Mar 15, 2016
Summary:
public
facebook#4935 changed the window dimensions for android by replacing them with the actual screen dimensions. This changes the window dimensions back to their original values and adds `Dimensions.get('screen')` for the actual screen dimensions of the device.

Reviewed By: astreet

Differential Revision: D2921584

fb-gh-sync-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
shipit-source-id: 5d2677029c71d50691691dc651a11e9c8b115e8f
@Larney11
Copy link

Larney11 commented Aug 6, 2016

Solution
Check out this StackOverflow solution.

This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants