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