/* * 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.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); final int minWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi()); final int minHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi()); final Rect containingRect = mWindowState.getContainingFrame(); final int cutoutSize = getCutoutSizeByHorGravity(GRAVITY_HOR_LEFT); assertEquals("Min width is incorrect", minWidth, containingRect.width() + cutoutSize); assertEquals("Min height is incorrect", minHeight, containingRect.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 containingRect = mWindowState.getContainingFrame(); 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 = mDisplay.getDpi(); expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi); expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi); } verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx, containingRect, 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 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 containingFrame, Rect parentFrame) { final int cutoutSize = getCutoutSizeByHorGravity(hGravity); assertEquals("Width is incorrect", expectedWidthPx, containingFrame.width() + cutoutSize); assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height()); if (vGravity == GRAVITY_VER_TOP) { assertEquals("Should be on the top", parentFrame.top, containingFrame.top); } else if (vGravity == GRAVITY_VER_BOTTOM) { assertEquals("Should be on the bottom", parentFrame.bottom, containingFrame.bottom); } if (hGravity == GRAVITY_HOR_LEFT) { assertEquals("Should be on the left", parentFrame.left, containingFrame.left - cutoutSize); } else if (hGravity == GRAVITY_HOR_RIGHT){ assertEquals("Should be on the right", parentFrame.right, containingFrame.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; } } }