From 82b6802083cb01a525758edde176ceeaec9514f6 Mon Sep 17 00:00:00 2001 From: "jorgegil@google.com" Date: Mon, 16 Nov 2020 11:00:26 -0800 Subject: [PATCH] Add unit tests for PipBoundsAlgorithm Bug: 169373982 Test: atest PipBoundsAlgorithmTest Change-Id: I033039191f34e4bae22cd2690b0413cab1aaa7a5 --- .../wm/shell/pip/PipBoundsAlgorithm.java | 73 ++++++++- .../wm/shell/pip/PipSnapAlgorithm.java | 73 --------- .../wm/shell/pip/phone/PipTouchHandler.java | 2 +- .../wm/shell/pip/PipBoundsAlgorithmTest.java | 148 +++++++++++++++++- .../shell/pip/phone/PipTouchHandlerTest.java | 4 +- 5 files changed, 217 insertions(+), 83 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java index df6683ebb80b2..9b739932f9de7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java @@ -22,6 +22,7 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; +import android.graphics.PointF; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.Size; @@ -42,6 +43,9 @@ public class PipBoundsAlgorithm { private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; + private float mDefaultSizePercent; + private float mMinAspectRatioForMinSize; + private float mMaxAspectRatioForMinSize; private float mDefaultAspectRatio; private float mMinAspectRatio; private float mMaxAspectRatio; @@ -51,7 +55,7 @@ public class PipBoundsAlgorithm { public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState) { mPipBoundsState = pipBoundsState; - mSnapAlgorithm = new PipSnapAlgorithm(context); + mSnapAlgorithm = new PipSnapAlgorithm(); reloadResources(context); // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload // resources as it would clobber mAspectRatio when entering PiP from fullscreen which @@ -83,6 +87,11 @@ public class PipBoundsAlgorithm { com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); mMaxAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); + mDefaultSizePercent = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); + mMaxAspectRatioForMinSize = res.getFloat( + com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); + mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; } /** @@ -174,7 +183,7 @@ public class PipBoundsAlgorithm { final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize() : defaultMinEdgeSize; // Use the existing size but adjusted to the aspect ratio and min edge size. - size = mSnapAlgorithm.getSizeForAspectRatio( + size = getSizeForAspectRatio( new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize); } else { if (overrideMinSize != null) { @@ -184,7 +193,7 @@ public class PipBoundsAlgorithm { } else { // Calculate the default size using the display size and default min edge size. final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo(); - size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize, + size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } } @@ -229,7 +238,7 @@ public class PipBoundsAlgorithm { defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio); } else { // Calculate the default size using the display size and default min edge size. - defaultSize = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, + defaultSize = getSizeForAspectRatio(mDefaultAspectRatio, mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight); } Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(), @@ -303,6 +312,62 @@ public class PipBoundsAlgorithm { return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm); } + /** + * @return the size of the PiP at the given aspectRatio, ensuring that the minimum edge + * is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, + int displayHeight) { + final int smallestDisplaySize = Math.min(displayWidth, displayHeight); + final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); + + final int width; + final int height; + if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { + // Beyond these points, we can just use the min size as the shorter edge + if (aspectRatio <= 1) { + // Portrait, width is the minimum size + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + } else { + // Within these points, we ensure that the bounds fit within the radius of the limits + // at the points + final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; + final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); + height = (int) Math.round(Math.sqrt((radius * radius) + / (aspectRatio * aspectRatio + 1))); + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + + /** + * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the + * minimum edge is at least minEdgeSize. + */ + public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { + final int smallestSize = Math.min(size.getWidth(), size.getHeight()); + final int minSize = (int) Math.max(minEdgeSize, smallestSize); + + final int width; + final int height; + if (aspectRatio <= 1) { + // Portrait, width is the minimum size. + width = minSize; + height = Math.round(width / aspectRatio); + } else { + // Landscape, height is the minimum size + height = minSize; + width = Math.round(height * aspectRatio); + } + return new Size(width, height); + } + /** * Dumps internal states. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java index 71060752df09e..d6dc536b9e365 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java @@ -20,11 +20,7 @@ import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE; import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.PointF; import android.graphics.Rect; -import android.util.Size; /** * Calculates the snap targets and the snap position for the PIP given a position and a velocity. @@ -32,19 +28,6 @@ import android.util.Size; */ public class PipSnapAlgorithm { - private final float mDefaultSizePercent; - private final float mMinAspectRatioForMinSize; - private final float mMaxAspectRatioForMinSize; - - public PipSnapAlgorithm(Context context) { - Resources res = context.getResources(); - mDefaultSizePercent = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureDefaultSizePercent); - mMaxAspectRatioForMinSize = res.getFloat( - com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); - mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; - } - /** * Returns a fraction that describes where the PiP bounds is. * See {@link #getSnapFraction(Rect, Rect, int)}. @@ -150,62 +133,6 @@ public class PipSnapAlgorithm { movementBoundsOut.bottom -= bottomOffset; } - /** - * @return the size of the PiP at the given {@param aspectRatio}, ensuring that the minimum edge - * is at least {@param minEdgeSize}. - */ - public Size getSizeForAspectRatio(float aspectRatio, float minEdgeSize, int displayWidth, - int displayHeight) { - final int smallestDisplaySize = Math.min(displayWidth, displayHeight); - final int minSize = (int) Math.max(minEdgeSize, smallestDisplaySize * mDefaultSizePercent); - - final int width; - final int height; - if (aspectRatio <= mMinAspectRatioForMinSize || aspectRatio > mMaxAspectRatioForMinSize) { - // Beyond these points, we can just use the min size as the shorter edge - if (aspectRatio <= 1) { - // Portrait, width is the minimum size - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - } else { - // Within these points, we ensure that the bounds fit within the radius of the limits - // at the points - final float widthAtMaxAspectRatioForMinSize = mMaxAspectRatioForMinSize * minSize; - final float radius = PointF.length(widthAtMaxAspectRatioForMinSize, minSize); - height = (int) Math.round(Math.sqrt((radius * radius) / - (aspectRatio * aspectRatio + 1))); - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - - /** - * @return the adjusted size so that it conforms to the given aspectRatio, ensuring that the - * minimum edge is at least minEdgeSize. - */ - public Size getSizeForAspectRatio(Size size, float aspectRatio, float minEdgeSize) { - final int smallestSize = Math.min(size.getWidth(), size.getHeight()); - final int minSize = (int) Math.max(minEdgeSize, smallestSize); - - final int width; - final int height; - if (aspectRatio <= 1) { - // Portrait, width is the minimum size. - width = minSize; - height = Math.round(width / aspectRatio); - } else { - // Landscape, height is the minimum size - height = minSize; - width = Math.round(height * aspectRatio); - } - return new Size(width, height); - } - /** * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes * the new bounds out to {@param boundsOut}. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 1c5d5b8a82628..37d7e51519271 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -353,7 +353,7 @@ public class PipTouchHandler { float aspectRatio = (float) normalBounds.width() / normalBounds.height(); Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size expandedSize = mPipBoundsAlgorithm.getSnapAlgorithm().getSizeForAspectRatio( + Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio( aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y); mPipBoundsState.setExpandedBounds( new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight())); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java index 7a6e0c1b41fc3..a65d832359d2f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java @@ -50,6 +50,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { private static final float DEFAULT_ASPECT_RATIO = 1f; private static final float MIN_ASPECT_RATIO = 0.5f; private static final float MAX_ASPECT_RATIO = 2f; + private static final int DEFAULT_MIN_EDGE_SIZE = 100; private PipBoundsAlgorithm mPipBoundsAlgorithm; private DisplayInfo mDefaultDisplayInfo; @@ -73,7 +74,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { com.android.internal.R.integer.config_defaultPictureInPictureGravity, Gravity.END | Gravity.BOTTOM); res.addOverride( - com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, 100); + com.android.internal.R.dimen.default_minimal_size_pip_resizable_task, + DEFAULT_MIN_EDGE_SIZE); res.addOverride( com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets, "16x16"); @@ -111,6 +113,127 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { ASPECT_RATIO_ERROR_MARGIN); } + @Test + public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() { + final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO, + DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth, + mDefaultDisplayInfo.logicalHeight); + + mPipBoundsState.setOverrideMinSize(null); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(defaultSize, new Size(defaultBounds.width(), defaultBounds.height())); + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + } + + @Test + public void getDefaultBounds_widerOverrideMinSize_matchesMinSizeWidthAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(150, 120); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The width of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getWidth(), defaultBounds.width()); + } + + @Test + public void getDefaultBounds_tallerOverrideMinSize_matchesMinSizeHeightAndDefaultAspectRatio() { + overrideDefaultAspectRatio(1.0f); + // The min size's aspect ratio is greater than the default aspect ratio. + final Size overrideMinSize = new Size(120, 150); + + mPipBoundsState.setOverrideMinSize(overrideMinSize); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // The default aspect ratio should trump the min size aspect ratio. + assertEquals(DEFAULT_ASPECT_RATIO, getRectAspectRatio(defaultBounds), + ASPECT_RATIO_ERROR_MARGIN); + // The height of the min size is still used with the default aspect ratio. + assertEquals(overrideMinSize.getHeight(), defaultBounds.height()); + } + + @Test + public void getDefaultBounds_imeShowing_offsetByImeHeight() { + final int imeHeight = 30; + mPipBoundsState.setImeVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(imeHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_shelfShowing_offsetByShelfHeight() { + final int shelfHeight = 30; + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithShelf = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithShelf.top); + } + + @Test + public void getDefaultBounds_imeAndShelfShowing_offsetByTallest() { + final int imeHeight = 30; + final int shelfHeight = 40; + mPipBoundsState.setImeVisibility(false, 0); + mPipBoundsState.setShelfVisibility(false, 0); + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + mPipBoundsState.setImeVisibility(true, imeHeight); + mPipBoundsState.setShelfVisibility(true, shelfHeight); + final Rect defaultBoundsWithIme = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(shelfHeight, defaultBounds.top - defaultBoundsWithIme.top); + } + + @Test + public void getDefaultBounds_boundsAtDefaultGravity() { + final Rect insetBounds = new Rect(); + mPipBoundsAlgorithm.getInsetBounds(insetBounds); + overrideDefaultStackGravity(Gravity.END | Gravity.BOTTOM); + + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + assertEquals(insetBounds.bottom, defaultBounds.bottom); + assertEquals(insetBounds.right, defaultBounds.right); + } + + @Test + public void getNormalBounds_invalidAspectRatio_returnsDefaultBounds() { + final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds(); + + // Set an invalid current aspect ratio. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO / 2); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBounds, normalBounds); + } + + @Test + public void getNormalBounds_validAspectRatio_returnsAdjustedDefaultBounds() { + final Rect defaultBoundsAdjustedToAspectRatio = mPipBoundsAlgorithm.getDefaultBounds(); + mPipBoundsAlgorithm.transformBoundsToAspectRatio(defaultBoundsAdjustedToAspectRatio, + MIN_ASPECT_RATIO, false /* useCurrentMinEdgeSize */, false /* useCurrentSize */); + + // Set a valid current aspect ratio different that the default. + mPipBoundsState.setAspectRatio(MIN_ASPECT_RATIO); + final Rect normalBounds = mPipBoundsAlgorithm.getNormalBounds(); + + assertEquals(defaultBoundsAdjustedToAspectRatio, normalBounds); + } + @Test public void getEntryDestinationBounds_returnBoundsMatchesAspectRatio() { final float[] aspectRatios = new float[] { @@ -121,8 +244,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { for (float aspectRatio : aspectRatios) { mPipBoundsState.setAspectRatio(aspectRatio); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - final float actualAspectRatio = - destinationBounds.width() / (destinationBounds.height() * 1f); + final float actualAspectRatio = getRectAspectRatio(destinationBounds); assertEquals("Destination bounds matches the given aspect ratio", aspectRatio, actualAspectRatio, ASPECT_RATIO_ERROR_MARGIN); } @@ -274,6 +396,22 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } + private void overrideDefaultAspectRatio(float aspectRatio) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio, + aspectRatio); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + + private void overrideDefaultStackGravity(int stackGravity) { + final TestableResources res = mContext.getOrCreateTestableResources(); + res.addOverride( + com.android.internal.R.integer.config_defaultPictureInPictureGravity, + stackGravity); + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + } + private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { final Rect expectedWithMargin = new Rect(expected); expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); @@ -282,4 +420,8 @@ public class PipBoundsAlgorithmTest extends ShellTestCase { + " with error margin " + ROUNDING_ERROR_MARGIN, expectedWithMargin.contains(actual)); } + + private static float getRectAspectRatio(Rect rect) { + return rect.width() / (rect.height() * 1f); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index b25c74d128186..c00210e57398c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -91,7 +91,7 @@ public class PipTouchHandlerTest extends ShellTestCase { mPipBoundsState = new PipBoundsState(mContext); mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm(); - mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); + mPipSnapAlgorithm = new PipSnapAlgorithm(); mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, mFloatingContentCoordinator, mPipUiEventLogger); @@ -129,7 +129,7 @@ public class PipTouchHandlerTest extends ShellTestCase { public void updateMovementBounds_maxBounds() { Point displaySize = new Point(); mContext.getDisplay().getRealSize(displaySize); - Size maxSize = mPipSnapAlgorithm.getSizeForAspectRatio(1, + Size maxSize = mPipBoundsAlgorithm.getSizeForAspectRatio(1, mContext.getResources().getDimensionPixelSize( R.dimen.pip_expanded_shortest_edge_size), displaySize.x, displaySize.y); Rect maxBounds = new Rect(0, 0, maxSize.getWidth(), maxSize.getHeight());