• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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