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