• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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