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