WindowFrameTests.java revision e1fe7fa288a34ecaaab390f49ef540edc4a6c52d
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, null); 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