WindowFrameTests.java revision af691c0be7bbfea63e880dd717c51a38a0bc874a
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
17package com.android.server.wm;
18
19import org.junit.Before;
20import org.junit.Test;
21import org.junit.runner.RunWith;
22
23import android.app.ActivityManager.TaskDescription;
24import android.content.Context;
25import android.graphics.Rect;
26import android.platform.test.annotations.Presubmit;
27import android.support.test.InstrumentationRegistry;
28import android.support.test.filters.SmallTest;
29import android.support.test.runner.AndroidJUnit4;
30import android.view.DisplayInfo;
31import android.view.Gravity;
32import android.view.IWindow;
33import android.view.WindowManager;
34
35import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
36import static android.view.WindowManager.LayoutParams.FILL_PARENT;
37import static org.junit.Assert.assertEquals;
38import static org.junit.Assert.assertTrue;
39
40/**
41 * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
42 *
43 * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
44 */
45@SmallTest
46@Presubmit
47@RunWith(AndroidJUnit4.class)
48public class WindowFrameTests extends WindowTestsBase {
49
50    private WindowToken mWindowToken;
51    private final IWindow mIWindow = new TestIWindow();
52
53    class WindowStateWithTask extends WindowState {
54        final Task mTask;
55        boolean mDockedResizingForTest = false;
56        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
57            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0,
58                    false /* ownerCanAddInternalSystemWindow */);
59            mTask = t;
60        }
61
62        @Override
63        Task getTask() {
64            return mTask;
65        }
66
67        @Override
68        boolean isDockedResizing() {
69            return mDockedResizingForTest;
70        }
71    };
72
73    class TaskWithBounds extends Task {
74        final Rect mBounds;
75        final Rect mInsetBounds = new Rect();
76        boolean mFullscreenForTest = true;
77        TaskWithBounds(Rect bounds) {
78            super(0, mStubStack, 0, sWm, null, null, 0, false, false, new TaskDescription(), null);
79            mBounds = bounds;
80        }
81        @Override
82        void getBounds(Rect outBounds) {
83            outBounds.set(mBounds);
84        }
85        @Override
86        void getTempInsetBounds(Rect outBounds) {
87            outBounds.set(mInsetBounds);
88        }
89        @Override
90        boolean isFullscreen() {
91            return mFullscreenForTest;
92        }
93    }
94
95    TaskStack mStubStack;
96
97    @Before
98    public void setUp() throws Exception {
99        final Context context = InstrumentationRegistry.getTargetContext();
100        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
101
102        // Just any non zero value.
103        sWm.mSystemDecorLayer = 10000;
104
105        mWindowToken = new WindowTestUtils.TestAppWindowToken(sWm.getDefaultDisplayContentLocked());
106        mStubStack = new TaskStack(sWm, 0);
107    }
108
109    public void assertRect(Rect rect, int left, int top, int right, int bottom) {
110        assertEquals(left, rect.left);
111        assertEquals(top, rect.top);
112        assertEquals(right, rect.right);
113        assertEquals(bottom, rect.bottom);
114    }
115
116    @Test
117    public void testLayoutInFullscreenTaskInsets() throws Exception {
118        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
119        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
120        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
121
122        final int bottomContentInset = 100;
123        final int topContentInset = 50;
124        final int bottomVisibleInset = 30;
125        final int topVisibleInset = 70;
126        final int leftStableInset = 20;
127        final int rightStableInset = 90;
128
129        // With no insets or system decor all the frames incoming from PhoneWindowManager
130        // are identical.
131        final Rect pf = new Rect(0, 0, 1000, 1000);
132        final Rect df = pf;
133        final Rect of = df;
134        final Rect cf = new Rect(pf);
135        // Produce some insets
136        cf.top += 50;
137        cf.bottom -= 100;
138        final Rect vf = new Rect(pf);
139        vf.top += topVisibleInset;
140        vf.bottom -= bottomVisibleInset;
141        final Rect sf = new Rect(pf);
142        sf.left += leftStableInset;
143        sf.right -= rightStableInset;
144
145        final Rect dcf = pf;
146        // When mFrame extends past cf, the content insets are
147        // the difference between mFrame and ContentFrame. Visible
148        // and stable frames work the same way.
149        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
150        assertRect(w.mFrame,0, 0, 1000, 1000);
151        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
152        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
153        assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
154        // The frames remain as passed in shrunk to the window frame
155        assertTrue(cf.equals(w.getContentFrameLw()));
156        assertTrue(vf.equals(w.getVisibleFrameLw()));
157        assertTrue(sf.equals(w.getStableFrameLw()));
158        // On the other hand mFrame doesn't extend past cf we won't get any insets
159        w.mAttrs.x = 100;
160        w.mAttrs.y = 100;
161        w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
162        w.mRequestedWidth = 100;
163        w.mRequestedHeight = 100;
164        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
165        assertRect(w.mFrame, 100, 100, 200, 200);
166        assertRect(w.mContentInsets, 0, 0, 0, 0);
167        // In this case the frames are shrunk to the window frame.
168        assertTrue(w.mFrame.equals(w.getContentFrameLw()));
169        assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
170        assertTrue(w.mFrame.equals(w.getStableFrameLw()));
171    }
172
173    @Test
174    public void testLayoutInFullscreenTaskNoInsets() throws Exception {
175        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
176        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
177        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
178
179        // With no insets or system decor all the frames incoming from PhoneWindowManager
180        // are identical.
181        final Rect pf = new Rect(0, 0, 1000, 1000);
182
183        // Here the window has FILL_PARENT, FILL_PARENT
184        // so we expect it to fill the entire available frame.
185        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
186        assertRect(w.mFrame, 0, 0, 1000, 1000);
187
188        // It can select various widths and heights within the bounds.
189        // Strangely the window attribute width is ignored for normal windows
190        // and we use mRequestedWidth/mRequestedHeight
191        w.mAttrs.width = 300;
192        w.mAttrs.height = 300;
193        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
194        // Explicit width and height without requested width/height
195        // gets us nothing.
196        assertRect(w.mFrame, 0, 0, 0, 0);
197
198        w.mRequestedWidth = 300;
199        w.mRequestedHeight = 300;
200        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
201        // With requestedWidth/Height we can freely choose our size within the
202        // parent bounds.
203        assertRect(w.mFrame, 0, 0, 300, 300);
204
205        // With FLAG_SCALED though, requestedWidth/height is used to control
206        // the unscaled surface size, and mAttrs.width/height becomes the
207        // layout controller.
208        w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED;
209        w.mRequestedHeight = -1;
210        w.mRequestedWidth = -1;
211        w.mAttrs.width = 100;
212        w.mAttrs.height = 100;
213        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
214        assertRect(w.mFrame, 0, 0, 100, 100);
215        w.mAttrs.flags = 0;
216
217        // But sizes too large will be clipped to the containing frame
218        w.mRequestedWidth = 1200;
219        w.mRequestedHeight = 1200;
220        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
221        assertRect(w.mFrame, 0, 0, 1000, 1000);
222
223        // Before they are clipped though windows will be shifted
224        w.mAttrs.x = 300;
225        w.mAttrs.y = 300;
226        w.mRequestedWidth = 1000;
227        w.mRequestedHeight = 1000;
228        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
229        assertRect(w.mFrame, 0, 0, 1000, 1000);
230
231        // If there is room to move around in the parent frame the window will be shifted according
232        // to gravity.
233        w.mAttrs.x = 0;
234        w.mAttrs.y = 0;
235        w.mRequestedWidth = 300;
236        w.mRequestedHeight = 300;
237        w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
238        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
239        assertRect(w.mFrame, 700, 0, 1000, 300);
240        w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
241        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
242        assertRect(w.mFrame, 700, 700, 1000, 1000);
243        // Window specified  x and y are interpreted as offsets in the opposite
244        // direction of gravity
245        w.mAttrs.x = 100;
246        w.mAttrs.y = 100;
247        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
248        assertRect(w.mFrame, 600, 600, 900, 900);
249    }
250
251    @Test
252    public void testLayoutNonfullscreenTask() {
253        final Rect taskBounds = new Rect(300, 300, 700, 700);
254        TaskWithBounds task = new TaskWithBounds(taskBounds);
255        task.mFullscreenForTest = false;
256        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
257        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
258
259        final Rect pf = new Rect(0, 0, 1000, 1000);
260        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
261        // For non fullscreen tasks the containing frame is based off the
262        // task bounds not the parent frame.
263        assertRect(w.mFrame, 300, 300, 700, 700);
264        assertRect(w.getContentFrameLw(), 300, 300, 700, 700);
265        assertRect(w.mContentInsets, 0, 0, 0, 0);
266
267        pf.set(0, 0, 1000, 1000);
268        // We still produce insets against the containing frame the same way.
269        final Rect cf = new Rect(0, 0, 500, 500);
270        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
271        assertRect(w.mFrame, 300, 300, 700, 700);
272        assertRect(w.getContentFrameLw(), 300, 300, 500, 500);
273        assertRect(w.mContentInsets, 0, 0, 200, 200);
274
275        pf.set(0, 0, 1000, 1000);
276        // However if we set temp inset bounds, the insets will be computed
277        // as if our window was laid out there,  but it will be laid out according to
278        // the task bounds.
279        task.mInsetBounds.set(200, 200, 600, 600);
280        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
281        assertRect(w.mFrame, 300, 300, 700, 700);
282        assertRect(w.getContentFrameLw(), 300, 300, 600, 600);
283        assertRect(w.mContentInsets, 0, 0, 100, 100);
284    }
285
286    @Test
287    public void testCalculatePolicyCrop() {
288        final WindowStateWithTask w = createWindow(
289                new TaskWithBounds(null), FILL_PARENT, FILL_PARENT);
290        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
291
292        final Rect pf = new Rect(0, 0, 1000, 1000);
293        final Rect df = pf;
294        final Rect of = df;
295        final Rect cf = new Rect(pf);
296        // Produce some insets
297        cf.top += 50;
298        cf.bottom -= 100;
299        final Rect vf = cf;
300        final Rect sf = vf;
301        // We use a decor content frame with insets to produce cropping.
302        Rect dcf = cf;
303
304        final Rect policyCrop = new Rect();
305
306        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
307        w.calculatePolicyCrop(policyCrop);
308        // If we were above system decor we wouldnt' get any cropping though
309        w.mLayer = sWm.mSystemDecorLayer + 1;
310        w.calculatePolicyCrop(policyCrop);
311        assertRect(policyCrop, 0, 0, 1000, 1000);
312        w.mLayer = 1;
313        dcf.setEmpty();
314        // Likewise with no decor frame we would get no crop
315        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
316        w.calculatePolicyCrop(policyCrop);
317        assertRect(policyCrop, 0, 0, 1000, 1000);
318
319        // Now we set up a window which doesn't fill the entire decor frame.
320        // Normally it would be cropped to it's frame but in the case of docked resizing
321        // we need to account for the fact the windows surface will be made
322        // fullscreen and thus also make the crop fullscreen.
323        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
324        w.mAttrs.width = 500;
325        w.mAttrs.height = 500;
326        w.mRequestedWidth = 500;
327        w.mRequestedHeight = 500;
328        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
329
330        w.calculatePolicyCrop(policyCrop);
331        // Normally the crop is shrunk from the decor frame
332        // to the computed window frame.
333        assertRect(policyCrop, 0, 0, 500, 500);
334
335        w.mDockedResizingForTest = true;
336        w.calculatePolicyCrop(policyCrop);
337        // But if we are docked resizing it won't be, however we will still be
338        // shrunk to the decor frame and the display.
339        final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
340        assertRect(policyCrop, 0, 0,
341                Math.min(pf.width(), displayInfo.logicalWidth),
342                Math.min(pf.height(), displayInfo.logicalHeight));
343    }
344
345    private WindowStateWithTask createWindow(Task task, int width, int height) {
346        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
347        attrs.width = width;
348        attrs.height = height;
349
350        return new WindowStateWithTask(attrs, task);
351    }
352}
353