blob: b372d5333d571eec0a512d3f038171e0ae2cc880 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.server.wm;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.WindowManagerState.dpToPx;
import static android.server.wm.app.Components.BOTTOM_LEFT_LAYOUT_ACTIVITY;
import static android.server.wm.app.Components.BOTTOM_RIGHT_LAYOUT_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
import static android.server.wm.app.Components.TOP_LEFT_LAYOUT_ACTIVITY;
import static android.server.wm.app.Components.TOP_RIGHT_LAYOUT_ACTIVITY;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsets.Type.captionBar;
import static android.view.WindowInsets.Type.systemBars;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.WindowState;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.WindowMetrics;
import org.junit.Test;
import java.util.List;
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ManifestLayoutTests
*/
@Presubmit
public class ManifestLayoutTests extends ActivityManagerTestBase {
// Test parameters
private static final int DEFAULT_WIDTH_DP = 240;
private static final int DEFAULT_HEIGHT_DP = 160;
private static final float DEFAULT_WIDTH_FRACTION = 0.50f;
private static final float DEFAULT_HEIGHT_FRACTION = 0.70f;
private static final int MIN_WIDTH_DP = 100;
private static final int MIN_HEIGHT_DP = 80;
private static final int GRAVITY_VER_CENTER = 0x01;
private static final int GRAVITY_VER_TOP = 0x02;
private static final int GRAVITY_VER_BOTTOM = 0x04;
private static final int GRAVITY_HOR_CENTER = 0x10;
private static final int GRAVITY_HOR_LEFT = 0x20;
private static final int GRAVITY_HOR_RIGHT = 0x40;
private WindowManagerState.DisplayContent mDisplay;
private WindowState mWindowState;
@Test
public void testGravityAndDefaultSizeTopLeft() throws Exception {
testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/);
}
@Test
public void testGravityAndDefaultSizeTopRight() throws Exception {
testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/);
}
@Test
public void testGravityAndDefaultSizeBottomLeft() throws Exception {
testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/);
}
@Test
public void testGravityAndDefaultSizeBottomRight() throws Exception {
testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/);
}
@Test
public void testMinimalSizeFreeform() throws Exception {
assumeTrue("Skipping test: no freeform support", supportsFreeform());
testMinimalSize(true /* freeform */);
}
@Test
@Presubmit
public void testMinimalSizeDocked() throws Exception {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
testMinimalSize(false /* freeform */);
}
private void testMinimalSize(boolean freeform) throws Exception {
// Issue command to resize to <0,0,1,1>. We expect the size to be floored at
// MIN_WIDTH_DPxMIN_HEIGHT_DP.
if (freeform) {
launchActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY, WINDOWING_MODE_FREEFORM);
resizeActivityTask(BOTTOM_RIGHT_LAYOUT_ACTIVITY, 0, 0, 1, 1);
} else { // stackId == DOCKED_STACK_ID
launchActivitiesInSplitScreen(
getLaunchActivityBuilder().setTargetActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY),
getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY));
mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, 1, 1));
}
getDisplayAndWindowState(BOTTOM_RIGHT_LAYOUT_ACTIVITY, false);
// Use default density because ActivityInfo.WindowLayout is initialized by that.
final int minWidth = dpToPx(MIN_WIDTH_DP, DisplayMetrics.DENSITY_DEVICE_STABLE);
final int minHeight = dpToPx(MIN_HEIGHT_DP, DisplayMetrics.DENSITY_DEVICE_STABLE);
final Rect parentFrame = mWindowState.getParentFrame();
final int cutoutSize = getCutoutSizeByHorGravity(GRAVITY_HOR_LEFT);
assertEquals("Min width is incorrect", minWidth, parentFrame.width() + cutoutSize);
assertEquals("Min height is incorrect", minHeight, parentFrame.height());
}
private void testLayout(
int vGravity, int hGravity, boolean fraction) throws Exception {
assumeTrue("Skipping test: no freeform support", supportsFreeform());
final ComponentName activityName;
if (vGravity == GRAVITY_VER_TOP) {
activityName = (hGravity == GRAVITY_HOR_LEFT) ? TOP_LEFT_LAYOUT_ACTIVITY
: TOP_RIGHT_LAYOUT_ACTIVITY;
} else {
activityName = (hGravity == GRAVITY_HOR_LEFT) ? BOTTOM_LEFT_LAYOUT_ACTIVITY
: BOTTOM_RIGHT_LAYOUT_ACTIVITY;
}
// Launch in freeform stack
launchActivity(activityName, WINDOWING_MODE_FREEFORM);
getDisplayAndWindowState(activityName, true);
final Rect parentFrame = mWindowState.getParentFrame();
final WindowMetrics windowMetrics = mWm.getMaximumWindowMetrics();
final Rect stableBounds = new Rect(windowMetrics.getBounds());
stableBounds.inset(windowMetrics.getWindowInsets().getInsetsIgnoringVisibility(
systemBars() & ~captionBar()));
final int expectedWidthPx, expectedHeightPx;
// Evaluate the expected window size in px. If we're using fraction dimensions,
// calculate the size based on the app rect size. Otherwise, convert the expected
// size in dp to px.
if (fraction) {
expectedWidthPx = (int) (stableBounds.width() * DEFAULT_WIDTH_FRACTION);
expectedHeightPx = (int) (stableBounds.height() * DEFAULT_HEIGHT_FRACTION);
} else {
final int densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi);
expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi);
}
verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx,
parentFrame, stableBounds);
}
private void getDisplayAndWindowState(ComponentName activityName, boolean checkFocus)
throws Exception {
final String windowName = getWindowName(activityName);
mWmState.computeState(activityName);
if (checkFocus) {
mWmState.assertFocusedWindow("Test window must be the front window.", windowName);
} else {
mWmState.assertVisibility(activityName, true);
}
final List<WindowState> windowList =
mWmState.getMatchingVisibleWindowState(windowName);
assertEquals("Should have exactly one window state for the activity.",
1, windowList.size());
mWindowState = windowList.get(0);
assertNotNull("Should have a valid window", mWindowState);
mDisplay = mWmState.getDisplay(mWindowState.getDisplayId());
assertNotNull("Should be on a display", mDisplay);
}
private void verifyFrameSizeAndPosition(
int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx,
Rect parentFrame, Rect stableBounds) {
final int cutoutSize = getCutoutSizeByHorGravity(hGravity);
assertEquals("Width is incorrect",
expectedWidthPx, parentFrame.width() + cutoutSize);
assertEquals("Height is incorrect", expectedHeightPx, parentFrame.height());
if (vGravity == GRAVITY_VER_TOP) {
assertEquals("Should be on the top", stableBounds.top, parentFrame.top);
} else if (vGravity == GRAVITY_VER_BOTTOM) {
assertEquals("Should be on the bottom", stableBounds.bottom, parentFrame.bottom);
}
if (hGravity == GRAVITY_HOR_LEFT) {
assertEquals("Should be on the left",
stableBounds.left, parentFrame.left - cutoutSize);
} else if (hGravity == GRAVITY_HOR_RIGHT){
assertEquals("Should be on the right",
stableBounds.right, parentFrame.right + cutoutSize);
}
}
private int getCutoutSizeByHorGravity(int hGravity) {
DisplayCutout cutout = mDm.getDisplay(DEFAULT_DISPLAY).getCutout();
if (cutout == null) {
return 0;
}
// When the layoutInDisplayCutoutMode is default, the status bar & navigation bar already
// take top and bottom cutout into account.
// Here we only need to account for left & right cutout areas.
if (hGravity == GRAVITY_HOR_LEFT) {
return cutout.getSafeInsetLeft();
} else if (hGravity == GRAVITY_HOR_RIGHT) {
return cutout.getSafeInsetRight();
} else {
return 0;
}
}
}