Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +69 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; } /** Loading Loading @@ -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) { Loading @@ -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); } } Loading Loading @@ -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(), Loading Loading @@ -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. */ Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +0 −73 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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)}. Loading Loading @@ -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}. Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +1 −1 Original line number Diff line number Diff line Loading @@ -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())); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +145 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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"); Loading Loading @@ -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[] { Loading @@ -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); } Loading Loading @@ -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); Loading @@ -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); } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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()); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java +69 −4 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; } /** Loading Loading @@ -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) { Loading @@ -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); } } Loading Loading @@ -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(), Loading Loading @@ -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. */ Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSnapAlgorithm.java +0 −73 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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)}. Loading Loading @@ -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}. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +1 −1 Original line number Diff line number Diff line Loading @@ -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())); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java +145 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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"); Loading Loading @@ -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[] { Loading @@ -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); } Loading Loading @@ -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); Loading @@ -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); } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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()); Loading