1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 20 import static android.server.wm.ComponentNameUtils.getWindowName; 21 import static android.server.wm.WindowManagerState.dpToPx; 22 import static android.server.wm.app.Components.BOTTOM_LEFT_LAYOUT_ACTIVITY; 23 import static android.server.wm.app.Components.BOTTOM_RIGHT_LAYOUT_ACTIVITY; 24 import static android.server.wm.app.Components.TEST_ACTIVITY; 25 import static android.server.wm.app.Components.TOP_LEFT_LAYOUT_ACTIVITY; 26 import static android.server.wm.app.Components.TOP_RIGHT_LAYOUT_ACTIVITY; 27 import static android.view.Display.DEFAULT_DISPLAY; 28 import static android.view.WindowInsets.Type.captionBar; 29 import static android.view.WindowInsets.Type.systemBars; 30 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertNotNull; 33 import static org.junit.Assume.assumeTrue; 34 35 import android.content.ComponentName; 36 import android.graphics.Rect; 37 import android.platform.test.annotations.Presubmit; 38 import android.server.wm.WindowManagerState.WindowState; 39 import android.view.DisplayCutout; 40 import android.view.WindowMetrics; 41 42 import org.junit.Test; 43 44 import java.util.List; 45 46 /** 47 * Build/Install/Run: 48 * atest CtsWindowManagerDeviceTestCases:ManifestLayoutTests 49 */ 50 @Presubmit 51 public class ManifestLayoutTests extends ActivityManagerTestBase { 52 53 // Test parameters 54 private static final int DEFAULT_WIDTH_DP = 240; 55 private static final int DEFAULT_HEIGHT_DP = 160; 56 private static final float DEFAULT_WIDTH_FRACTION = 0.50f; 57 private static final float DEFAULT_HEIGHT_FRACTION = 0.70f; 58 private static final int MIN_WIDTH_DP = 100; 59 private static final int MIN_HEIGHT_DP = 80; 60 61 private static final int GRAVITY_VER_CENTER = 0x01; 62 private static final int GRAVITY_VER_TOP = 0x02; 63 private static final int GRAVITY_VER_BOTTOM = 0x04; 64 private static final int GRAVITY_HOR_CENTER = 0x10; 65 private static final int GRAVITY_HOR_LEFT = 0x20; 66 private static final int GRAVITY_HOR_RIGHT = 0x40; 67 68 private WindowManagerState.DisplayContent mDisplay; 69 private WindowState mWindowState; 70 71 @Test testGravityAndDefaultSizeTopLeft()72 public void testGravityAndDefaultSizeTopLeft() throws Exception { 73 testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/); 74 } 75 76 @Test testGravityAndDefaultSizeTopRight()77 public void testGravityAndDefaultSizeTopRight() throws Exception { 78 testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/); 79 } 80 81 @Test testGravityAndDefaultSizeBottomLeft()82 public void testGravityAndDefaultSizeBottomLeft() throws Exception { 83 testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/); 84 } 85 86 @Test testGravityAndDefaultSizeBottomRight()87 public void testGravityAndDefaultSizeBottomRight() throws Exception { 88 testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/); 89 } 90 91 @Test testMinimalSizeFreeform()92 public void testMinimalSizeFreeform() throws Exception { 93 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 94 95 testMinimalSize(true /* freeform */); 96 } 97 98 @Test 99 @Presubmit testMinimalSizeDocked()100 public void testMinimalSizeDocked() throws Exception { 101 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 102 103 testMinimalSize(false /* freeform */); 104 } 105 testMinimalSize(boolean freeform)106 private void testMinimalSize(boolean freeform) throws Exception { 107 // Issue command to resize to <0,0,1,1>. We expect the size to be floored at 108 // MIN_WIDTH_DPxMIN_HEIGHT_DP. 109 if (freeform) { 110 launchActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY, WINDOWING_MODE_FREEFORM); 111 resizeActivityTask(BOTTOM_RIGHT_LAYOUT_ACTIVITY, 0, 0, 1, 1); 112 } else { // stackId == DOCKED_STACK_ID 113 launchActivitiesInSplitScreen( 114 getLaunchActivityBuilder().setTargetActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY), 115 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 116 mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, 1, 1)); 117 } 118 getDisplayAndWindowState(BOTTOM_RIGHT_LAYOUT_ACTIVITY, false); 119 120 final int minWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi()); 121 final int minHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi()); 122 final Rect containingRect = mWindowState.getContainingFrame(); 123 final int cutoutSize = getCutoutSizeByHorGravity(GRAVITY_HOR_LEFT); 124 125 assertEquals("Min width is incorrect", minWidth, 126 containingRect.width() + cutoutSize); 127 assertEquals("Min height is incorrect", minHeight, containingRect.height()); 128 } 129 testLayout( int vGravity, int hGravity, boolean fraction)130 private void testLayout( 131 int vGravity, int hGravity, boolean fraction) throws Exception { 132 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 133 134 final ComponentName activityName; 135 if (vGravity == GRAVITY_VER_TOP) { 136 activityName = (hGravity == GRAVITY_HOR_LEFT) ? TOP_LEFT_LAYOUT_ACTIVITY 137 : TOP_RIGHT_LAYOUT_ACTIVITY; 138 } else { 139 activityName = (hGravity == GRAVITY_HOR_LEFT) ? BOTTOM_LEFT_LAYOUT_ACTIVITY 140 : BOTTOM_RIGHT_LAYOUT_ACTIVITY; 141 } 142 143 // Launch in freeform stack 144 launchActivity(activityName, WINDOWING_MODE_FREEFORM); 145 146 getDisplayAndWindowState(activityName, true); 147 148 final Rect containingRect = mWindowState.getContainingFrame(); 149 final WindowMetrics windowMetrics = mWm.getMaximumWindowMetrics(); 150 final Rect stableBounds = new Rect(windowMetrics.getBounds()); 151 stableBounds.inset(windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( 152 systemBars() & ~captionBar())); 153 final int expectedWidthPx, expectedHeightPx; 154 // Evaluate the expected window size in px. If we're using fraction dimensions, 155 // calculate the size based on the app rect size. Otherwise, convert the expected 156 // size in dp to px. 157 if (fraction) { 158 expectedWidthPx = (int) (stableBounds.width() * DEFAULT_WIDTH_FRACTION); 159 expectedHeightPx = (int) (stableBounds.height() * DEFAULT_HEIGHT_FRACTION); 160 } else { 161 final int densityDpi = mDisplay.getDpi(); 162 expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi); 163 expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi); 164 } 165 166 verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx, 167 containingRect, stableBounds); 168 } 169 getDisplayAndWindowState(ComponentName activityName, boolean checkFocus)170 private void getDisplayAndWindowState(ComponentName activityName, boolean checkFocus) 171 throws Exception { 172 final String windowName = getWindowName(activityName); 173 174 mWmState.computeState(activityName); 175 176 if (checkFocus) { 177 mWmState.assertFocusedWindow("Test window must be the front window.", windowName); 178 } else { 179 mWmState.assertVisibility(activityName, true); 180 } 181 182 final List<WindowState> windowList = 183 mWmState.getMatchingVisibleWindowState(windowName); 184 185 assertEquals("Should have exactly one window state for the activity.", 186 1, windowList.size()); 187 188 mWindowState = windowList.get(0); 189 assertNotNull("Should have a valid window", mWindowState); 190 191 mDisplay = mWmState.getDisplay(mWindowState.getDisplayId()); 192 assertNotNull("Should be on a display", mDisplay); 193 } 194 verifyFrameSizeAndPosition( int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx, Rect containingFrame, Rect parentFrame)195 private void verifyFrameSizeAndPosition( 196 int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx, 197 Rect containingFrame, Rect parentFrame) { 198 final int cutoutSize = getCutoutSizeByHorGravity(hGravity); 199 assertEquals("Width is incorrect", 200 expectedWidthPx, containingFrame.width() + cutoutSize); 201 assertEquals("Height is incorrect", expectedHeightPx, containingFrame.height()); 202 203 if (vGravity == GRAVITY_VER_TOP) { 204 assertEquals("Should be on the top", parentFrame.top, containingFrame.top); 205 } else if (vGravity == GRAVITY_VER_BOTTOM) { 206 assertEquals("Should be on the bottom", parentFrame.bottom, containingFrame.bottom); 207 } 208 209 if (hGravity == GRAVITY_HOR_LEFT) { 210 assertEquals("Should be on the left", 211 parentFrame.left, containingFrame.left - cutoutSize); 212 } else if (hGravity == GRAVITY_HOR_RIGHT){ 213 assertEquals("Should be on the right", 214 parentFrame.right, containingFrame.right + cutoutSize); 215 } 216 } 217 getCutoutSizeByHorGravity(int hGravity)218 private int getCutoutSizeByHorGravity(int hGravity) { 219 DisplayCutout cutout = mDm.getDisplay(DEFAULT_DISPLAY).getCutout(); 220 if (cutout == null) { 221 return 0; 222 } 223 224 // When the layoutInDisplayCutoutMode is default, the status bar & navigation bar already 225 // take top and bottom cutout into account. 226 // Here we only need to account for left & right cutout areas. 227 if (hGravity == GRAVITY_HOR_LEFT) { 228 return cutout.getSafeInsetLeft(); 229 } else if (hGravity == GRAVITY_HOR_RIGHT) { 230 return cutout.getSafeInsetRight(); 231 } else { 232 return 0; 233 } 234 } 235 } 236