Update the pip size spec logic am: aacf021023
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20654971 Change-Id: I7c0a4dd46aa4c3705d7be3b268344568580d8fb4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -379,13 +382,20 @@ public abstract class WMShellModule {
|
||||
return new PhonePipKeepClearAlgorithm(context);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -69,6 +72,13 @@ public class PipUtils {
|
||||
return new Pair<>(null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user