1 /* 2 * Copyright (C) 2020 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 17 package com.android.wm.shell.pip; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 22 import android.graphics.Rect; 23 import android.testing.AndroidTestingRunner; 24 import android.testing.TestableLooper; 25 import android.testing.TestableResources; 26 import android.util.Size; 27 import android.view.DisplayInfo; 28 import android.view.Gravity; 29 30 import androidx.test.filters.SmallTest; 31 32 import com.android.wm.shell.ShellTestCase; 33 import com.android.wm.shell.common.DisplayLayout; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 /** 40 * Unit tests against {@link PipBoundsAlgorithm}, including but not limited to: 41 * - default/movement bounds 42 * - save/restore PiP position on application lifecycle 43 * - save/restore PiP position on screen rotation 44 */ 45 @RunWith(AndroidTestingRunner.class) 46 @SmallTest 47 @TestableLooper.RunWithLooper(setAsMainLooper = true) 48 public class PipBoundsAlgorithmTest extends ShellTestCase { 49 private static final int ROUNDING_ERROR_MARGIN = 16; 50 private static final float ASPECT_RATIO_ERROR_MARGIN = 0.01f; 51 private static final float DEFAULT_ASPECT_RATIO = 1f; 52 private static final float MIN_ASPECT_RATIO = 0.5f; 53 private static final float MAX_ASPECT_RATIO = 2f; 54 private static final int DEFAULT_MIN_EDGE_SIZE = 100; 55 56 private PipBoundsAlgorithm mPipBoundsAlgorithm; 57 private DisplayInfo mDefaultDisplayInfo; 58 private PipBoundsState mPipBoundsState; 59 60 61 @Before setUp()62 public void setUp() throws Exception { 63 initializeMockResources(); 64 mPipBoundsState = new PipBoundsState(mContext); 65 mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, 66 new PipSnapAlgorithm()); 67 68 mPipBoundsState.setDisplayLayout( 69 new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true)); 70 } 71 initializeMockResources()72 private void initializeMockResources() { 73 final TestableResources res = mContext.getOrCreateTestableResources(); 74 res.addOverride( 75 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, 76 DEFAULT_ASPECT_RATIO); 77 res.addOverride( 78 com.android.internal.R.integer.config_defaultPictureInPictureGravity, 79 Gravity.END | Gravity.BOTTOM); 80 res.addOverride( 81 com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 82 DEFAULT_MIN_EDGE_SIZE); 83 res.addOverride( 84 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, 85 "16x16"); 86 res.addOverride( 87 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio, 88 MIN_ASPECT_RATIO); 89 res.addOverride( 90 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio, 91 MAX_ASPECT_RATIO); 92 93 mDefaultDisplayInfo = new DisplayInfo(); 94 mDefaultDisplayInfo.displayId = 1; 95 mDefaultDisplayInfo.logicalWidth = 1000; 96 mDefaultDisplayInfo.logicalHeight = 1500; 97 } 98 99 @Test getDefaultAspectRatio()100 public void getDefaultAspectRatio() { 101 assertEquals("Default aspect ratio matches resources", 102 DEFAULT_ASPECT_RATIO, mPipBoundsAlgorithm.getDefaultAspectRatio(), 103 ASPECT_RATIO_ERROR_MARGIN); 104 } 105 106 @Test onConfigurationChanged_reloadResources()107 public void onConfigurationChanged_reloadResources() { 108 final float newDefaultAspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; 109 final TestableResources res = mContext.getOrCreateTestableResources(); 110 res.addOverride(com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, 111 newDefaultAspectRatio); 112 113 mPipBoundsAlgorithm.onConfigurationChanged(mContext); 114 115 assertEquals("Default aspect ratio should be reloaded", 116 mPipBoundsAlgorithm.getDefaultAspectRatio(), newDefaultAspectRatio, 117 ASPECT_RATIO_ERROR_MARGIN); 118 } 119 120 @Test getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio()121 public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() { 122 final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO, 123 DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth, 124 mDefaultDisplayInfo.logicalHeight); 125 126 mPipBoundsState.setOverrideMinSize(null); 127 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 128 129 assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height())); 130 assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), 131 ASPECT_RATIO_ERROR_MARGIN); 132 } 133 134 @Test getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio()135 public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() { 136 overrideDefaultAspectRatio(1.0f); 137 // The min size's aspect ratio is greater than the default aspect ratio. 138 final Size overrideMinSize = new Size(150, 120); 139 140 mPipBoundsState.setOverrideMinSize(overrideMinSize); 141 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 142 143 // The default aspect ratio should trump the min size aspect ratio. 144 assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), 145 ASPECT_RATIO_ERROR_MARGIN); 146 // The width of the min size is still used with the default aspect ratio. 147 assertEquals(overrideMinSize.getWidth(), defaultBounds.width()); 148 } 149 150 @Test getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio()151 public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() { 152 overrideDefaultAspectRatio(1.0f); 153 // The min size's aspect ratio is greater than the default aspect ratio. 154 final Size overrideMinSize = new Size(120, 150); 155 156 mPipBoundsState.setOverrideMinSize(overrideMinSize); 157 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 158 159 // The default aspect ratio should trump the min size aspect ratio. 160 assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), 161 ASPECT_RATIO_ERROR_MARGIN); 162 // The height of the min size is still used with the default aspect ratio. 163 assertEquals(overrideMinSize.getHeight(), defaultBounds.height()); 164 } 165 166 @Test getDefaultBounds_imeShowing_offsetByImeHeight()167 public void getDefaultBounds_imeShowing_offsetByImeHeight() { 168 final int imeHeight = 30; 169 mPipBoundsState.setImeVisibility(false, 0); 170 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 171 172 mPipBoundsState.setImeVisibility(true, imeHeight); 173 final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); 174 175 assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top); 176 } 177 178 @Test getDefaultBounds_shelfShowing_offsetByShelfHeight()179 public void getDefaultBounds_shelfShowing_offsetByShelfHeight() { 180 final int shelfHeight = 30; 181 mPipBoundsState.setShelfVisibility(false, 0); 182 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 183 184 mPipBoundsState.setShelfVisibility(true, shelfHeight); 185 final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds(); 186 187 assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top); 188 } 189 190 @Test getDefaultBounds_imeAndShelfShowing_offsetByTallest()191 public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() { 192 final int imeHeight = 30; 193 final int shelfHeight = 40; 194 mPipBoundsState.setImeVisibility(false, 0); 195 mPipBoundsState.setShelfVisibility(false, 0); 196 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 197 198 mPipBoundsState.setImeVisibility(true, imeHeight); 199 mPipBoundsState.setShelfVisibility(true, shelfHeight); 200 final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); 201 202 assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top); 203 } 204 205 @Test getDefaultBounds_boundsAtDefaultGravity()206 public void getDefaultBounds_boundsAtDefaultGravity() { 207 final Rect insetBounds = new Rect(); 208 mPipBoundsAlgorithm.getInsetBounds(insetBounds); 209 overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM); 210 211 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 212 213 assertEquals(insetBounds.bottom, defaultBounds.bottom); 214 assertEquals(insetBounds.right, defaultBounds.right); 215 } 216 217 @Test getNormalBounds_invalidAspectRatio_returnsDefaultBounds()218 public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() { 219 final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); 220 221 // Set an invalid current aspect ratio. 222 mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2); 223 final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); 224 225 assertEquals(defaultBounds, normalBounds); 226 } 227 228 @Test getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds()229 public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() { 230 final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds(); 231 mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio, 232 MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */); 233 234 // Set a valid current aspect ratio different that the default. 235 mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO); 236 final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); 237 238 assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds); 239 } 240 241 @Test getEntryDestinationBounds_returnBoundsMatchesAspectRatio()242 public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() { 243 final float[] aspectRatios = new float[] { 244 (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2, 245 DEFAULT_ASPECT_RATIO, 246 (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2 247 }; 248 for (float aspectRatio : aspectRatios) { 249 mPipBoundsState.setAspectRatio(aspectRatio); 250 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 251 final float actualAspectRatio = getRectAspectRatio(destinationBounds); 252 assertEquals("Destination bounds matches the given aspect ratio", 253 aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); 254 } 255 } 256 257 @Test getEntryDestinationBounds_invalidAspectRatio_returnsDefaultAspectRatio()258 public void getEntryDestinationBounds_invalidAspectRatio_returnsDefaultAspectRatio() { 259 final float[] invalidAspectRatios = new float[] { 260 MIN_ASPECT_RATIO / 2, 261 MAX_ASPECT_RATIO * 2 262 }; 263 for (float aspectRatio : invalidAspectRatios) { 264 mPipBoundsState.setAspectRatio(aspectRatio); 265 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 266 final float actualAspectRatio = 267 destinationBounds.width() / (destinationBounds.height() * 1f); 268 assertEquals("Destination bounds fallbacks to default aspect ratio", 269 mPipBoundsAlgorithm.getDefaultAspectRatio(), actualAspectRatio, 270 ASPECT_RATIO_ERROR_MARGIN); 271 } 272 } 273 274 @Test getAdjustedDestinationBounds_returnBoundsMatchesAspectRatio()275 public void getAdjustedDestinationBounds_returnBoundsMatchesAspectRatio() { 276 final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; 277 final Rect currentBounds = new Rect(0, 0, 0, 100); 278 currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; 279 280 mPipBoundsState.setAspectRatio(aspectRatio); 281 final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds( 282 currentBounds, aspectRatio); 283 284 final float actualAspectRatio = 285 destinationBounds.width() / (destinationBounds.height() * 1f); 286 assertEquals("Destination bounds matches the given aspect ratio", 287 aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); 288 } 289 290 @Test getEntryDestinationBounds_withMinSize_returnMinBounds()291 public void getEntryDestinationBounds_withMinSize_returnMinBounds() { 292 final float[] aspectRatios = new float[] { 293 (MIN_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2, 294 DEFAULT_ASPECT_RATIO, 295 (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2 296 }; 297 final Size[] minimalSizes = new Size[] { 298 new Size((int) (100 * aspectRatios[0]), 100), 299 new Size((int) (100 * aspectRatios[1]), 100), 300 new Size((int) (100 * aspectRatios[2]), 100) 301 }; 302 for (int i = 0; i < aspectRatios.length; i++) { 303 final float aspectRatio = aspectRatios[i]; 304 final Size minimalSize = minimalSizes[i]; 305 mPipBoundsState.setAspectRatio(aspectRatio); 306 mPipBoundsState.setOverrideMinSize(minimalSize); 307 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 308 assertTrue("Destination bounds is no smaller than minimal requirement", 309 (destinationBounds.width() == minimalSize.getWidth() 310 && destinationBounds.height() >= minimalSize.getHeight()) 311 || (destinationBounds.height() == minimalSize.getHeight() 312 && destinationBounds.width() >= minimalSize.getWidth())); 313 final float actualAspectRatio = 314 destinationBounds.width() / (destinationBounds.height() * 1f); 315 assertEquals("Destination bounds matches the given aspect ratio", 316 aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); 317 } 318 } 319 320 @Test getAdjustedDestinationBounds_ignoreMinBounds()321 public void getAdjustedDestinationBounds_ignoreMinBounds() { 322 final float aspectRatio = (DEFAULT_ASPECT_RATIO + MAX_ASPECT_RATIO) / 2; 323 final Rect currentBounds = new Rect(0, 0, 0, 100); 324 currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; 325 final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2); 326 327 mPipBoundsState.setAspectRatio(aspectRatio); 328 mPipBoundsState.setOverrideMinSize(minSize); 329 final Rect destinationBounds = mPipBoundsAlgorithm.getAdjustedDestinationBounds( 330 currentBounds, aspectRatio); 331 332 assertTrue("Destination bounds ignores minimal size", 333 destinationBounds.width() > minSize.getWidth() 334 && destinationBounds.height() > minSize.getHeight()); 335 } 336 337 @Test getEntryDestinationBounds_reentryStateExists_restoreLastSize()338 public void getEntryDestinationBounds_reentryStateExists_restoreLastSize() { 339 mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO); 340 final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 341 reentryBounds.scale(1.25f); 342 final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds); 343 344 mPipBoundsState.saveReentryState( 345 new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction); 346 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 347 348 assertEquals(reentryBounds.width(), destinationBounds.width()); 349 assertEquals(reentryBounds.height(), destinationBounds.height()); 350 } 351 352 @Test getEntryDestinationBounds_reentryStateExists_restoreLastPosition()353 public void getEntryDestinationBounds_reentryStateExists_restoreLastPosition() { 354 mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO); 355 final Rect reentryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 356 reentryBounds.offset(0, -100); 357 final float reentrySnapFraction = mPipBoundsAlgorithm.getSnapFraction(reentryBounds); 358 359 mPipBoundsState.saveReentryState( 360 new Size(reentryBounds.width(), reentryBounds.height()), reentrySnapFraction); 361 362 final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 363 364 assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds); 365 } 366 367 @Test setShelfHeight_offsetBounds()368 public void setShelfHeight_offsetBounds() { 369 final int shelfHeight = 100; 370 mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO); 371 final Rect oldPosition = mPipBoundsAlgorithm.getEntryDestinationBounds(); 372 373 mPipBoundsState.setShelfVisibility(true, shelfHeight); 374 final Rect newPosition = mPipBoundsAlgorithm.getEntryDestinationBounds(); 375 376 oldPosition.offset(0, -shelfHeight); 377 assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition); 378 } 379 380 @Test onImeVisibilityChanged_offsetBounds()381 public void onImeVisibilityChanged_offsetBounds() { 382 final int imeHeight = 100; 383 mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO); 384 final Rect oldPosition = mPipBoundsAlgorithm.getEntryDestinationBounds(); 385 386 mPipBoundsState.setImeVisibility(true, imeHeight); 387 final Rect newPosition = mPipBoundsAlgorithm.getEntryDestinationBounds(); 388 389 oldPosition.offset(0, -imeHeight); 390 assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition); 391 } 392 393 @Test getEntryDestinationBounds_noReentryState_useDefaultBounds()394 public void getEntryDestinationBounds_noReentryState_useDefaultBounds() { 395 mPipBoundsState.setAspectRatio(DEFAULT_ASPECT_RATIO); 396 final Rect defaultBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 397 398 mPipBoundsState.clearReentryState(); 399 400 final Rect actualBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); 401 402 assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); 403 } 404 405 @Test adjustNormalBoundsToFitMenu_alreadyFits()406 public void adjustNormalBoundsToFitMenu_alreadyFits() { 407 final Rect normalBounds = new Rect(0, 0, 400, 711); 408 final Size minMenuSize = new Size(396, 292); 409 mPipBoundsState.setAspectRatio( 410 ((float) normalBounds.width()) / ((float) normalBounds.height())); 411 412 final Rect bounds = 413 mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); 414 415 assertEquals(normalBounds, bounds); 416 } 417 418 @Test adjustNormalBoundsToFitMenu_widthTooSmall()419 public void adjustNormalBoundsToFitMenu_widthTooSmall() { 420 final Rect normalBounds = new Rect(0, 0, 297, 528); 421 final Size minMenuSize = new Size(396, 292); 422 mPipBoundsState.setAspectRatio( 423 ((float) normalBounds.width()) / ((float) normalBounds.height())); 424 425 final Rect bounds = 426 mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); 427 428 assertEquals(minMenuSize.getWidth(), bounds.width()); 429 assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), 430 bounds.height(), 0.3f); 431 } 432 433 @Test adjustNormalBoundsToFitMenu_heightTooSmall()434 public void adjustNormalBoundsToFitMenu_heightTooSmall() { 435 final Rect normalBounds = new Rect(0, 0, 400, 280); 436 final Size minMenuSize = new Size(396, 292); 437 mPipBoundsState.setAspectRatio( 438 ((float) normalBounds.width()) / ((float) normalBounds.height())); 439 440 final Rect bounds = 441 mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); 442 443 assertEquals(minMenuSize.getHeight(), bounds.height()); 444 assertEquals(minMenuSize.getHeight() * mPipBoundsState.getAspectRatio(), 445 bounds.width(), 0.3f); 446 } 447 448 @Test adjustNormalBoundsToFitMenu_widthAndHeightTooSmall()449 public void adjustNormalBoundsToFitMenu_widthAndHeightTooSmall() { 450 final Rect normalBounds = new Rect(0, 0, 350, 280); 451 final Size minMenuSize = new Size(396, 292); 452 mPipBoundsState.setAspectRatio( 453 ((float) normalBounds.width()) / ((float) normalBounds.height())); 454 455 final Rect bounds = 456 mPipBoundsAlgorithm.adjustNormalBoundsToFitMenu(normalBounds, minMenuSize); 457 458 assertEquals(minMenuSize.getWidth(), bounds.width()); 459 assertEquals(minMenuSize.getWidth() / mPipBoundsState.getAspectRatio(), 460 bounds.height(), 0.3f); 461 } 462 overrideDefaultAspectRatio(float aspectRatio)463 private void overrideDefaultAspectRatio(float aspectRatio) { 464 final TestableResources res = mContext.getOrCreateTestableResources(); 465 res.addOverride( 466 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, 467 aspectRatio); 468 mPipBoundsAlgorithm.onConfigurationChanged(mContext); 469 } 470 overrideDefaultStackGravity(int stackGravity)471 private void overrideDefaultStackGravity(int stackGravity) { 472 final TestableResources res = mContext.getOrCreateTestableResources(); 473 res.addOverride( 474 com.android.internal.R.integer.config_defaultPictureInPictureGravity, 475 stackGravity); 476 mPipBoundsAlgorithm.onConfigurationChanged(mContext); 477 } 478 assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual)479 private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { 480 final Rect expectedWithMargin = new Rect(expected); 481 expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); 482 assertTrue(from + ": expect " + expected 483 + " contains " + actual 484 + " with error margin " + ROUNDING_ERROR_MARGIN, 485 expectedWithMargin.contains(actual)); 486 } 487 getRectAspectRatio(Rect rect)488 private static float getRectAspectRatio(Rect rect) { 489 return rect.width() / (rect.height() * 1f); 490 } 491 } 492