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