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 Original line 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
        // Calculate the snap fraction of the current stack along the old movement bounds
        final Rect postChangeStackBounds = new Rect(oldBounds);
        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
        // Update the display layout
        mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
        mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
@@ -273,7 +274,8 @@ public class PipBoundsHandler {
        final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
        final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
                false /* adjustForIme */);
                false /* adjustForIme */);
        mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
        mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
                snapFraction);
                snapFraction, mPipBoundsState.getStashedState(), mPipBoundsState.getStashOffset(),
                mPipBoundsState.getDisplayBounds());


        getInsetBounds(outInsetBounds);
        getInsetBounds(outInsetBounds);
        outBounds.set(postChangeStackBounds);
        outBounds.set(postChangeStackBounds);
@@ -321,7 +323,7 @@ public class PipBoundsHandler {
            boolean useCurrentMinEdgeSize, boolean useCurrentSize) {
            boolean useCurrentMinEdgeSize, boolean useCurrentSize) {
        // Save the snap fraction and adjust the size based on the new aspect ratio.
        // Save the snap fraction and adjust the size based on the new aspect ratio.
        final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
        final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
                getMovementBounds(stackBounds));
                getMovementBounds(stackBounds), mPipBoundsState.getStashedState());
        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
        final Size size;
        final Size size;
        if (useCurrentMinEdgeSize || useCurrentSize) {
        if (useCurrentMinEdgeSize || useCurrentSize) {
+58 −6
Original line number Original line Diff line number Diff line
@@ -16,34 +16,70 @@


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


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


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


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


/**
/**
 * Singleton source of truth for the current state of PIP bounds.
 * Singleton source of truth for the current state of PIP bounds.
 */
 */
public final class PipBoundsState {
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 static final String TAG = PipBoundsState.class.getSimpleName();


    private final @NonNull Rect mBounds = new Rect();
    private final @NonNull Rect mBounds = new Rect();
    private final Context mContext;
    private float mAspectRatio;
    private float mAspectRatio;
    private boolean mIsStashed;
    private int mStashedState = STASH_TYPE_NONE;
    private int mStashOffset;
    private PipReentryState mPipReentryState;
    private PipReentryState mPipReentryState;
    private ComponentName mLastPipComponentName;
    private ComponentName mLastPipComponentName;
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final DisplayLayout mDisplayLayout = new DisplayLayout();
    private final DisplayLayout mDisplayLayout = new DisplayLayout();
    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();
    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.
     * 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) {
    public void setStashed(@StashType int stashedState) {
        mIsStashed = isStashed;
        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.
     * Whether PiP is stashed or not.
     */
     */
    public boolean isStashed() {
    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) {
    public void setAspectRatio(float aspectRatio) {
@@ -245,7 +296,8 @@ public final class PipBoundsState {
        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
        pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
        pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
        pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
        pw.println(innerPrefix + "mIsStashed=" + mIsStashed);
        pw.println(innerPrefix + "mStashedState=" + mStashedState);
        pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
        if (mPipReentryState == null) {
        if (mPipReentryState == null) {
            pw.println(innerPrefix + "mPipReentryState=null");
            pw.println(innerPrefix + "mPipReentryState=null");
        } else {
        } else {
+45 −6
Original line number Original line Diff line number Diff line
@@ -16,6 +16,10 @@


package com.android.wm.shell.pip;
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.Context;
import android.content.res.Resources;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.PointF;
@@ -41,10 +45,21 @@ public class PipSnapAlgorithm {
        mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
        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
     * @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 stackBounds} are. If the {@param stackBounds} are not currently on the
     *         {@param movementBounds} exactly, then they will be snapped to the movement bounds.
     *         {@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}:
     *         The fraction is defined in a clockwise fashion against the {@param movementBounds}:
     *
     *
@@ -54,9 +69,10 @@ public class PipSnapAlgorithm {
     *          3 +---+ 2
     *          3 +---+ 2
     *            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();
        final Rect tmpBounds = new Rect();
        snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds);
        snapRectToClosestEdge(stackBounds, movementBounds, tmpBounds, stashType);
        final float widthFraction = (float) (tmpBounds.left - movementBounds.left) /
        final float widthFraction = (float) (tmpBounds.left - movementBounds.left) /
                movementBounds.width();
                movementBounds.width();
        final float heightFraction = (float) (tmpBounds.top - movementBounds.top) /
        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
     * Adjusts {@param movementBoundsOut} so that it is the movement bounds for the given
     * {@param stackBounds}.
     * {@param stackBounds}.
@@ -178,17 +210,24 @@ public class PipSnapAlgorithm {
     * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
     * Snaps the {@param stackBounds} to the closest edge of the {@param movementBounds} and writes
     * the new bounds out to {@param boundsOut}.
     * 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,
        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,
        final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
                stackBounds.top));
                stackBounds.top));
        boundsOut.set(stackBounds);
        boundsOut.set(stackBounds);


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


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


package com.android.wm.shell.pip.phone;
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.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.ComponentName;
@@ -33,7 +36,6 @@ import androidx.dynamicanimation.animation.AnimationHandler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.dynamicanimation.animation.SpringForce;


import com.android.wm.shell.R;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.FloatProperties;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.animation.PhysicsAnimator;
import com.android.wm.shell.common.FloatingContentCoordinator;
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. */
    /** The region that all of PIP must stay within. */
    private final Rect mFloatingAllowedArea = new Rect();
    private final Rect mFloatingAllowedArea = new Rect();


    private int mStashOffset = 0;

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


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


        reloadResources();

        mResizePipUpdateListener = (target, values) -> {
        mResizePipUpdateListener = (target, values) -> {
            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
                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
    @NonNull
    @Override
    @Override
    public Rect getFloatingBoundsOnScreen() {
    public Rect getFloatingBoundsOnScreen() {
@@ -380,6 +373,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
     */
    void stashToEdge(
    void stashToEdge(
            float velocityX, float velocityY, @Nullable Runnable endAction) {
            float velocityX, float velocityY, @Nullable Runnable endAction) {
        mPipBoundsState.setStashed(velocityX < 0 ? STASH_TYPE_LEFT : STASH_TYPE_RIGHT);
        movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
        movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
    }
    }


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


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


        final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
        final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;
@@ -471,9 +467,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        if (savedSnapFraction < 0f) {
        if (savedSnapFraction < 0f) {
            // If there are no saved snap fractions, then just use the current bounds
            // If there are no saved snap fractions, then just use the current bounds
            savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(getBounds()),
            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) {
        if (immediate) {
            movePip(normalBounds);
            movePip(normalBounds);
@@ -512,8 +511,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mFlingConfigY = new PhysicsAnimator.FlingConfig(
        mFlingConfigY = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
                DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
        mStashConfigX = new PhysicsAnimator.FlingConfig(
        mStashConfigX = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, mStashOffset - mPipBoundsState.getBounds().width(),
                DEFAULT_FRICTION,
                mPipBoundsState.getDisplayBounds().right - mStashOffset);
                mPipBoundsState.getStashOffset() - mPipBoundsState.getBounds().width(),
                mPipBoundsState.getDisplayBounds().right - mPipBoundsState.getStashOffset());
    }
    }


    /**
    /**
Loading