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

Commit e2409781 authored by Ben Lin's avatar Ben Lin
Browse files

PiP: Consider stash state when calculating snap fraction.

If the PiP is stashed and something causes it to move (such as, screen
rotation or unexpand animations after timeout), we calculate its current
snap fraction, get the new bounds and movement bounds, and re-apply the
snap fraction. However during the snap fraction calculation, it assumes
that PiP bounds is within the movement bounds, which is not true when
PiP is stashed. So take this into consideration when doing the math.

This also changes the stashed status to reflect more than just yes/no -
but rather NONE/LEFT/RIGHT.

Bug: 165793553
Test: Stash PIP while menu is showing, and wait for timeout
Test: Stash PIP, rotate the device, see that snap fraction is persisted
Change-Id: Ib4eda44b7f280362b52609e4092dd4231ac8f04c
parent 41627475
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -256,7 +256,8 @@ public class PipBoundsHandler {

        // Calculate the snap fraction of the current stack along the old movement bounds
        final Rect postChangeStackBounds = new Rect(oldBounds);
        final float snapFraction = getSnapFraction(postChangeStackBounds);
        final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
                getMovementBounds(postChangeStackBounds), mPipBoundsState.getStashedState());

        // Update the display layout
        mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
@@ -273,7 +274,8 @@ public class PipBoundsHandler {
        final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
                false /* adjustForIme */);
        mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                snapFraction);
                snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
                mPipBoundsState.getDisplayBounds());

        getInsetBounds(outInsetBounds);
        outBounds.set(postChangeStackBounds);
@@ -321,7 +323,7 @@ public class PipBoundsHandler {
            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));
                getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
        final Size size;
        if (useCurrentMinEdgeSize || useCurrentSize) {
+58 −6
Original line number Diff line number Diff line
@@ -16,34 +16,70 @@

package com.android.wm.shell.pip;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Rect;
import android.util.Size;
import android.view.DisplayInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayLayout;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;

/**
 * Singleton source of truth for the current state of PIP bounds.
 */
public final class PipBoundsState {
    public static final int STASH_TYPE_NONE = 0;
    public static final int STASH_TYPE_LEFT = 1;
    public static final int STASH_TYPE_RIGHT = 2;

    @IntDef(prefix = { "STASH_TYPE_" }, value =  {
            STASH_TYPE_NONE,
            STASH_TYPE_LEFT,
            STASH_TYPE_RIGHT
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface StashType {}

    private static final String TAG = PipBoundsState.class.getSimpleName();

    private final @NonNull Rect mBounds = new Rect();
    private final Context mContext;
    private float mAspectRatio;
    private boolean mIsStashed;
    private int mStashedState = STASH_TYPE_NONE;
    private int mStashOffset;
    private PipReentryState mPipReentryState;
    private ComponentName mLastPipComponentName;
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final DisplayLayout mDisplayLayout = new DisplayLayout();
    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();

    public PipBoundsState(Context context) {
        mContext = context;
        reloadResources();
    }

    /**
     * Reloads the resources.
     */
    public void onConfigurationChanged() {
        reloadResources();
    }

    private void reloadResources() {
        mStashOffset = mContext.getResources()
                .getDimensionPixelSize(R.dimen.pip_stash_offset);
    }

    /**
     * Set the current PIP bounds.
     */
@@ -57,17 +93,32 @@ public final class PipBoundsState {
    }

    /**
     * Dictate where PiP currently should be stashed or not.
     * Dictate where PiP currently should be stashed, if at all.
     */
    public void setStashed(boolean isStashed) {
        mIsStashed = isStashed;
    public void setStashed(@StashType int stashedState) {
        mStashedState = stashedState;
    }

    /**
     * Return where the PiP is stashed, if at all.
     * @return {@code STASH_NONE}, {@code STASH_LEFT} or {@code STASH_RIGHT}.
     */
    public @StashType int getStashedState() {
        return mStashedState;
    }

    /**
     * Whether PiP is stashed or not.
     */
    public boolean isStashed() {
        return mIsStashed;
        return mStashedState != STASH_TYPE_NONE;
    }

    /**
     * Returns the offset from the edge of the screen for PiP stash.
     */
    public int getStashOffset() {
        return mStashOffset;
    }

    public void setAspectRatio(float aspectRatio) {
@@ -245,7 +296,8 @@ public final class PipBoundsState {
        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
        pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
        pw.println(innerPrefix + "mIsStashed=" + mIsStashed);
        pw.println(innerPrefix + "mStashedState=" + mStashedState);
        pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
        if (mPipReentryState == null) {
            pw.println(innerPrefix + "mPipReentryState=null");
        } else {
+45 −6
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.wm.shell.pip;

import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_NONE;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.PointF;
@@ -41,10 +45,21 @@ public class PipSnapAlgorithm {
        mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
    }

    /**
     * Returns a fraction that describes where the PiP bounds is.
     * See {@link #getSnapFraction(Rect, Rect, int)}.
     */
    public float getSnapFraction(Rect stackBounds, Rect movementBounds) {
        return getSnapFraction(stackBounds, movementBounds, STASH_TYPE_NONE);
    }

    /**
     * @return returns a fraction that describes where along the {@param movementBounds} the
     *         {@param stackBounds} are. If the {@param stackBounds} are not currently on the
     *         {@param movementBounds} exactly, then they will be snapped to the movement bounds.
     *         stashType dictates whether the PiP is stashed (off-screen) or not. If
     *         that's the case, we will have to do some math to calculate the snap fraction
     *         correctly.
     *
     *         The fraction is defined in a clockwise fashion against the {@param movementBounds}:
     *
@@ -54,9 +69,10 @@ public class PipSnapAlgorithm {
     *          3 +---+ 2
     *            3   2
     */
    public float getSnapFraction(Rect stackBounds, Rect movementBounds) {
    public float getSnapFraction(Rect stackBounds, Rect movementBounds,
            @PipBoundsState.StashType int stashType) {
        final Rect tmpBounds = new Rect();
        snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds);
        snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds, stashType);
        final float widthFraction = (float) (tmpBounds.left - movementBounds.left) /
                movementBounds.width();
        final float heightFraction = (float) (tmpBounds.top - movementBounds.top) /
@@ -103,6 +119,22 @@ public class PipSnapAlgorithm {
        }
    }

    /**
     * Same as {@link #applySnapFraction(Rect, Rect, float)}, but take stash state into
     * consideration.
     */
    public void applySnapFraction(Rect stackBounds, Rect movementBounds, float snapFraction,
            @PipBoundsState.StashType int stashType, int stashOffset, Rect displayBounds) {
        applySnapFraction(stackBounds, movementBounds, snapFraction);

        if (stashType != STASH_TYPE_NONE) {
            stackBounds.offsetTo(stashType == STASH_TYPE_LEFT
                            ? stashOffset - stackBounds.width()
                            : displayBounds.right - stashOffset,
                    stackBounds.top);
        }
    }

    /**
     * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
     * {@param stackBounds}.
@@ -178,17 +210,24 @@ public class PipSnapAlgorithm {
     * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
     * the new bounds out to {@param boundsOut}.
     */
    public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut) {
    public void snapRectToClosestEdge(Rect stackBounds, Rect movementBounds, Rect boundsOut,
            @PipBoundsState.StashType int stashType) {
        int leftEdge = stackBounds.left;
        if (stashType == STASH_TYPE_LEFT) {
            leftEdge = movementBounds.left;
        } else if (stashType == STASH_TYPE_RIGHT) {
            leftEdge = movementBounds.right;
        }
        final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right,
                stackBounds.left));
                leftEdge));
        final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
                stackBounds.top));
        boundsOut.set(stackBounds);

        // Otherwise, just find the closest edge
        final int fromLeft = Math.abs(stackBounds.left - movementBounds.left);
        final int fromLeft = Math.abs(leftEdge - movementBounds.left);
        final int fromTop = Math.abs(stackBounds.top - movementBounds.top);
        final int fromRight = Math.abs(movementBounds.right - stackBounds.left);
        final int fromRight = Math.abs(movementBounds.right - leftEdge);
        final int fromBottom = Math.abs(movementBounds.bottom - stackBounds.top);
        final int shortest = Math.min(Math.min(fromLeft, fromRight), Math.min(fromTop, fromBottom));
        if (shortest == fromLeft) {
+1 −0
Original line number Diff line number Diff line
@@ -196,6 +196,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac
            mMainExecutor.execute(() -> {
                mPipBoundsHandler.onConfigurationChanged(mContext);
                mTouchHandler.onConfigurationChanged();
                mPipBoundsState.onConfigurationChanged();
            });
        }

+16 −16
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.wm.shell.pip.phone;

import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_LEFT;
import static com.android.wm.shell.pip.PipBoundsState.STASH_TYPE_RIGHT;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -33,7 +36,6 @@ import androidx.dynamicanimation.animation.AnimationHandler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import androidx.dynamicanimation.animation.SpringForce;

import com.android.wm.shell.R;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -79,8 +81,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    /** The region that all of PIP must stay within. */
    private final Rect mFloatingAllowedArea = new Rect();

    private int mStashOffset = 0;

    /** Coordinator instance for resolving conflicts with other floating content. */
    private FloatingContentCoordinator mFloatingContentCoordinator;

@@ -179,8 +179,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
                mSfAnimationHandlerThreadLocal.get());

        reloadResources();

        mResizePipUpdateListener = (target, values) -> {
            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
@@ -189,11 +187,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        };
    }

    void reloadResources() {
        mStashOffset = mContext.getResources()
                .getDimensionPixelSize(R.dimen.pip_stash_offset);
    }

    @NonNull
    @Override
    public Rect getFloatingBoundsOnScreen() {
@@ -380,6 +373,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
    void stashToEdge(
            float velocityX, float velocityY, @Nullable Runnable endAction) {
        mPipBoundsState.setStashed(velocityX < 0 ? STASH_TYPE_LEFT : STASH_TYPE_RIGHT);
        movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
    }

@@ -399,9 +393,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                        FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
                .withEndActions(endAction);

        final float leftEdge = isStash ? mStashOffset - mPipBoundsState.getBounds().width()
        final float leftEdge = isStash
                ? mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width()
                : mMovementBounds.left;
        final float rightEdge = isStash ?  mPipBoundsState.getDisplayBounds().right - mStashOffset
        final float rightEdge = isStash
                ?  mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset()
                : mMovementBounds.right;

        final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
@@ -471,9 +467,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        if (savedSnapFraction < 0f) {
            // If there are no saved snap fractions, then just use the current bounds
            savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()),
                    currentMovementBounds);
                    currentMovementBounds, mPipBoundsState.getStashedState());
        }
        mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);

        mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction,
                mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
                mPipBoundsState.getDisplayBounds());

        if (immediate) {
            movePip(normalBounds);
@@ -512,8 +511,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mFlingConfigY = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
        mStashConfigX = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, mStashOffset - mPipBoundsState.getBounds().width(),
                mPipBoundsState.getDisplayBounds().right - mStashOffset);
                DEFAULT_FRICTION,
                mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width(),
                mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset());
    }

    /**
Loading