WindowFrameTests.java revision 15dd7efeb7491b80add341b0599027e246d07c6f
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.Gravity;
31import android.view.IWindow;
32import android.view.WindowManager;
33
34import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
35import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
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 {
49
50    private static WindowManagerService sWm = null;
51    private WindowToken mWindowToken;
52    private final IWindow mIWindow = new TestIWindow();
53
54    class WindowStateWithTask extends WindowState {
55        final Task mTask;
56        WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
57            super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0);
58            mTask = t;
59        }
60
61        @Override
62        Task getTask() {
63            return mTask;
64        }
65    };
66
67    class TaskWithBounds extends Task {
68        final Rect mBounds;
69        final Rect mInsetBounds = new Rect();
70        boolean mFullscreenForTest = true;
71        TaskWithBounds(Rect bounds) {
72            super(0, mStubStack, 0, sWm, null, null, false);
73            mBounds = bounds;
74        }
75        @Override
76        void getBounds(Rect outBounds) {
77            outBounds.set(mBounds);
78        }
79        @Override
80        void getTempInsetBounds(Rect outBounds) {
81            outBounds.set(mInsetBounds);
82        }
83        @Override
84        boolean isFullscreen() {
85            return mFullscreenForTest;
86        }
87    }
88
89    TaskStack mStubStack;
90
91    @Before
92    public void setUp() throws Exception {
93        final Context context = InstrumentationRegistry.getTargetContext();
94        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
95        mWindowToken = new WindowToken(sWm, new Binder(), 0, false,
96                sWm.getDefaultDisplayContentLocked());
97        mStubStack = new TaskStack(sWm, 0);
98    }
99
100    public void assertRect(Rect rect, int left, int top, int right, int bottom) {
101        assertEquals(left, rect.left);
102        assertEquals(top, rect.top);
103        assertEquals(right, rect.right);
104        assertEquals(bottom, rect.bottom);
105    }
106
107    @Test
108    public void testLayoutInFullscreenTaskInsets() throws Exception {
109        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
110        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
111        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
112
113        final int bottomContentInset = 100;
114        final int topContentInset = 50;
115        final int bottomVisibleInset = 30;
116        final int topVisibleInset = 70;
117        final int leftStableInset = 20;
118        final int rightStableInset = 90;
119
120        // With no insets or system decor all the frames incoming from PhoneWindowManager
121        // are identical.
122        final Rect pf = new Rect(0, 0, 1000, 1000);
123        final Rect df = pf;
124        final Rect of = df;
125        final Rect cf = new Rect(pf);
126        // Produce some insets
127        cf.top += 50;
128        cf.bottom -= 100;
129        final Rect vf = new Rect(pf);
130        vf.top += topVisibleInset;
131        vf.bottom -= bottomVisibleInset;
132        final Rect sf = new Rect(pf);
133        sf.left += leftStableInset;
134        sf.right -= rightStableInset;
135
136        final Rect dcf = pf;
137        // When mFrame extends past cf, the content insets are
138        // the difference between mFrame and ContentFrame. Visible
139        // and stable frames work the same way.
140        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
141        assertRect(w.mFrame,0, 0, 1000, 1000);
142        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
143        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
144        assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
145        // The frames remain as passed in shrunk to the window frame
146        assertTrue(cf.equals(w.getContentFrameLw()));
147        assertTrue(vf.equals(w.getVisibleFrameLw()));
148        assertTrue(sf.equals(w.getStableFrameLw()));
149        // On the other hand mFrame doesn't extend past cf we won't get any insets
150        w.mAttrs.x = 100;
151        w.mAttrs.y = 100;
152        w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
153        w.mRequestedWidth = 100;
154        w.mRequestedHeight = 100;
155        w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
156        assertRect(w.mFrame, 100, 100, 200, 200);
157        assertRect(w.mContentInsets, 0, 0, 0, 0);
158        // In this case the frames are shrunk to the window frame.
159        assertTrue(w.mFrame.equals(w.getContentFrameLw()));
160        assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
161        assertTrue(w.mFrame.equals(w.getStableFrameLw()));
162    }
163
164    @Test
165    public void testLayoutInFullscreenTaskNoInsets() throws Exception {
166        Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
167        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
168        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
169
170        // With no insets or system decor all the frames incoming from PhoneWindowManager
171        // are identical.
172        final Rect pf = new Rect(0, 0, 1000, 1000);
173
174        // Here the window has FILL_PARENT, FILL_PARENT
175        // so we expect it to fill the entire available frame.
176        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
177        assertRect(w.mFrame, 0, 0, 1000, 1000);
178
179        // It can select various widths and heights within the bounds.
180        // Strangely the window attribute width is ignored for normal windows
181        // and we use mRequestedWidth/mRequestedHeight
182        w.mAttrs.width = 300;
183        w.mAttrs.height = 300;
184        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
185        // Explicit width and height without requested width/height
186        // gets us nothing.
187        assertRect(w.mFrame, 0, 0, 0, 0);
188
189        w.mRequestedWidth = 300;
190        w.mRequestedHeight = 300;
191        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
192        // With requestedWidth/Height we can freely choose our size within the
193        // parent bounds.
194        assertRect(w.mFrame, 0, 0, 300, 300);
195
196        // With FLAG_SCALED though, requestedWidth/height is used to control
197        // the unscaled surface size, and mAttrs.width/height becomes the
198        // layout controller.
199        w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED;
200        w.mRequestedHeight = -1;
201        w.mRequestedWidth = -1;
202        w.mAttrs.width = 100;
203        w.mAttrs.height = 100;
204        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
205        assertRect(w.mFrame, 0, 0, 100, 100);
206        w.mAttrs.flags = 0;
207
208        // But sizes too large will be clipped to the containing frame
209        w.mRequestedWidth = 1200;
210        w.mRequestedHeight = 1200;
211        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
212        assertRect(w.mFrame, 0, 0, 1000, 1000);
213
214        // Before they are clipped though windows will be shifted
215        w.mAttrs.x = 300;
216        w.mAttrs.y = 300;
217        w.mRequestedWidth = 1000;
218        w.mRequestedHeight = 1000;
219        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
220        assertRect(w.mFrame, 0, 0, 1000, 1000);
221
222        // If there is room to move around in the parent frame the window will be shifted according
223        // to gravity.
224        w.mAttrs.x = 0;
225        w.mAttrs.y = 0;
226        w.mRequestedWidth = 300;
227        w.mRequestedHeight = 300;
228        w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
229        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
230        assertRect(w.mFrame, 700, 0, 1000, 300);
231        w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
232        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
233        assertRect(w.mFrame, 700, 700, 1000, 1000);
234        // Window specified  x and y are interpreted as offsets in the opposite
235        // direction of gravity
236        w.mAttrs.x = 100;
237        w.mAttrs.y = 100;
238        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
239        assertRect(w.mFrame, 600, 600, 900, 900);
240    }
241
242    @Test
243    public void testLayoutNonfullscreenTask() {
244        final Rect taskBounds = new Rect(300, 300, 700, 700);
245        TaskWithBounds task = new TaskWithBounds(taskBounds);
246        task.mFullscreenForTest = false;
247        WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
248        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
249
250        final Rect pf = new Rect(0, 0, 1000, 1000);
251        w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
252        // For non fullscreen tasks the containing frame is based off the
253        // task bounds not the parent frame.
254        assertRect(w.mFrame, 300, 300, 700, 700);
255        assertRect(w.getContentFrameLw(), 300, 300, 700, 700);
256        assertRect(w.mContentInsets, 0, 0, 0, 0);
257
258        pf.set(0, 0, 1000, 1000);
259        // We still produce insets against the containing frame the same way.
260        final Rect cf = new Rect(0, 0, 500, 500);
261        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
262        assertRect(w.mFrame, 300, 300, 700, 700);
263        assertRect(w.getContentFrameLw(), 300, 300, 500, 500);
264        assertRect(w.mContentInsets, 0, 0, 200, 200);
265
266        pf.set(0, 0, 1000, 1000);
267        // However if we set temp inset bounds, the insets will be computed
268        // as if our window was laid out there,  but it will be laid out according to
269        // the task bounds.
270        task.mInsetBounds.set(200, 200, 600, 600);
271        w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
272        assertRect(w.mFrame, 300, 300, 700, 700);
273        assertRect(w.getContentFrameLw(), 300, 300, 600, 600);
274        assertRect(w.mContentInsets, 0, 0, 100, 100);
275    }
276
277    private WindowState createWindow(Task task, int width, int height) {
278        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
279        attrs.width = width;
280        attrs.height = height;
281
282        return new WindowStateWithTask(attrs, task);
283    }
284}
285