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