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