Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2dd597ae authored by jorgegil@google.com's avatar jorgegil@google.com
Browse files

Fix resizability in apps using the override min size

Fixes an issue where activities using the manifest min
width/height was causing them to not be resizable.
This was caused by:
1. transformBoundsToMinimalSize is called on drag resize, which
causes the bounds to go back to the minimal size.
2. After fixing 1, drag resizes "jumps" from the minimal size to
the default bounds because the normal bounds are not set to the
minimal size
3. Reentry bounds are not restored correctly because when
calculating the destination bounds, the bounds where always
transformed to the minimal size at the end.

This CL changes a few things to fix these issues:
1. The minimal size is now considered when calculating the
entry bounds or transforming bounds to aspect ratio. This means
that instead of calculating the bounds/size using the "default"
size and then adjusting to the minimal size, we'll look at
whether we should calculate using the default or the minimal size
from the very beginning.
2. When the minimal size changes (onTaskAppeared, onTaskInfoChange,
swipeToHome), the normal bounds and movement bounds need to be
recalculated. The normal bounds are the default bounds, and the
default size changes if the minimal size is set or unset.
3. The min edge size used to be set by PipTouchHandler depending
on whether PIP is expanded to either expandedMinEdgeSize or 0. It
is now set to mDefaultMinEdgeSize if not expanded. This was not
causing issues before but it started to with these changes.

Also moved the minEdgeSize and overrideMinSize to PipBoundsState
as part of b/169373982

Fixes: 172598623
Bug: 169373982
Test: com.android.wm.shell.pip
Test: enter pip, expand, resize, reentry bounds,
minimal size in manifest, aspect ratio change all work
correctly.

Change-Id: I4baa46eb641323282fc23fd4499365c83ff38a7d
parent 31012130
Loading
Loading
Loading
Loading
+50 −60
Original line number Diff line number Diff line
@@ -58,8 +58,6 @@ public class PipBoundsHandler {
    private int mDefaultStackGravity;
    private int mDefaultMinSize;
    private Point mScreenEdgeInsets;
    private int mCurrentMinSize;
    private Size mOverrideMinimalSize;

    private boolean mIsImeShowing;
    private int mImeHeight;
@@ -74,6 +72,7 @@ public class PipBoundsHandler {
        // 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);
    }

    /**
@@ -87,7 +86,6 @@ public class PipBoundsHandler {
                com.android.internal.R.integer.config_defaultPictureInPictureGravity);
        mDefaultMinSize = res.getDimensionPixelSize(
                com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
        mCurrentMinSize = mDefaultMinSize;
        final String screenEdgeInsetsDpString = res.getString(
                com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
        final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
@@ -102,14 +100,6 @@ public class PipBoundsHandler {
                com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
    }

    /**
     * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds
     * @param minEdgeSize
     */
    public void setMinEdgeSize(int minEdgeSize) {
        mCurrentMinSize = minEdgeSize;
    }

    /**
     * Sets both shelf visibility and its height if applicable.
     * @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
@@ -167,16 +157,16 @@ public class PipBoundsHandler {
    }

    /**
     * See {@link #getDestinationBounds(Rect, Size, boolean)}
     * See {@link #getDestinationBounds(Rect, boolean)}
     */
    public Rect getDestinationBounds(Rect bounds, Size minimalSize) {
        return getDestinationBounds(bounds, minimalSize, false /* useCurrentMinEdgeSize */);
    public Rect getDestinationBounds(Rect bounds) {
        return getDestinationBounds(bounds, false /* useCurrentMinEdgeSize */);
    }

    /**
     * @return {@link Rect} of the destination PiP window bounds.
     */
    public Rect getDestinationBounds(Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) {
    public Rect getDestinationBounds(Rect bounds, boolean useCurrentMinEdgeSize) {
        boolean isReentryBounds = false;
        final Rect destinationBounds;
        if (bounds == null) {
@@ -191,7 +181,6 @@ public class PipBoundsHandler {
            } else {
                // Get actual default bounds.
                defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */);
                mOverrideMinimalSize = minimalSize;
            }

            destinationBounds = new Rect(defaultBounds);
@@ -306,67 +295,56 @@ public class PipBoundsHandler {
                && Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
    }

    /**
     * Sets the current bound with the currently store aspect ratio.
     * @param stackBounds
     */
    public void transformBoundsToAspectRatio(Rect stackBounds) {
        transformBoundsToAspectRatio(stackBounds, mPipBoundsState.getAspectRatio(),
                true /* useCurrentMinEdgeSize */, true /* useCurrentSize */);
    }

    /**
     * Set the current bounds (or the default bounds if there are no current bounds) with the
     * specified aspect ratio.
     */
    private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
    public void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
            boolean useCurrentMinEdgeSize, boolean useCurrentSize) {
        // Save the snap fraction and adjust the size based on the new aspect ratio.
        final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;

        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 = mSnapAlgorithm.getSizeForAspectRatio(
                    new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
        } 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 DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
            size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
                size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
                        displayInfo.logicalWidth, displayInfo.logicalHeight);
            }
        }

        final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
        final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
        stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
        // apply the override minimal size if applicable, this minimal size is specified by app
        if (mOverrideMinimalSize != null) {
            transformBoundsToMinimalSize(stackBounds, aspectRatio, mOverrideMinimalSize);
        }
        mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
    }

    /**
     * Transforms a given bounds to meet the minimal size constraints.
     * This function assumes the given {@param stackBounds} qualifies {@param aspectRatio}.
     */
    private void transformBoundsToMinimalSize(Rect stackBounds, float aspectRatio,
            Size minimalSize) {
        if (minimalSize == null) return;
        final Size adjustedMinimalSize;
        final float minimalSizeAspectRatio =
                minimalSize.getWidth() / (float) minimalSize.getHeight();
        if (minimalSizeAspectRatio > aspectRatio) {
            // minimal size is wider, fixed the width and increase the height
            adjustedMinimalSize = new Size(
                    minimalSize.getWidth(), (int) (minimalSize.getWidth() / aspectRatio));
    /** 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 {
            adjustedMinimalSize = new Size(
                    (int) (minimalSize.getHeight() * aspectRatio), minimalSize.getHeight());
            // Size is taller, fix the height and adjust the width.
            return new Size((int) (size.getHeight() * aspectRatio), size.getHeight());
        }
        final Rect containerBounds = new Rect(stackBounds);
        Gravity.apply(mDefaultStackGravity,
                adjustedMinimalSize.getWidth(), adjustedMinimalSize.getHeight(),
                containerBounds, stackBounds);
    }

    /**
@@ -383,12 +361,20 @@ public class PipBoundsHandler {
            final Rect insetBounds = new Rect();
            getInsetBounds(insetBounds);
            final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
            size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
            final Size defaultSize;
            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 = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
                        mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
            Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
                    0, Math.max(mIsImeShowing ? mImeHeight : 0,
                            mIsShelfShowing ? mShelfHeight : 0),
                    defaultBounds);
            }
            Gravity.apply(mDefaultStackGravity, defaultSize.getWidth(), defaultSize.getHeight(),
                    insetBounds, 0, Math.max(mIsImeShowing ? mImeHeight : 0,
                            mIsShelfShowing ? mShelfHeight : 0), defaultBounds);
        }
        return defaultBounds;
    }
@@ -443,6 +429,10 @@ public class PipBoundsHandler {
        mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
    }

    public int getDefaultMinSize() {
        return mDefaultMinSize;
    }

    /**
     * @return the pixels for a given dp value.
     */
+47 −0
Original line number Diff line number Diff line
@@ -61,8 +61,14 @@ public final class PipBoundsState {
    private ComponentName mLastPipComponentName;
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final DisplayLayout mDisplayLayout = new DisplayLayout();
    /** The current minimum edge size of PIP. */
    private int mMinEdgeSize;
    /** The preferred minimum (and default) size specified by apps. */
    private Size mOverrideMinSize;
    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();

    private Runnable mOnMinimalSizeChangeCallback;

    public PipBoundsState(Context context) {
        mContext = context;
        reloadResources();
@@ -203,10 +209,49 @@ public final 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(Size overrideMinSize) {
        final boolean changed = !Objects.equals(overrideMinSize, mOverrideMinSize);
        mOverrideMinSize = overrideMinSize;
        if (changed && mOnMinimalSizeChangeCallback != null) {
            mOnMinimalSizeChangeCallback.run();
        }
    }

    /** Returns the preferred minimal size specified by the activity in PIP. */
    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 AnimatingBoundsState getAnimatingBoundsState() {
        return mAnimatingBoundsState;
    }

    /**
     * Registers a callback when the minimal size of PIP that is set by the app changes.
     */
    public void setOnMinimalSizeChangeCallback(Runnable onMinimalSizeChangeCallback) {
        mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback;
    }

    /** Source of truth for the current animation bounds of PIP. */
    public static class AnimatingBoundsState {
        /** The bounds used when PIP is being dragged or animated. */
@@ -298,6 +343,8 @@ public final class PipBoundsState {
        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);
        if (mPipReentryState == null) {
            pw.println(innerPrefix + "mPipReentryState=null");
        } else {
+16 −13
Original line number Diff line number Diff line
@@ -339,10 +339,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            PictureInPictureParams pictureInPictureParams) {
        mShouldIgnoreEnteringPipTransition = true;
        sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
        mPipBoundsState.setLastPipComponentName(componentName);
        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(pictureInPictureParams));
        return mPipBoundsHandler.getDestinationBounds(null /* bounds */,
                getMinimalSize(activityInfo));
        setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
        return mPipBoundsHandler.getDestinationBounds(null /* bounds */);
    }

    /**
@@ -356,6 +354,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        }
    }

    private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
            ActivityInfo activityInfo) {
        mPipBoundsState.setLastPipComponentName(componentName);
        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(params));
        mPipBoundsState.setOverrideMinSize(getMinimalSize(activityInfo));
    }

    /**
     * Expands PiP to the previous bounds, this is done in two phases using
     * {@link WindowContainerTransaction}
@@ -483,7 +488,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        mLeash = leash;
        mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
        mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
        mPipBoundsState.setLastPipComponentName(mTaskInfo.topActivity);
        setBoundsStateForEntry(mTaskInfo.topActivity, mPictureInPictureParams,
                mTaskInfo.topActivityInfo);

        mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo);
        mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
@@ -520,9 +526,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            return;
        }

        mPipBoundsState.setAspectRatio(getAspectRatioOrDefault(mPictureInPictureParams));
        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
                getMinimalSize(mTaskInfo.topActivityInfo));
        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */);
        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
        final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();

@@ -675,6 +679,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
        Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
        mPipBoundsState.setLastPipComponentName(info.topActivity);
        mPipBoundsState.setOverrideMinSize(getMinimalSize(info.topActivityInfo));
        final PictureInPictureParams newParams = info.pictureInPictureParams;
        if (newParams == null || !applyPictureInPictureParams(newParams)) {
            Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
@@ -682,8 +687,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
        }
        // Aspect ratio changed, re-calculate destination bounds.
        final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo),
                true /* userCurrentMinEdgeSize */);
                mPipBoundsState.getBounds(), true /* useCurrentMinEdgeSize */);
        Objects.requireNonNull(destinationBounds, "Missing destination bounds");
        scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
                null /* updateBoundsCallback */);
@@ -698,7 +702,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
    public void onFixedRotationFinished(int displayId) {
        if (mShouldDeferEnteringPip && mState.isInPip()) {
            final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
                    null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
                    null /* bounds */);
            // schedule a regular animation to ensure all the callbacks are still being sent
            enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
        }
@@ -773,8 +777,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
            return;
        }

        final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */,
                getMinimalSize(mTaskInfo.topActivityInfo));
        final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(null /* bounds */);
        if (newDestinationBounds.equals(currentDestinationBounds)) return;
        if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
            animator.updateEndValue(newDestinationBounds);
+8 −1
Original line number Diff line number Diff line
@@ -242,7 +242,14 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
            displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo);
            mPipBoundsState.setDisplayInfo(newDisplayInfo);
            updateMovementBounds(null /* toBounds */, false /* fromRotation */,
                    false /* fromImeAdjustment */, false /* fromShelfAdustment */,
                    false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
                    null /* wct */);
        });
        mPipBoundsState.setOnMinimalSizeChangeCallback(
                () -> {
                    // The minimal size drives the normal bounds, so they need to be recalculated.
                    updateMovementBounds(null /* toBounds */, false /* fromRotation */,
                            false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
                            null /* wct */);
                });
        mMediaController = pipMediaController;
+3 −1
Original line number Diff line number Diff line
@@ -451,7 +451,9 @@ public class PipResizeGestureHandler {
                                mDownPoint.x, mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x,
                                mMinSize.y, mMaxSize, true,
                                mLastDownBounds.width() > mLastDownBounds.height()));
                        mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds);
                        mPipBoundsHandler.transformBoundsToAspectRatio(mLastResizeBounds,
                                mPipBoundsState.getAspectRatio(), false /* useCurrentMinEdgeSize */,
                                true /* useCurrentSize */);
                        mPipTaskOrganizer.scheduleUserResizePip(mLastDownBounds, mLastResizeBounds,
                                null);
                    }
Loading