summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/res/values/config.xml4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java31
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java527
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java214
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java22
19 files changed, 930 insertions, 258 deletions
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 774f6c6379b2..76eb0945d990 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -105,6 +105,10 @@
1.777778
</item>
+ <!-- The aspect ratio that by which optimizations to large screen sizes are made.
+ Needs to be less that or equal to 1. -->
+ <item name="config_pipLargeScreenOptimizedAspectRatio" format="float" type="dimen">0.5625</item>
+
<!-- The default gravity for the picture-in-picture window.
Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT -->
<integer name="config_defaultPictureInPictureGravity">0x55</integer>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
index 8022e9b1cd81..94db878233a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java
@@ -39,6 +39,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
import com.android.wm.shell.pip.PipUiEventLogger;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.tv.TvPipBoundsAlgorithm;
import com.android.wm.shell.pip.tv.TvPipBoundsController;
import com.android.wm.shell.pip.tv.TvPipBoundsState;
@@ -69,6 +70,7 @@ public abstract class TvPipModule {
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -88,6 +90,7 @@ public abstract class TvPipModule {
shellInit,
shellController,
tvPipBoundsState,
+ pipSizeSpecHandler,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -127,14 +130,23 @@ public abstract class TvPipModule {
@WMSingleton
@Provides
static TvPipBoundsAlgorithm provideTvPipBoundsAlgorithm(Context context,
- TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) {
- return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm);
+ TvPipBoundsState tvPipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new TvPipBoundsAlgorithm(context, tvPipBoundsState, pipSnapAlgorithm,
+ pipSizeSpecHandler);
}
@WMSingleton
@Provides
- static TvPipBoundsState provideTvPipBoundsState(Context context) {
- return new TvPipBoundsState(context);
+ static TvPipBoundsState provideTvPipBoundsState(Context context,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new TvPipBoundsState(context, pipSizeSpecHandler);
+ }
+
+ @WMSingleton
+ @Provides
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+ return new PipSizeSpecHandler(context);
}
// Handler needed for loadDrawableAsync() in PipControlsViewController
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 512a4ef386bb..1135aa36aa4d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -77,6 +77,7 @@ import com.android.wm.shell.pip.phone.PhonePipKeepClearAlgorithm;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.pip.phone.PipController;
import com.android.wm.shell.pip.phone.PipMotionHelper;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -338,6 +339,7 @@ public abstract class WMShellModule {
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -354,17 +356,18 @@ public abstract class WMShellModule {
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
- pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
- phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
- pipTransitionController, windowManagerShellWrapper, taskStackListener,
- pipParamsChangedForwarder, displayInsetsController, oneHandedController,
- mainExecutor));
+ pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler, pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+ oneHandedController, mainExecutor));
}
@WMSingleton
@Provides
- static PipBoundsState providePipBoundsState(Context context) {
- return new PipBoundsState(context);
+ static PipBoundsState providePipBoundsState(Context context,
+ PipSizeSpecHandler pipSizeSpecHandler) {
+ return new PipBoundsState(context, pipSizeSpecHandler);
}
@WMSingleton
@@ -381,11 +384,18 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static PipSizeSpecHandler providePipSizeSpecHelper(Context context) {
+ return new PipSizeSpecHandler(context);
+ }
+
+ @WMSingleton
+ @Provides
static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context,
PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm,
- PhonePipKeepClearAlgorithm pipKeepClearAlgorithm) {
+ PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
+ PipSizeSpecHandler pipSizeSpecHandler) {
return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm,
- pipKeepClearAlgorithm);
+ pipKeepClearAlgorithm, pipSizeSpecHandler);
}
// Handler is used by Icon.loadDrawableAsync
@@ -409,13 +419,14 @@ public abstract class WMShellModule {
PhonePipMenuController menuPhoneController,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipTaskOrganizer pipTaskOrganizer,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
PipUiEventLogger pipUiEventLogger,
@ShellMainThread ShellExecutor mainExecutor) {
return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm,
- pipBoundsState, pipTaskOrganizer, pipMotionHelper,
+ pipBoundsState, pipSizeSpecHandler, pipTaskOrganizer, pipMotionHelper,
floatingContentCoordinator, pipUiEventLogger, mainExecutor);
}
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 f6d67d858f98..867162be4c6d 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
@@ -16,24 +16,19 @@
package com.android.wm.shell.pip;
-import static android.util.TypedValue.COMPLEX_UNIT_DIP;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PictureInPictureParams;
import android.content.Context;
import android.content.pm.ActivityInfo;
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;
-import android.util.TypedValue;
import android.view.Gravity;
import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import java.io.PrintWriter;
@@ -45,33 +40,29 @@ public class PipBoundsAlgorithm {
private static final String TAG = PipBoundsAlgorithm.class.getSimpleName();
private static final float INVALID_SNAP_FRACTION = -1f;
- private final @NonNull PipBoundsState mPipBoundsState;
+ @NonNull private final PipBoundsState mPipBoundsState;
+ @NonNull protected final PipSizeSpecHandler mPipSizeSpecHandler;
private final PipSnapAlgorithm mSnapAlgorithm;
private final PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
- private float mDefaultSizePercent;
- private float mMinAspectRatioForMinSize;
- private float mMaxAspectRatioForMinSize;
private float mDefaultAspectRatio;
private float mMinAspectRatio;
private float mMaxAspectRatio;
private int mDefaultStackGravity;
- private int mDefaultMinSize;
- private int mOverridableMinSize;
- protected Point mScreenEdgeInsets;
public PipBoundsAlgorithm(Context context, @NonNull PipBoundsState pipBoundsState,
@NonNull PipSnapAlgorithm pipSnapAlgorithm,
- @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm) {
+ @NonNull PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
mPipBoundsState = pipBoundsState;
mSnapAlgorithm = pipSnapAlgorithm;
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
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
// triggers a configuration change and the resources to be reloaded.
mPipBoundsState.setAspectRatio(mDefaultAspectRatio);
- mPipBoundsState.setMinEdgeSize(mDefaultMinSize);
}
/**
@@ -83,27 +74,15 @@ public class PipBoundsAlgorithm {
R.dimen.config_pictureInPictureDefaultAspectRatio);
mDefaultStackGravity = res.getInteger(
R.integer.config_defaultPictureInPictureGravity);
- mDefaultMinSize = res.getDimensionPixelSize(
- R.dimen.default_minimal_size_pip_resizable_task);
- mOverridableMinSize = res.getDimensionPixelSize(
- R.dimen.overridable_minimal_size_pip_resizable_task);
final String screenEdgeInsetsDpString = res.getString(
R.string.config_defaultPictureInPictureScreenEdgeInsets);
final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
? Size.parseSize(screenEdgeInsetsDpString)
: null;
- mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
- : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
- dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
mMinAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
mMaxAspectRatio = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
- mDefaultSizePercent = res.getFloat(
- R.dimen.config_pictureInPictureDefaultSizePercent);
- mMaxAspectRatioForMinSize = res.getFloat(
- R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
- mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
}
/**
@@ -180,8 +159,9 @@ public class PipBoundsAlgorithm {
if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
// If either dimension is smaller than the allowed minimum, adjust them
// according to mOverridableMinSize
- return new Size(Math.max(windowLayout.minWidth, mOverridableMinSize),
- Math.max(windowLayout.minHeight, mOverridableMinSize));
+ return new Size(
+ Math.max(windowLayout.minWidth, mPipSizeSpecHandler.getOverrideMinEdgeSize()),
+ Math.max(windowLayout.minHeight, mPipSizeSpecHandler.getOverrideMinEdgeSize()));
}
return null;
}
@@ -243,28 +223,13 @@ public class PipBoundsAlgorithm {
final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
- final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
final Size size;
if (useCurrentMinEdgeSize || useCurrentSize) {
- // The default minimum edge size, or the override min edge size if set.
- final int defaultMinEdgeSize = overrideMinSize == null ? mDefaultMinSize
- : mPipBoundsState.getOverrideMinEdgeSize();
- final int minEdgeSize = useCurrentMinEdgeSize ? mPipBoundsState.getMinEdgeSize()
- : defaultMinEdgeSize;
- // Use the existing size but adjusted to the aspect ratio and min edge size.
- size = getSizeForAspectRatio(
- new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
+ // Use the existing size but adjusted to the new aspect ratio.
+ size = mPipSizeSpecHandler.getSizeForAspectRatio(
+ new Size(stackBounds.width(), stackBounds.height()), aspectRatio);
} else {
- if (overrideMinSize != null) {
- // The override minimal size is set, use that as the default size making sure it's
- // adjusted to the aspect ratio.
- size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio);
- } else {
- // Calculate the default size using the display size and default min edge size.
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
- displayLayout.width(), displayLayout.height());
- }
+ size = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
}
final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
@@ -273,18 +238,6 @@ public class PipBoundsAlgorithm {
mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
}
- /** Adjusts the given size to conform to the given aspect ratio. */
- private Size adjustSizeToAspectRatio(@NonNull Size size, float aspectRatio) {
- final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
- if (sizeAspectRatio > aspectRatio) {
- // Size is wider, fix the width and increase the height
- return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
- } else {
- // Size is taller, fix the height and adjust the width.
- return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
- }
- }
-
/**
* @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
* provided, then it will apply the default bounds to the provided snap fraction and size.
@@ -303,17 +256,9 @@ public class PipBoundsAlgorithm {
final Size defaultSize;
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
- if (overrideMinSize != null) {
- // The override minimal size is set, use that as the default size making sure it's
- // adjusted to the aspect ratio.
- defaultSize = adjustSizeToAspectRatio(overrideMinSize, mDefaultAspectRatio);
- } else {
- // Calculate the default size using the display size and default min edge size.
- defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
- mDefaultMinSize, displayLayout.width(), displayLayout.height());
- }
+
+ // Calculate the default size
+ defaultSize = mPipSizeSpecHandler.getDefaultSize(mDefaultAspectRatio);
// Now that we have the default size, apply the snap fraction if valid or position the
// bounds using the default gravity.
@@ -335,12 +280,7 @@ public class PipBoundsAlgorithm {
* Populates the bounds on the screen that the PIP can be visible in.
*/
public void getInsetBounds(Rect outRect) {
- final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
- Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
- outRect.set(insets.left + mScreenEdgeInsets.x,
- insets.top + mScreenEdgeInsets.y,
- displayLayout.width() - insets.right - mScreenEdgeInsets.x,
- displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+ outRect.set(mPipSizeSpecHandler.getInsetBounds());
}
/**
@@ -405,71 +345,11 @@ public class PipBoundsAlgorithm {
mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
}
- public int getDefaultMinSize() {
- return mDefaultMinSize;
- }
-
/**
* @return the pixels for a given dp value.
*/
private int dpToPx(float dpValue, DisplayMetrics dm) {
- 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);
+ return PipUtils.dpToPx(dpValue, dm);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 5376ae372de2..61da10be6b67 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -37,6 +37,7 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.io.PrintWriter;
@@ -83,13 +84,10 @@ public class PipBoundsState {
private int mStashedState = STASH_TYPE_NONE;
private int mStashOffset;
private @Nullable PipReentryState mPipReentryState;
+ private final @Nullable PipSizeSpecHandler mPipSizeSpecHandler;
private @Nullable ComponentName mLastPipComponentName;
private int mDisplayId = Display.DEFAULT_DISPLAY;
private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
- /** The current minimum edge size of PIP. */
- private int mMinEdgeSize;
- /** The preferred minimum (and default) size specified by apps. */
- private @Nullable Size mOverrideMinSize;
private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
private boolean mIsImeShowing;
private int mImeHeight;
@@ -122,9 +120,10 @@ public class PipBoundsState {
private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
- public PipBoundsState(@NonNull Context context) {
+ public PipBoundsState(@NonNull Context context, PipSizeSpecHandler pipSizeSpecHandler) {
mContext = context;
reloadResources();
+ mPipSizeSpecHandler = pipSizeSpecHandler;
}
/** Reloads the resources. */
@@ -323,20 +322,10 @@ public class PipBoundsState {
mPipReentryState = null;
}
- /** Set the PIP minimum edge size. */
- public void setMinEdgeSize(int minEdgeSize) {
- mMinEdgeSize = minEdgeSize;
- }
-
- /** Returns the PIP's current minimum edge size. */
- public int getMinEdgeSize() {
- return mMinEdgeSize;
- }
-
/** Sets the preferred size of PIP as specified by the activity in PIP mode. */
public void setOverrideMinSize(@Nullable Size overrideMinSize) {
- final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
- mOverrideMinSize = overrideMinSize;
+ final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize());
+ mPipSizeSpecHandler.setOverrideMinSize(overrideMinSize);
if (changed && mOnMinimalSizeChangeCallback != null) {
mOnMinimalSizeChangeCallback.run();
}
@@ -345,13 +334,12 @@ public class PipBoundsState {
/** Returns the preferred minimal size specified by the activity in PIP. */
@Nullable
public Size getOverrideMinSize() {
- return mOverrideMinSize;
+ return mPipSizeSpecHandler.getOverrideMinSize();
}
/** Returns the minimum edge size of the override minimum size, or 0 if not set. */
public int getOverrideMinEdgeSize() {
- if (mOverrideMinSize == null) return 0;
- return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
+ return mPipSizeSpecHandler.getOverrideMinEdgeSize();
}
/** Get the state of the bounds in motion. */
@@ -581,11 +569,8 @@ public class PipBoundsState {
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
- pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
pw.println(innerPrefix + "mStashedState=" + mStashedState);
pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
- pw.println(innerPrefix + "mMinEdgeSize=" + mMinEdgeSize);
- pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index fa0061982c45..8b98790d3499 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.pip;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
@@ -26,8 +27,10 @@ import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
+import android.util.TypedValue;
import android.window.TaskSnapshot;
import com.android.internal.protolog.common.ProtoLog;
@@ -70,6 +73,13 @@ public class PipUtils {
}
/**
+ * @return the pixels for a given dp value.
+ */
+ public static int dpToPx(float dpValue, DisplayMetrics dm) {
+ return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
+ }
+
+ /**
* @return true if the aspect ratios differ
*/
public static boolean aspectRatioChanged(float aspectRatio1, float aspectRatio2) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 525beb166f7e..d86468a838d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -137,6 +137,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private PipKeepClearAlgorithmInterface mPipKeepClearAlgorithm;
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private PipMotionHelper mPipMotionHelper;
private PipTouchHandler mTouchHandler;
private PipTransitionController mPipTransitionController;
@@ -380,6 +381,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -401,11 +403,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
return new PipController(context, shellInit, shellCommandHandler, shellController,
displayController, pipAnimationController, pipAppOpsListener,
- pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
- pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
- pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
- taskStackListener, pipParamsChangedForwarder, displayInsetsController,
- oneHandedController, mainExecutor)
+ pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipSizeSpecHandler,
+ pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer,
+ pipTransitionState, pipTouchHandler, pipTransitionController,
+ windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
+ displayInsetsController, oneHandedController, mainExecutor)
.mImpl;
}
@@ -419,6 +421,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithmInterface pipKeepClearAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
PipMotionHelper pipMotionHelper,
PipMediaController pipMediaController,
PhonePipMenuController phonePipMenuController,
@@ -444,6 +447,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipKeepClearAlgorithm = pipKeepClearAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
mPipMotionHelper = pipMotionHelper;
mPipTaskOrganizer = pipTaskOrganizer;
mPipTransitionState = pipTransitionState;
@@ -512,7 +516,10 @@ public class PipController implements PipTransitionController.PipTransitionCallb
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
mPipBoundsState.setDisplayId(mContext.getDisplayId());
- mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
+
+ DisplayLayout layout = new DisplayLayout(mContext, mContext.getDisplay());
+ mPipSizeSpecHandler.setDisplayLayout(layout);
+ mPipBoundsState.setDisplayLayout(layout);
try {
mWindowManagerShellWrapper.addPinnedStackListener(mPinnedTaskListener);
@@ -686,6 +693,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
mPipBoundsState.onConfigurationChanged();
+ mPipSizeSpecHandler.onConfigurationChanged();
}
@Override
@@ -711,7 +719,11 @@ public class PipController implements PipTransitionController.PipTransitionCallb
Runnable updateDisplayLayout = () -> {
final boolean fromRotation = Transitions.ENABLE_SHELL_TRANSITIONS
&& mPipBoundsState.getDisplayLayout().rotation() != layout.rotation();
+
+ // update the internal state of objects subscribed to display changes
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mPipBoundsState.setDisplayLayout(layout);
+
final WindowContainerTransaction wct =
fromRotation ? new WindowContainerTransaction() : null;
updateMovementBounds(null /* toBounds */,
@@ -1083,6 +1095,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mPipTaskOrganizer.dump(pw, innerPrefix);
mPipBoundsState.dump(pw, innerPrefix);
mPipInputConsumer.dump(pw, innerPrefix);
+ mPipSizeSpecHandler.dump(pw, innerPrefix);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
new file mode 100644
index 000000000000..a906522b8de2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipSizeSpecHandler.java
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static com.android.wm.shell.pip.PipUtils.dpToPx;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.SystemProperties;
+import android.util.Size;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.R;
+import com.android.wm.shell.common.DisplayLayout;
+
+import java.io.PrintWriter;
+
+/**
+ * Acts as a source of truth for appropriate size spec for PIP.
+ */
+public class PipSizeSpecHandler {
+ private static final String TAG = PipSizeSpecHandler.class.getSimpleName();
+
+ @NonNull private final DisplayLayout mDisplayLayout = new DisplayLayout();
+
+ @VisibleForTesting
+ final SizeSpecSource mSizeSpecSourceImpl;
+
+ /** The preferred minimum (and default) size specified by apps. */
+ @Nullable private Size mOverrideMinSize;
+
+ /** Used to store values obtained from resource files. */
+ private Point mScreenEdgeInsets;
+ private float mMinAspectRatioForMinSize;
+ private float mMaxAspectRatioForMinSize;
+ private int mDefaultMinSize;
+
+ @NonNull private final Context mContext;
+
+ private interface SizeSpecSource {
+ /** Returns max size allowed for the PIP window */
+ Size getMaxSize(float aspectRatio);
+
+ /** Returns default size for the PIP window */
+ Size getDefaultSize(float aspectRatio);
+
+ /** Returns min size allowed for the PIP window */
+ Size getMinSize(float aspectRatio);
+
+ /** Returns the adjusted size based on current size and target aspect ratio */
+ Size getSizeForAspectRatio(Size size, float aspectRatio);
+
+ /** Updates internal resources on configuration changes */
+ default void reloadResources() {}
+ }
+
+ /**
+ * Determines PIP window size optimized for large screens and aspect ratios close to 1:1
+ */
+ private class SizeSpecLargeScreenOptimizedImpl implements SizeSpecSource {
+ private static final float DEFAULT_OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+ /** Default and minimum percentages for the PIP size logic. */
+ private final float mDefaultSizePercent;
+ private final float mMinimumSizePercent;
+
+ /** Aspect ratio that the PIP size spec logic optimizes for. */
+ private float mOptimizedAspectRatio;
+
+ private SizeSpecLargeScreenOptimizedImpl() {
+ mDefaultSizePercent = Float.parseFloat(SystemProperties
+ .get("com.android.wm.shell.pip.phone.def_percentage", "0.6"));
+ mMinimumSizePercent = Float.parseFloat(SystemProperties
+ .get("com.android.wm.shell.pip.phone.min_percentage", "0.5"));
+ }
+
+ @Override
+ public void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mOptimizedAspectRatio = res.getFloat(R.dimen.config_pipLargeScreenOptimizedAspectRatio);
+ // make sure the optimized aspect ratio is valid with a default value to fall back to
+ if (mOptimizedAspectRatio > 1) {
+ mOptimizedAspectRatio = DEFAULT_OPTIMIZED_ASPECT_RATIO;
+ }
+ }
+
+ /**
+ * Calculates the max size of PIP.
+ *
+ * Optimizes for 16:9 aspect ratios, making them take full length of shortest display edge.
+ * As aspect ratio approaches values close to 1:1, the logic does not let PIP occupy the
+ * whole screen. A linear function is used to calculate these sizes.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the max size of the PIP
+ */
+ @Override
+ public Size getMaxSize(float aspectRatio) {
+ final int totalHorizontalPadding = getInsetBounds().left
+ + (getDisplayBounds().width() - getInsetBounds().right);
+ final int totalVerticalPadding = getInsetBounds().top
+ + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+ final int shorterLength = (int) (1f * Math.min(
+ getDisplayBounds().width() - totalHorizontalPadding,
+ getDisplayBounds().height() - totalVerticalPadding));
+
+ int maxWidth, maxHeight;
+
+ // use the optimized max sizing logic only within a certain aspect ratio range
+ if (aspectRatio >= mOptimizedAspectRatio && aspectRatio <= 1 / mOptimizedAspectRatio) {
+ // this formula and its derivation is explained in b/198643358#comment16
+ maxWidth = (int) (mOptimizedAspectRatio * shorterLength
+ + shorterLength * (aspectRatio - mOptimizedAspectRatio) / (1
+ + aspectRatio));
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ if (aspectRatio > 1f) {
+ maxWidth = shorterLength;
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ maxHeight = shorterLength;
+ maxWidth = (int) (maxHeight * aspectRatio);
+ }
+ }
+
+ return new Size(maxWidth, maxHeight);
+ }
+
+ /**
+ * Decreases the dimensions by a percentage relative to max size to get default size.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the default size of the PIP
+ */
+ @Override
+ public Size getDefaultSize(float aspectRatio) {
+ Size minSize = this.getMinSize(aspectRatio);
+
+ if (mOverrideMinSize != null) {
+ return minSize;
+ }
+
+ Size maxSize = this.getMaxSize(aspectRatio);
+
+ int defaultWidth = Math.max((int) (maxSize.getWidth() * mDefaultSizePercent),
+ minSize.getWidth());
+ int defaultHeight = Math.max((int) (maxSize.getHeight() * mDefaultSizePercent),
+ minSize.getHeight());
+
+ return new Size(defaultWidth, defaultHeight);
+ }
+
+ /**
+ * Decreases the dimensions by a certain percentage relative to max size to get min size.
+ *
+ * @param aspectRatio aspect ratio of the PIP window
+ * @return dimensions of the min size of the PIP
+ */
+ @Override
+ public Size getMinSize(float aspectRatio) {
+ // if there is an overridden min size provided, return that
+ if (mOverrideMinSize != null) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ Size maxSize = this.getMaxSize(aspectRatio);
+
+ int minWidth = (int) (maxSize.getWidth() * mMinimumSizePercent);
+ int minHeight = (int) (maxSize.getHeight() * mMinimumSizePercent);
+
+ // make sure the calculated min size is not smaller than the allowed default min size
+ if (aspectRatio > 1f) {
+ minHeight = (int) Math.max(minHeight, mDefaultMinSize);
+ minWidth = (int) (minHeight * aspectRatio);
+ } else {
+ minWidth = (int) Math.max(minWidth, mDefaultMinSize);
+ minHeight = (int) (minWidth / aspectRatio);
+ }
+ return new Size(minWidth, minHeight);
+ }
+
+ /**
+ * Returns the size for target aspect ratio making sure new size conforms with the rules.
+ *
+ * <p>Recalculates the dimensions such that the target aspect ratio is achieved, while
+ * maintaining the same maximum size to current size ratio.
+ *
+ * @param size current size
+ * @param aspectRatio target aspect ratio
+ */
+ @Override
+ public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+ // getting the percentage of the max size that current size takes
+ float currAspectRatio = (float) size.getWidth() / size.getHeight();
+ Size currentMaxSize = getMaxSize(currAspectRatio);
+ float currentPercent = (float) size.getWidth() / currentMaxSize.getWidth();
+
+ // getting the max size for the target aspect ratio
+ Size updatedMaxSize = getMaxSize(aspectRatio);
+
+ int width = (int) (updatedMaxSize.getWidth() * currentPercent);
+ int height = (int) (updatedMaxSize.getHeight() * currentPercent);
+
+ // adjust the dimensions if below allowed min edge size
+ if (width < getMinEdgeSize() && aspectRatio <= 1) {
+ width = getMinEdgeSize();
+ height = (int) (width / aspectRatio);
+ } else if (height < getMinEdgeSize() && aspectRatio > 1) {
+ height = getMinEdgeSize();
+ width = (int) (height * aspectRatio);
+ }
+
+ // reduce the dimensions of the updated size to the calculated percentage
+ return new Size(width, height);
+ }
+ }
+
+ private class SizeSpecDefaultImpl implements SizeSpecSource {
+ private float mDefaultSizePercent;
+ private float mMinimumSizePercent;
+
+ @Override
+ public void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mMaxAspectRatioForMinSize = res.getFloat(
+ R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
+ mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
+
+ mDefaultSizePercent = res.getFloat(R.dimen.config_pictureInPictureDefaultSizePercent);
+ mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
+ }
+
+ @Override
+ public Size getMaxSize(float aspectRatio) {
+ final int shorterLength = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+
+ final int totalHorizontalPadding = getInsetBounds().left
+ + (getDisplayBounds().width() - getInsetBounds().right);
+ final int totalVerticalPadding = getInsetBounds().top
+ + (getDisplayBounds().height() - getInsetBounds().bottom);
+
+ final int maxWidth, maxHeight;
+
+ if (aspectRatio > 1f) {
+ maxWidth = (int) Math.max(getDefaultSize(aspectRatio).getWidth(),
+ shorterLength - totalHorizontalPadding);
+ maxHeight = (int) (maxWidth / aspectRatio);
+ } else {
+ maxHeight = (int) Math.max(getDefaultSize(aspectRatio).getHeight(),
+ shorterLength - totalVerticalPadding);
+ maxWidth = (int) (maxHeight * aspectRatio);
+ }
+
+ return new Size(maxWidth, maxHeight);
+ }
+
+ @Override
+ public Size getDefaultSize(float aspectRatio) {
+ if (mOverrideMinSize != null) {
+ return this.getMinSize(aspectRatio);
+ }
+
+ final int smallestDisplaySize = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+ final int minSize = (int) Math.max(getMinEdgeSize(),
+ 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, 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);
+ }
+
+ @Override
+ public Size getMinSize(float aspectRatio) {
+ if (mOverrideMinSize != null) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ final int shorterLength = Math.min(getDisplayBounds().width(),
+ getDisplayBounds().height());
+ final int minWidth, minHeight;
+
+ if (aspectRatio > 1f) {
+ minWidth = (int) Math.min(getDefaultSize(aspectRatio).getWidth(),
+ shorterLength * mMinimumSizePercent);
+ minHeight = (int) (minWidth / aspectRatio);
+ } else {
+ minHeight = (int) Math.min(getDefaultSize(aspectRatio).getHeight(),
+ shorterLength * mMinimumSizePercent);
+ minWidth = (int) (minHeight * aspectRatio);
+ }
+
+ return new Size(minWidth, minHeight);
+ }
+
+ @Override
+ public Size getSizeForAspectRatio(Size size, float aspectRatio) {
+ final int smallestSize = Math.min(size.getWidth(), size.getHeight());
+ final int minSize = Math.max(getMinEdgeSize(), 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);
+ }
+ }
+
+ public PipSizeSpecHandler(Context context) {
+ mContext = context;
+
+ boolean enablePipSizeLargeScreen = SystemProperties
+ .getBoolean("persist.wm.debug.enable_pip_size_large_screen", false);
+
+ // choose between two implementations of size spec logic
+ if (enablePipSizeLargeScreen) {
+ mSizeSpecSourceImpl = new SizeSpecLargeScreenOptimizedImpl();
+ } else {
+ mSizeSpecSourceImpl = new SizeSpecDefaultImpl();
+ }
+
+ reloadResources();
+ }
+
+ /** Reloads the resources */
+ public void onConfigurationChanged() {
+ reloadResources();
+ }
+
+ private void reloadResources() {
+ final Resources res = mContext.getResources();
+
+ mDefaultMinSize = res.getDimensionPixelSize(
+ R.dimen.default_minimal_size_pip_resizable_task);
+
+ final String screenEdgeInsetsDpString = res.getString(
+ R.string.config_defaultPictureInPictureScreenEdgeInsets);
+ final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
+ ? Size.parseSize(screenEdgeInsetsDpString)
+ : null;
+ mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
+ : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
+ dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
+
+ // update the internal resources of the size spec source's stub
+ mSizeSpecSourceImpl.reloadResources();
+ }
+
+ /** Returns the display's bounds. */
+ @NonNull
+ public Rect getDisplayBounds() {
+ return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+ }
+
+ /** Get the display layout. */
+ @NonNull
+ public DisplayLayout getDisplayLayout() {
+ return mDisplayLayout;
+ }
+
+ /** Update the display layout. */
+ public void setDisplayLayout(@NonNull DisplayLayout displayLayout) {
+ mDisplayLayout.set(displayLayout);
+ }
+
+ public Point getScreenEdgeInsets() {
+ return mScreenEdgeInsets;
+ }
+
+ /**
+ * Returns the inset bounds the PIP window can be visible in.
+ */
+ public Rect getInsetBounds() {
+ Rect insetBounds = new Rect();
+ final DisplayLayout displayLayout = getDisplayLayout();
+ Rect insets = getDisplayLayout().stableInsets();
+ insetBounds.set(insets.left + mScreenEdgeInsets.x,
+ insets.top + mScreenEdgeInsets.y,
+ displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+ displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
+ return insetBounds;
+ }
+
+ /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
+ public void setOverrideMinSize(@Nullable Size overrideMinSize) {
+ mOverrideMinSize = overrideMinSize;
+ }
+
+ /** Returns the preferred minimal size specified by the activity in PIP. */
+ @Nullable
+ public Size getOverrideMinSize() {
+ return mOverrideMinSize;
+ }
+
+ /** Returns the minimum edge size of the override minimum size, or 0 if not set. */
+ public int getOverrideMinEdgeSize() {
+ if (mOverrideMinSize == null) return 0;
+ return Math.min(mOverrideMinSize.getWidth(), mOverrideMinSize.getHeight());
+ }
+
+ public int getMinEdgeSize() {
+ return mOverrideMinSize == null ? mDefaultMinSize : getOverrideMinEdgeSize();
+ }
+
+ /**
+ * Returns the size for the max size spec.
+ */
+ public Size getMaxSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getMaxSize(aspectRatio);
+ }
+
+ /**
+ * Returns the size for the default size spec.
+ */
+ public Size getDefaultSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getDefaultSize(aspectRatio);
+ }
+
+ /**
+ * Returns the size for the min size spec.
+ */
+ public Size getMinSize(float aspectRatio) {
+ return mSizeSpecSourceImpl.getMinSize(aspectRatio);
+ }
+
+ /**
+ * Returns the adjusted size so that it conforms to the given aspectRatio.
+ *
+ * @param size current size
+ * @param aspectRatio target aspect ratio
+ */
+ public Size getSizeForAspectRatio(@NonNull Size size, float aspectRatio) {
+ if (size.equals(mOverrideMinSize)) {
+ return adjustOverrideMinSizeToAspectRatio(aspectRatio);
+ }
+
+ return mSizeSpecSourceImpl.getSizeForAspectRatio(size, aspectRatio);
+ }
+
+ /**
+ * Returns the adjusted overridden min size if it is set; otherwise, returns null.
+ *
+ * <p>Overridden min size needs to be adjusted in its own way while making sure that the target
+ * aspect ratio is maintained
+ *
+ * @param aspectRatio target aspect ratio
+ */
+ @Nullable
+ @VisibleForTesting
+ Size adjustOverrideMinSizeToAspectRatio(float aspectRatio) {
+ if (mOverrideMinSize == null) {
+ return null;
+ }
+ final Size size = mOverrideMinSize;
+ final float sizeAspectRatio = size.getWidth() / (float) size.getHeight();
+ if (sizeAspectRatio > aspectRatio) {
+ // Size is wider, fix the width and increase the height
+ return new Size(size.getWidth(), (int) (size.getWidth() / aspectRatio));
+ } else {
+ // Size is taller, fix the height and adjust the width.
+ return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
+ }
+ }
+
+ /** Dumps internal state. */
+ public void dump(PrintWriter pw, String prefix) {
+ final String innerPrefix = prefix + " ";
+ pw.println(prefix + TAG);
+ pw.println(innerPrefix + "mSizeSpecSourceImpl=" + mSizeSpecSourceImpl.toString());
+ pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
+ pw.println(innerPrefix + "mOverrideMinSize=" + mOverrideMinSize);
+ }
+}
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 850c561c891f..0e8d13d9979d 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
@@ -78,7 +78,8 @@ public class PipTouchHandler {
private boolean mEnableResize;
private final Context mContext;
private final PipBoundsAlgorithm mPipBoundsAlgorithm;
- private final @NonNull PipBoundsState mPipBoundsState;
+ @NonNull private final PipBoundsState mPipBoundsState;
+ @NonNull private final PipSizeSpecHandler mPipSizeSpecHandler;
private final PipUiEventLogger mPipUiEventLogger;
private final PipDismissTargetHandler mPipDismissTargetHandler;
private final PipTaskOrganizer mPipTaskOrganizer;
@@ -99,7 +100,6 @@ public class PipTouchHandler {
// The reference inset bounds, used to determine the dismiss fraction
private final Rect mInsetBounds = new Rect();
- private int mExpandedShortestEdgeSize;
// Used to workaround an issue where the WM rotation happens before we are notified, allowing
// us to send stale bounds
@@ -120,7 +120,6 @@ public class PipTouchHandler {
private float mSavedSnapFraction = -1f;
private boolean mSendingHoverAccessibilityEvents;
private boolean mMovementWithinDismiss;
- private float mMinimumSizePercent;
// Touch state
private final PipTouchState mTouchState;
@@ -174,6 +173,7 @@ public class PipTouchHandler {
PhonePipMenuController menuController,
PipBoundsAlgorithm pipBoundsAlgorithm,
@NonNull PipBoundsState pipBoundsState,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler,
PipTaskOrganizer pipTaskOrganizer,
PipMotionHelper pipMotionHelper,
FloatingContentCoordinator floatingContentCoordinator,
@@ -184,6 +184,7 @@ public class PipTouchHandler {
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipBoundsState = pipBoundsState;
+ mPipSizeSpecHandler = pipSizeSpecHandler;
mPipTaskOrganizer = pipTaskOrganizer;
mMenuController = menuController;
mPipUiEventLogger = pipUiEventLogger;
@@ -271,10 +272,7 @@ public class PipTouchHandler {
private void reloadResources() {
final Resources res = mContext.getResources();
mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer);
- mExpandedShortestEdgeSize = res.getDimensionPixelSize(
- R.dimen.pip_expanded_shortest_edge_size);
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
- mMinimumSizePercent = res.getFraction(R.fraction.config_pipShortestEdgePercent, 1, 1);
mPipDismissTargetHandler.updateMagneticTargetSize();
}
@@ -407,10 +405,7 @@ public class PipTouchHandler {
// Calculate the expanded size
float aspectRatio = (float) normalBounds.width() / normalBounds.height();
- Point displaySize = new Point();
- mContext.getDisplay().getRealSize(displaySize);
- Size expandedSize = mPipBoundsAlgorithm.getSizeForAspectRatio(
- aspectRatio, mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
+ Size expandedSize = mPipSizeSpecHandler.getDefaultSize(aspectRatio);
mPipBoundsState.setExpandedBounds(
new Rect(0, 0, expandedSize.getWidth(), expandedSize.getHeight()));
Rect expandedMovementBounds = new Rect();
@@ -418,7 +413,7 @@ public class PipTouchHandler {
mPipBoundsState.getExpandedBounds(), insetBounds, expandedMovementBounds,
bottomOffset);
- updatePipSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ updatePipSizeConstraints(normalBounds, aspectRatio);
// The extra offset does not really affect the movement bounds, but are applied based on the
// current state (ime showing, or shelf offset) when we need to actually shift
@@ -496,14 +491,14 @@ public class PipTouchHandler {
* @param aspectRatio aspect ratio to use for the calculation of min/max size
*/
public void updateMinMaxSize(float aspectRatio) {
- updatePipSizeConstraints(mInsetBounds, mPipBoundsState.getNormalBounds(),
+ updatePipSizeConstraints(mPipBoundsState.getNormalBounds(),
aspectRatio);
}
- private void updatePipSizeConstraints(Rect insetBounds, Rect normalBounds,
+ private void updatePipSizeConstraints(Rect normalBounds,
float aspectRatio) {
if (mPipResizeGestureHandler.isUsingPinchToZoom()) {
- updatePinchResizeSizeConstraints(insetBounds, normalBounds, aspectRatio);
+ updatePinchResizeSizeConstraints(aspectRatio);
} else {
mPipResizeGestureHandler.updateMinSize(normalBounds.width(), normalBounds.height());
mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getExpandedBounds().width(),
@@ -511,26 +506,13 @@ public class PipTouchHandler {
}
}
- private void updatePinchResizeSizeConstraints(Rect insetBounds, Rect normalBounds,
- float aspectRatio) {
- final int shorterLength = Math.min(mPipBoundsState.getDisplayBounds().width(),
- mPipBoundsState.getDisplayBounds().height());
- final int totalHorizontalPadding = insetBounds.left
- + (mPipBoundsState.getDisplayBounds().width() - insetBounds.right);
- final int totalVerticalPadding = insetBounds.top
- + (mPipBoundsState.getDisplayBounds().height() - insetBounds.bottom);
+ private void updatePinchResizeSizeConstraints(float aspectRatio) {
final int minWidth, minHeight, maxWidth, maxHeight;
- if (aspectRatio > 1f) {
- minWidth = (int) Math.min(normalBounds.width(), shorterLength * mMinimumSizePercent);
- minHeight = (int) (minWidth / aspectRatio);
- maxWidth = (int) Math.max(normalBounds.width(), shorterLength - totalHorizontalPadding);
- maxHeight = (int) (maxWidth / aspectRatio);
- } else {
- minHeight = (int) Math.min(normalBounds.height(), shorterLength * mMinimumSizePercent);
- minWidth = (int) (minHeight * aspectRatio);
- maxHeight = (int) Math.max(normalBounds.height(), shorterLength - totalVerticalPadding);
- maxWidth = (int) (maxHeight * aspectRatio);
- }
+
+ minWidth = mPipSizeSpecHandler.getMinSize(aspectRatio).getWidth();
+ minHeight = mPipSizeSpecHandler.getMinSize(aspectRatio).getHeight();
+ maxWidth = mPipSizeSpecHandler.getMaxSize(aspectRatio).getWidth();
+ maxHeight = mPipSizeSpecHandler.getMaxSize(aspectRatio).getHeight();
mPipResizeGestureHandler.updateMinSize(minWidth, minHeight);
mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight);
@@ -1064,11 +1046,6 @@ public class PipTouchHandler {
mPipBoundsAlgorithm.getMovementBounds(mPipBoundsState.getBounds(),
mInsetBounds, mPipBoundsState.getMovementBounds(), mIsImeShowing ? mImeHeight : 0);
mMotionHelper.onMovementBoundsChanged();
-
- boolean isMenuExpanded = mMenuState == MENU_STATE_FULL;
- mPipBoundsState.setMinEdgeSize(
- isMenuExpanded && willResizeMenu() ? mExpandedShortestEdgeSize
- : mPipBoundsAlgorithm.getDefaultMinSize());
}
private Rect getMovementBounds(Rect curBounds) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
index 1ff77f7d36dc..22feb43ffd62 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsAlgorithm.java
@@ -41,6 +41,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipKeepClearAlgorithmInterface;
import com.android.wm.shell.pip.PipSnapAlgorithm;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.pip.tv.TvPipKeepClearAlgorithm.Placement;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -63,9 +64,10 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
public TvPipBoundsAlgorithm(Context context,
@NonNull TvPipBoundsState tvPipBoundsState,
- @NonNull PipSnapAlgorithm pipSnapAlgorithm) {
+ @NonNull PipSnapAlgorithm pipSnapAlgorithm,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
super(context, tvPipBoundsState, pipSnapAlgorithm,
- new PipKeepClearAlgorithmInterface() {});
+ new PipKeepClearAlgorithmInterface() {}, pipSizeSpecHandler);
this.mTvPipBoundsState = tvPipBoundsState;
this.mKeepClearAlgorithm = new TvPipKeepClearAlgorithm();
reloadResources(context);
@@ -370,7 +372,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_HORIZONTAL) {
expandedSize = mTvPipBoundsState.getTvExpandedSize();
} else {
- int maxHeight = displayLayout.height() - (2 * mScreenEdgeInsets.y)
+ int maxHeight = displayLayout.height()
+ - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().y)
- pipDecorations.top - pipDecorations.bottom;
float aspectRatioHeight = mFixedExpandedWidthInPx / expandedRatio;
@@ -393,7 +396,8 @@ public class TvPipBoundsAlgorithm extends PipBoundsAlgorithm {
if (mTvPipBoundsState.getTvFixedPipOrientation() == ORIENTATION_VERTICAL) {
expandedSize = mTvPipBoundsState.getTvExpandedSize();
} else {
- int maxWidth = displayLayout.width() - (2 * mScreenEdgeInsets.x)
+ int maxWidth = displayLayout.width()
+ - (2 * mPipSizeSpecHandler.getScreenEdgeInsets().x)
- pipDecorations.left - pipDecorations.right;
float aspectRatioWidth = mFixedExpandedHeightInPx * expandedRatio;
if (maxWidth > aspectRatioWidth) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index ca22882187d8..4e3ee51326c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -30,6 +30,7 @@ import android.view.Gravity;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -64,8 +65,9 @@ public class TvPipBoundsState extends PipBoundsState {
private @NonNull Insets mPipMenuPermanentDecorInsets = Insets.NONE;
private @NonNull Insets mPipMenuTemporaryDecorInsets = Insets.NONE;
- public TvPipBoundsState(@NonNull Context context) {
- super(context);
+ public TvPipBoundsState(@NonNull Context context,
+ @NonNull PipSizeSpecHandler pipSizeSpecHandler) {
+ super(context, pipSizeSpecHandler);
mIsTvExpandedPipSupported = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_EXPANDED_PICTURE_IN_PICTURE);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 4e1b0469eb96..6bc666f074e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -50,6 +50,7 @@ import com.android.wm.shell.pip.PipMediaController;
import com.android.wm.shell.pip.PipParamsChangedForwarder;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
import com.android.wm.shell.sysui.ShellController;
@@ -102,6 +103,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
private final ShellController mShellController;
private final TvPipBoundsState mTvPipBoundsState;
+ private final PipSizeSpecHandler mPipSizeSpecHandler;
private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm;
private final TvPipBoundsController mTvPipBoundsController;
private final PipAppOpsListener mAppOpsListener;
@@ -133,6 +135,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -151,6 +154,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
shellInit,
shellController,
tvPipBoundsState,
+ pipSizeSpecHandler,
tvPipBoundsAlgorithm,
tvPipBoundsController,
pipAppOpsListener,
@@ -171,6 +175,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
ShellInit shellInit,
ShellController shellController,
TvPipBoundsState tvPipBoundsState,
+ PipSizeSpecHandler pipSizeSpecHandler,
TvPipBoundsAlgorithm tvPipBoundsAlgorithm,
TvPipBoundsController tvPipBoundsController,
PipAppOpsListener pipAppOpsListener,
@@ -189,9 +194,13 @@ public class TvPipController implements PipTransitionController.PipTransitionCal
mShellController = shellController;
mDisplayController = displayController;
+ DisplayLayout layout = new DisplayLayout(context, context.getDisplay());
+
mTvPipBoundsState = tvPipBoundsState;
+ mTvPipBoundsState.setDisplayLayout(layout);
mTvPipBoundsState.setDisplayId(context.getDisplayId());
- mTvPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
+ mPipSizeSpecHandler = pipSizeSpecHandler;
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mTvPipBoundsAlgorithm = tvPipBoundsAlgorithm;
mTvPipBoundsController = tvPipBoundsController;
mTvPipBoundsController.setListener(this);
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 298d0a624869..3d8cd2e4776d 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
@@ -32,6 +32,7 @@ import androidx.test.filters.SmallTest;
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import org.junit.Before;
import org.junit.Test;
@@ -57,17 +58,22 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
private PipBoundsAlgorithm mPipBoundsAlgorithm;
private DisplayInfo mDefaultDisplayInfo;
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
@Before
public void setUp() throws Exception {
initializeMockResources();
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+ mPipSizeSpecHandler);
- mPipBoundsState.setDisplayLayout(
- new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
+ DisplayLayout layout =
+ new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true);
+ mPipBoundsState.setDisplayLayout(layout);
+ mPipSizeSpecHandler.setDisplayLayout(layout);
}
private void initializeMockResources() {
@@ -120,9 +126,7 @@ public class PipBoundsAlgorithmTest extends ShellTestCase {
@Test
public void getDefaultBounds_noOverrideMinSize_matchesDefaultSizeAndAspectRatio() {
- final Size defaultSize = mPipBoundsAlgorithm.getSizeForAspectRatio(DEFAULT_ASPECT_RATIO,
- DEFAULT_MIN_EDGE_SIZE, mDefaultDisplayInfo.logicalWidth,
- mDefaultDisplayInfo.logicalHeight);
+ final Size defaultSize = mPipSizeSpecHandler.getDefaultSize(DEFAULT_ASPECT_RATIO);
mPipBoundsState.setOverrideMinSize(null);
final Rect defaultBounds = mPipBoundsAlgorithm.getDefaultBounds();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
index 8e30f65cee78..ede5bde9c92c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java
@@ -33,6 +33,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.util.function.TriConsumer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import org.junit.Before;
import org.junit.Test;
@@ -57,7 +58,7 @@ public class PipBoundsStateTest extends ShellTestCase {
@Before
public void setUp() {
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, new PipSizeSpecHandler(mContext));
mTestComponentName1 = new ComponentName(mContext, "component1");
mTestComponentName2 = new ComponentName(mContext, "component2");
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 17e7d74c57e0..2da4af80fd18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -53,6 +53,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
+import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
@@ -86,6 +87,7 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private PipBoundsState mPipBoundsState;
private PipTransitionState mPipTransitionState;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private ComponentName mComponent1;
private ComponentName mComponent2;
@@ -95,10 +97,12 @@ public class PipTaskOrganizerTest extends ShellTestCase {
MockitoAnnotations.initMocks(this);
mComponent1 = new ComponentName(mContext, "component1");
mComponent2 = new ComponentName(mContext, "component2");
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipTransitionState = new PipTransitionState();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState,
- new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {});
+ new PipSnapAlgorithm(), new PipKeepClearAlgorithmInterface() {},
+ mPipSizeSpecHandler);
mMainExecutor = new TestShellExecutor();
mPipTaskOrganizer = new PipTaskOrganizer(mContext,
mMockSyncTransactionQueue, mPipTransitionState, mPipBoundsState,
@@ -253,8 +257,10 @@ public class PipTaskOrganizerTest extends ShellTestCase {
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
- mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
- mContext.getResources(), true, true));
+ DisplayLayout layout = new DisplayLayout(info,
+ mContext.getResources(), true, true);
+ mPipBoundsState.setDisplayLayout(layout);
+ mPipSizeSpecHandler.setDisplayLayout(layout);
mPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
mPipTaskOrganizer.setSurfaceControlTransactionFactory(
MockSurfaceControlHelper::createMockSurfaceControlTransaction);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 35c09a121a1c..4a68287a4486 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -65,7 +65,6 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
-import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -108,6 +107,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private PipMotionHelper mMockPipMotionHelper;
@Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper;
@Mock private PipBoundsState mMockPipBoundsState;
+ @Mock private PipSizeSpecHandler mMockPipSizeSpecHandler;
@Mock private TaskStackListenerImpl mMockTaskStackListener;
@Mock private ShellExecutor mMockExecutor;
@Mock private Optional<OneHandedController> mMockOneHandedController;
@@ -130,11 +130,12 @@ public class PipControllerTest extends ShellTestCase {
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockPipParamsChangedForwarder,
- mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor);
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+ mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+ mMockWindowManagerShellWrapper, mMockTaskStackListener,
+ mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+ mMockOneHandedController, mMockExecutor);
mShellInit.init();
when(mMockPipBoundsAlgorithm.getSnapAlgorithm()).thenReturn(mMockPipSnapAlgorithm);
when(mMockPipTouchHandler.getMotionHelper()).thenReturn(mMockPipMotionHelper);
@@ -220,11 +221,12 @@ public class PipControllerTest extends ShellTestCase {
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
mShellController, mMockDisplayController, mMockPipAnimationController,
mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
- mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
- mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
- mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
- mMockTaskStackListener, mMockPipParamsChangedForwarder,
- mMockDisplayInsetsController, mMockOneHandedController, mMockExecutor));
+ mMockPipBoundsState, mMockPipSizeSpecHandler, mMockPipMotionHelper,
+ mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer,
+ mMockPipTransitionState, mMockPipTouchHandler, mMockPipTransitionController,
+ mMockWindowManagerShellWrapper, mMockTaskStackListener,
+ mMockPipParamsChangedForwarder, mMockDisplayInsetsController,
+ mMockOneHandedController, mMockExecutor));
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index c1993b25030b..c7b9eb3d1074 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -85,15 +85,18 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
private PipBoundsState mPipBoundsState;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
final PipSnapAlgorithm pipSnapAlgorithm = new PipSnapAlgorithm();
final PipKeepClearAlgorithmInterface pipKeepClearAlgorithm =
new PipKeepClearAlgorithmInterface() {};
final PipBoundsAlgorithm pipBoundsAlgorithm = new PipBoundsAlgorithm(mContext,
- mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm);
+ mPipBoundsState, pipSnapAlgorithm, pipKeepClearAlgorithm, mPipSizeSpecHandler);
final PipMotionHelper motionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, pipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
new file mode 100644
index 000000000000..d9ff7d1f1089
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipSizeSpecHandlerTest.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip.phone;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.testing.AndroidTestingRunner;
+import android.util.Size;
+import android.view.DisplayInfo;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.exceptions.misusing.InvalidUseOfMatchersException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+/**
+ * Unit test against {@link PipSizeSpecHandler} with feature flag on.
+ */
+@RunWith(AndroidTestingRunner.class)
+public class PipSizeSpecHandlerTest extends ShellTestCase {
+ /** A sample overridden min edge size. */
+ private static final int OVERRIDE_MIN_EDGE_SIZE = 40;
+ /** A sample default min edge size */
+ private static final int DEFAULT_MIN_EDGE_SIZE = 40;
+ /** Display edge size */
+ private static final int DISPLAY_EDGE_SIZE = 1000;
+ /** Default sizing percentage */
+ private static final float DEFAULT_PERCENT = 0.6f;
+ /** Minimum sizing percentage */
+ private static final float MIN_PERCENT = 0.5f;
+ /** Aspect ratio that the new PIP size spec logic optimizes for. */
+ private static final float OPTIMIZED_ASPECT_RATIO = 9f / 16;
+
+ /** A map of aspect ratios to be tested to expected sizes */
+ private static Map<Float, Size> sExpectedMaxSizes;
+ private static Map<Float, Size> sExpectedDefaultSizes;
+ private static Map<Float, Size> sExpectedMinSizes;
+ /** A static mockito session object to mock {@link SystemProperties} */
+ private static StaticMockitoSession sStaticMockitoSession;
+
+ @Mock private Context mContext;
+ @Mock private Resources mResources;
+
+ private PipSizeSpecHandler mPipSizeSpecHandler;
+
+ /**
+ * Sets up static Mockito session for SystemProperties and mocks necessary static methods.
+ */
+ private static void setUpStaticSystemPropertiesSession() {
+ sStaticMockitoSession = mockitoSession()
+ .mockStatic(SystemProperties.class).startMocking();
+ // make sure the feature flag is on
+ when(SystemProperties.getBoolean(anyString(), anyBoolean())).thenReturn(true);
+ when(SystemProperties.get(anyString(), anyString())).thenAnswer(invocation -> {
+ String property = invocation.getArgument(0);
+ if (property.equals("com.android.wm.shell.pip.phone.def_percentage")) {
+ return Float.toString(DEFAULT_PERCENT);
+ } else if (property.equals("com.android.wm.shell.pip.phone.min_percentage")) {
+ return Float.toString(MIN_PERCENT);
+ }
+
+ // throw an exception if illegal arguments are used for these tests
+ throw new InvalidUseOfMatchersException(
+ String.format("Argument %s does not match", property)
+ );
+ });
+ }
+
+ /**
+ * Initializes the map with the aspect ratios to be tested and corresponding expected max sizes.
+ */
+ private static void initExpectedSizes() {
+ sExpectedMaxSizes = new HashMap<>();
+ sExpectedDefaultSizes = new HashMap<>();
+ sExpectedMinSizes = new HashMap<>();
+
+ sExpectedMaxSizes.put(16f / 9, new Size(1000, 562));
+ sExpectedDefaultSizes.put(16f / 9, new Size(600, 337));
+ sExpectedMinSizes.put(16f / 9, new Size(499, 281));
+
+ sExpectedMaxSizes.put(4f / 3, new Size(892, 669));
+ sExpectedDefaultSizes.put(4f / 3, new Size(535, 401));
+ sExpectedMinSizes.put(4f / 3, new Size(445, 334));
+
+ sExpectedMaxSizes.put(3f / 4, new Size(669, 892));
+ sExpectedDefaultSizes.put(3f / 4, new Size(401, 535));
+ sExpectedMinSizes.put(3f / 4, new Size(334, 445));
+
+ sExpectedMaxSizes.put(9f / 16, new Size(562, 999));
+ sExpectedDefaultSizes.put(9f / 16, new Size(337, 599));
+ sExpectedMinSizes.put(9f / 16, new Size(281, 499));
+ }
+
+ private void forEveryTestCaseCheck(Map<Float, Size> expectedSizes,
+ Function<Float, Size> callback) {
+ for (Map.Entry<Float, Size> expectedSizesEntry : expectedSizes.entrySet()) {
+ float aspectRatio = expectedSizesEntry.getKey();
+ Size expectedSize = expectedSizesEntry.getValue();
+
+ Assert.assertEquals(expectedSize, callback.apply(aspectRatio));
+ }
+ }
+
+ @Before
+ public void setUp() {
+ initExpectedSizes();
+ setUpStaticSystemPropertiesSession();
+
+ when(mResources.getDimensionPixelSize(anyInt())).thenReturn(DEFAULT_MIN_EDGE_SIZE);
+ when(mResources.getFloat(anyInt())).thenReturn(OPTIMIZED_ASPECT_RATIO);
+ when(mResources.getString(anyInt())).thenReturn("0x0");
+ when(mResources.getDisplayMetrics())
+ .thenReturn(getContext().getResources().getDisplayMetrics());
+
+ // set up the mock context for spec handler specifically
+ when(mContext.getResources()).thenReturn(mResources);
+
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+
+ // no overridden min edge size by default
+ mPipSizeSpecHandler.setOverrideMinSize(null);
+
+ DisplayInfo displayInfo = new DisplayInfo();
+ displayInfo.logicalWidth = DISPLAY_EDGE_SIZE;
+ displayInfo.logicalHeight = DISPLAY_EDGE_SIZE;
+
+ // use the parent context (not the mocked one) to obtain the display layout
+ // this is done to avoid unnecessary mocking while allowing for custom display dimensions
+ DisplayLayout displayLayout = new DisplayLayout(displayInfo, getContext().getResources(),
+ false, false);
+ mPipSizeSpecHandler.setDisplayLayout(displayLayout);
+ }
+
+ @After
+ public void cleanUp() {
+ sStaticMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testGetMaxSize() {
+ forEveryTestCaseCheck(sExpectedMaxSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getMaxSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetDefaultSize() {
+ forEveryTestCaseCheck(sExpectedDefaultSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getDefaultSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetMinSize() {
+ forEveryTestCaseCheck(sExpectedMinSizes,
+ (aspectRatio) -> mPipSizeSpecHandler.getMinSize(aspectRatio));
+ }
+
+ @Test
+ public void testGetSizeForAspectRatio_noOverrideMinSize() {
+ // an initial size with 16:9 aspect ratio
+ Size initSize = new Size(600, 337);
+
+ Size expectedSize = new Size(337, 599);
+ Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+ Assert.assertEquals(expectedSize, actualSize);
+ }
+
+ @Test
+ public void testGetSizeForAspectRatio_withOverrideMinSize() {
+ // an initial size with a 1:1 aspect ratio
+ mPipSizeSpecHandler.setOverrideMinSize(new Size(OVERRIDE_MIN_EDGE_SIZE,
+ OVERRIDE_MIN_EDGE_SIZE));
+ // make sure initial size is same as override min size
+ Size initSize = mPipSizeSpecHandler.getOverrideMinSize();
+
+ Size expectedSize = new Size(40, 71);
+ Size actualSize = mPipSizeSpecHandler.getSizeForAspectRatio(initSize, 9f / 16);
+
+ Assert.assertEquals(expectedSize, actualSize);
+ }
+}
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 8ad2932b69e4..5c4863ff752f 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
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Size;
import androidx.test.filters.SmallTest;
@@ -90,6 +91,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
private PipSnapAlgorithm mPipSnapAlgorithm;
private PipMotionHelper mMotionHelper;
private PipResizeGestureHandler mPipResizeGestureHandler;
+ private PipSizeSpecHandler mPipSizeSpecHandler;
private DisplayLayout mDisplayLayout;
private Rect mInsetBounds;
@@ -103,16 +105,17 @@ public class PipTouchHandlerTest extends ShellTestCase {
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mPipBoundsState = new PipBoundsState(mContext);
+ mPipSizeSpecHandler = new PipSizeSpecHandler(mContext);
+ mPipBoundsState = new PipBoundsState(mContext, mPipSizeSpecHandler);
mPipSnapAlgorithm = new PipSnapAlgorithm();
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState, mPipSnapAlgorithm,
- new PipKeepClearAlgorithmInterface() {});
+ new PipKeepClearAlgorithmInterface() {}, mPipSizeSpecHandler);
PipMotionHelper pipMotionHelper = new PipMotionHelper(mContext, mPipBoundsState,
mPipTaskOrganizer, mPhonePipMenuController, mPipSnapAlgorithm,
mMockPipTransitionController, mFloatingContentCoordinator);
mPipTouchHandler = new PipTouchHandler(mContext, mShellInit, mPhonePipMenuController,
- mPipBoundsAlgorithm, mPipBoundsState, mPipTaskOrganizer, pipMotionHelper,
- mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
+ mPipBoundsAlgorithm, mPipBoundsState, mPipSizeSpecHandler, mPipTaskOrganizer,
+ pipMotionHelper, mFloatingContentCoordinator, mPipUiEventLogger, mMainExecutor);
// We aren't actually using ShellInit, so just call init directly
mPipTouchHandler.onInit();
mMotionHelper = Mockito.spy(mPipTouchHandler.getMotionHelper());
@@ -122,6 +125,7 @@ public class PipTouchHandlerTest extends ShellTestCase {
mDisplayLayout = new DisplayLayout(mContext, mContext.getDisplay());
mPipBoundsState.setDisplayLayout(mDisplayLayout);
+ mPipSizeSpecHandler.setDisplayLayout(mDisplayLayout);
mInsetBounds = new Rect(mPipBoundsState.getDisplayBounds().left + INSET,
mPipBoundsState.getDisplayBounds().top + INSET,
mPipBoundsState.getDisplayBounds().right - INSET,
@@ -154,13 +158,17 @@ public class PipTouchHandlerTest extends ShellTestCase {
mPipTouchHandler.onMovementBoundsChanged(mInsetBounds, mPipBounds, mCurBounds,
mFromImeAdjustment, mFromShelfAdjustment, mDisplayRotation);
+ // getting the expected min and max size
+ float aspectRatio = (float) mPipBounds.width() / mPipBounds.height();
+ Size expectedMinSize = mPipSizeSpecHandler.getMinSize(aspectRatio);
+ Size expectedMaxSize = mPipSizeSpecHandler.getMaxSize(aspectRatio);
+
assertEquals(expectedMovementBounds, mPipBoundsState.getNormalMovementBounds());
verify(mPipResizeGestureHandler, times(1))
- .updateMinSize(mPipBounds.width(), mPipBounds.height());
+ .updateMinSize(expectedMinSize.getWidth(), expectedMinSize.getHeight());
verify(mPipResizeGestureHandler, times(1))
- .updateMaxSize(shorterLength - 2 * mInsetBounds.left,
- shorterLength - 2 * mInsetBounds.left);
+ .updateMaxSize(expectedMaxSize.getWidth(), expectedMaxSize.getHeight());
}
@Test