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.Assert.assertTrue; 34 import static org.junit.Assume.assumeTrue; 35 36 import android.content.ComponentName; 37 import android.graphics.Rect; 38 import android.platform.test.annotations.Presubmit; 39 import android.server.wm.WindowManagerState.WindowState; 40 import android.util.DisplayMetrics; 41 import android.view.DisplayCutout; 42 import android.view.WindowMetrics; 43 44 import org.junit.Test; 45 46 import java.util.List; 47 48 /** 49 * Build/Install/Run: 50 * atest CtsWindowManagerDeviceTestCases:ManifestLayoutTests 51 */ 52 @Presubmit 53 public class ManifestLayoutTests extends ActivityManagerTestBase { 54 55 // Test parameters 56 private static final int DEFAULT_WIDTH_DP = 240; 57 private static final int DEFAULT_HEIGHT_DP = 160; 58 private static final float DEFAULT_WIDTH_FRACTION = 0.50f; 59 private static final float DEFAULT_HEIGHT_FRACTION = 0.70f; 60 private static final int MIN_WIDTH_DP = 100; 61 private static final int MIN_HEIGHT_DP = 80; 62 63 private static final int GRAVITY_VER_CENTER = 0x01; 64 private static final int GRAVITY_VER_TOP = 0x02; 65 private static final int GRAVITY_VER_BOTTOM = 0x04; 66 private static final int GRAVITY_HOR_CENTER = 0x10; 67 private static final int GRAVITY_HOR_LEFT = 0x20; 68 private static final int GRAVITY_HOR_RIGHT = 0x40; 69 70 private WindowManagerState.DisplayContent mDisplay; 71 private WindowState mWindowState; 72 73 @Test testGravityAndDefaultSizeTopLeft()74 public void testGravityAndDefaultSizeTopLeft() throws Exception { 75 testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_LEFT, false /*fraction*/); 76 } 77 78 @Test testGravityAndDefaultSizeTopRight()79 public void testGravityAndDefaultSizeTopRight() throws Exception { 80 testLayout(GRAVITY_VER_TOP, GRAVITY_HOR_RIGHT, true /*fraction*/); 81 } 82 83 @Test testGravityAndDefaultSizeBottomLeft()84 public void testGravityAndDefaultSizeBottomLeft() throws Exception { 85 testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_LEFT, true /*fraction*/); 86 } 87 88 @Test testGravityAndDefaultSizeBottomRight()89 public void testGravityAndDefaultSizeBottomRight() throws Exception { 90 testLayout(GRAVITY_VER_BOTTOM, GRAVITY_HOR_RIGHT, false /*fraction*/); 91 } 92 93 @Test testMinimalSizeFreeform()94 public void testMinimalSizeFreeform() throws Exception { 95 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 96 97 testMinimalSize(true /* freeform */); 98 } 99 100 @Test 101 @Presubmit testMinimalSizeDocked()102 public void testMinimalSizeDocked() throws Exception { 103 assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow()); 104 105 testMinimalSize(false /* freeform */); 106 } 107 testMinimalSize(boolean freeform)108 private void testMinimalSize(boolean freeform) throws Exception { 109 // Issue command to resize to <0,0,1,1>. We expect the size to be floored at 110 // MIN_WIDTH_DPxMIN_HEIGHT_DP. 111 if (freeform) { 112 launchActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY, WINDOWING_MODE_FREEFORM); 113 resizeActivityTask(BOTTOM_RIGHT_LAYOUT_ACTIVITY, 0, 0, 1, 1); 114 } else { // stackId == DOCKED_STACK_ID 115 launchActivitiesInSplitScreen( 116 getLaunchActivityBuilder().setTargetActivity(BOTTOM_RIGHT_LAYOUT_ACTIVITY), 117 getLaunchActivityBuilder().setTargetActivity(TEST_ACTIVITY)); 118 mTaskOrganizer.setRootPrimaryTaskBounds(new Rect(0, 0, 1, 1)); 119 } 120 getDisplayAndWindowState(BOTTOM_RIGHT_LAYOUT_ACTIVITY, false); 121 122 // Use default density because ActivityInfo.WindowLayout is initialized by that. 123 final int minWidth = dpToPx(MIN_WIDTH_DP, DisplayMetrics.DENSITY_DEVICE_STABLE); 124 final int minHeight = dpToPx(MIN_HEIGHT_DP, DisplayMetrics.DENSITY_DEVICE_STABLE); 125 126 // The alternative size of the current display density. 127 final int alternativeMinWidth = dpToPx(MIN_WIDTH_DP, mDisplay.getDpi()); 128 final int alternativeMinHeight = dpToPx(MIN_HEIGHT_DP, mDisplay.getDpi()); 129 130 final Rect parentFrame = mWindowState.getParentFrame(); 131 final int cutoutSize = getCutoutSizeByHorGravity(GRAVITY_HOR_LEFT); 132 final int actualWidth = parentFrame.width() + cutoutSize; 133 final int actualHeight = parentFrame.height(); 134 135 assertTrue("Min width is incorrect", 136 (actualWidth == minWidth || actualWidth == alternativeMinWidth)); 137 assertTrue("Min height is incorrect", 138 (actualHeight == minHeight || actualHeight == alternativeMinHeight)); 139 } 140 testLayout( int vGravity, int hGravity, boolean fraction)141 private void testLayout( 142 int vGravity, int hGravity, boolean fraction) throws Exception { 143 assumeTrue("Skipping test: no freeform support", supportsFreeform()); 144 145 final ComponentName activityName; 146 if (vGravity == GRAVITY_VER_TOP) { 147 activityName = (hGravity == GRAVITY_HOR_LEFT) ? TOP_LEFT_LAYOUT_ACTIVITY 148 : TOP_RIGHT_LAYOUT_ACTIVITY; 149 } else { 150 activityName = (hGravity == GRAVITY_HOR_LEFT) ? BOTTOM_LEFT_LAYOUT_ACTIVITY 151 : BOTTOM_RIGHT_LAYOUT_ACTIVITY; 152 } 153 154 // Launch in freeform stack 155 launchActivity(activityName, WINDOWING_MODE_FREEFORM); 156 157 getDisplayAndWindowState(activityName, true); 158 159 final Rect parentFrame = mWindowState.getParentFrame(); 160 final WindowMetrics windowMetrics = mWm.getMaximumWindowMetrics(); 161 final Rect stableBounds = new Rect(windowMetrics.getBounds()); 162 stableBounds.inset(windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( 163 systemBars() & ~captionBar())); 164 final int expectedWidthPx, expectedHeightPx; 165 // Evaluate the expected window size in px. If we're using fraction dimensions, 166 // calculate the size based on the app rect size. Otherwise, convert the expected 167 // size in dp to px. 168 if (fraction) { 169 expectedWidthPx = (int) (stableBounds.width() * DEFAULT_WIDTH_FRACTION); 170 expectedHeightPx = (int) (stableBounds.height() * DEFAULT_HEIGHT_FRACTION); 171 } else { 172 final int densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE; 173 expectedWidthPx = dpToPx(DEFAULT_WIDTH_DP, densityDpi); 174 expectedHeightPx = dpToPx(DEFAULT_HEIGHT_DP, densityDpi); 175 } 176 177 verifyFrameSizeAndPosition(vGravity, hGravity, expectedWidthPx, expectedHeightPx, 178 parentFrame, stableBounds); 179 } 180 getDisplayAndWindowState(ComponentName activityName, boolean checkFocus)181 private void getDisplayAndWindowState(ComponentName activityName, boolean checkFocus) 182 throws Exception { 183 final String windowName = getWindowName(activityName); 184 185 mWmState.computeState(activityName); 186 187 if (checkFocus) { 188 mWmState.assertFocusedWindow("Test window must be the front window.", windowName); 189 } else { 190 mWmState.assertVisibility(activityName, true); 191 } 192 193 final List<WindowState> windowList = 194 mWmState.getMatchingVisibleWindowState(windowName); 195 196 assertEquals("Should have exactly one window state for the activity.", 197 1, windowList.size()); 198 199 mWindowState = windowList.get(0); 200 assertNotNull("Should have a valid window", mWindowState); 201 202 mDisplay = mWmState.getDisplay(mWindowState.getDisplayId()); 203 assertNotNull("Should be on a display", mDisplay); 204 } 205 verifyFrameSizeAndPosition( int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx, Rect parentFrame, Rect stableBounds)206 private void verifyFrameSizeAndPosition( 207 int vGravity, int hGravity, int expectedWidthPx, int expectedHeightPx, 208 Rect parentFrame, Rect stableBounds) { 209 final int cutoutSize = getCutoutSizeByHorGravity(hGravity); 210 assertEquals("Width is incorrect", 211 expectedWidthPx, parentFrame.width() + cutoutSize); 212 assertEquals("Height is incorrect", expectedHeightPx, parentFrame.height()); 213 214 if (vGravity == GRAVITY_VER_TOP) { 215 assertEquals("Should be on the top", stableBounds.top, parentFrame.top); 216 } else if (vGravity == GRAVITY_VER_BOTTOM) { 217 assertEquals("Should be on the bottom", stableBounds.bottom, parentFrame.bottom); 218 } 219 220 if (hGravity == GRAVITY_HOR_LEFT) { 221 assertEquals("Should be on the left", 222 stableBounds.left, parentFrame.left - cutoutSize); 223 } else if (hGravity == GRAVITY_HOR_RIGHT){ 224 assertEquals("Should be on the right", 225 stableBounds.right, parentFrame.right + cutoutSize); 226 } 227 } 228 getCutoutSizeByHorGravity(int hGravity)229 private int getCutoutSizeByHorGravity(int hGravity) { 230 DisplayCutout cutout = mDm.getDisplay(DEFAULT_DISPLAY).getCutout(); 231 if (cutout == null) { 232 return 0; 233 } 234 235 // When the layoutInDisplayCutoutMode is default, the status bar & navigation bar already 236 // take top and bottom cutout into account. 237 // Here we only need to account for left & right cutout areas. 238 if (hGravity == GRAVITY_HOR_LEFT) { 239 return cutout.getSafeInsetLeft(); 240 } else if (hGravity == GRAVITY_HOR_RIGHT) { 241 return cutout.getSafeInsetRight(); 242 } else { 243 return 0; 244 } 245 } 246 } 247