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

Commit 9ce747ac authored by jorgegil@google.com's avatar jorgegil@google.com
Browse files

Move animation bounds into PipBoundsState

Moves mTemporaryBounds and mAnimatingToBounds into
a new state class for animations.

Bug: 169373982
Test: com.android.wm.shell.pip
Test: drag, move, resize, fling, dismiss working fine
Change-Id: Ie487f0cad2a1b2c2e5efc4d0404c8812b2f6c8ff
parent a2a25399
Loading
Loading
Loading
Loading
+56 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ public final class PipBoundsState {
    private ComponentName mLastPipComponentName;
    private final DisplayInfo mDisplayInfo = new DisplayInfo();
    private final DisplayLayout mDisplayLayout = new DisplayLayout();
    private final @NonNull AnimatingBoundsState mAnimatingBoundsState = new AnimatingBoundsState();

    /**
     * Set the current PIP bounds.
@@ -151,6 +152,60 @@ public final class PipBoundsState {
        mPipReentryState = null;
    }

    public AnimatingBoundsState getAnimatingBoundsState() {
        return mAnimatingBoundsState;
    }

    /** Source of truth for the current animation bounds of PIP. */
    public static class AnimatingBoundsState {
        /** The bounds used when PIP is being dragged or animated. */
        private final Rect mTemporaryBounds = new Rect();
        /** The destination bounds to which PIP is animating. */
        private final Rect mAnimatingToBounds = new Rect();

        /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */
        public boolean isAnimating() {
            return !mTemporaryBounds.isEmpty();
        }

        /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */
        public void setTemporaryBounds(Rect bounds) {
            mTemporaryBounds.set(bounds);
        }

        /** Set the bounds to which PIP is animating. */
        public void setAnimatingToBounds(Rect bounds) {
            mAnimatingToBounds.set(bounds);
        }

        /** Called when all ongoing dragging and animation operations have ended. */
        public void onAllAnimationsEnded() {
            mTemporaryBounds.setEmpty();
        }

        /** Called when an ongoing physics animation has ended. */
        public void onPhysicsAnimationEnded() {
            mAnimatingToBounds.setEmpty();
        }

        /** Returns the temporary animation bounds. */
        public Rect getTemporaryBounds() {
            return mTemporaryBounds;
        }

        /** Returns the destination bounds to which PIP is currently animating. */
        public Rect getAnimatingToBounds() {
            return mAnimatingToBounds;
        }

        void dump(PrintWriter pw, String prefix) {
            final String innerPrefix = prefix + "  ";
            pw.println(prefix + AnimatingBoundsState.class.getSimpleName());
            pw.println(innerPrefix + "mTemporaryBounds=" + mTemporaryBounds);
            pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds);
        }
    }

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

@@ -195,5 +250,6 @@ public final class PipBoundsState {
        } else {
            mPipReentryState.dump(pw, innerPrefix);
        }
        mAnimatingBoundsState.dump(pw, innerPrefix);
    }
}
+45 −72
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ import com.android.wm.shell.pip.PipBoundsState;
import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;

import java.io.PrintWriter;
import java.util.function.Consumer;

import kotlin.Unit;
@@ -80,20 +79,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    /** The region that all of PIP must stay within. */
    private final Rect mFloatingAllowedArea = new Rect();

    /**
     * Temporary bounds used when PIP is being dragged or animated. These bounds are applied to PIP
     * using {@link PipTaskOrganizer#scheduleUserResizePip}, so that we can animate shrinking into
     * and expanding out of the magnetic dismiss target.
     *
     * Once PIP is done being dragged or animated, we set {@link #mBounds} equal to these temporary
     * bounds, and call {@link PipTaskOrganizer#scheduleFinishResizePip} to 'officially' move PIP to
     * its new bounds.
     */
    private final Rect mTemporaryBounds = new Rect();

    /** The destination bounds to which PIP is animating. */
    private final Rect mAnimatingToBounds = new Rect();

    private int mStashOffset = 0;

    /** Coordinator instance for resolving conflicts with other floating content. */
@@ -108,15 +93,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
            });

    /**
     * PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations.
     * PhysicsAnimator instance for animating {@link PipBoundsState#getAnimatingBoundsState()}
     * using physics animations.
     */
    private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
            mTemporaryBounds);
    private final PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator;

    private MagnetizedObject<Rect> mMagnetizedPip;

    /**
     * Update listener that resizes the PIP to {@link #mTemporaryBounds}.
     * Update listener that resizes the PIP to {@link PipBoundsState#getAnimatingBoundsState()}.
     */
    private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;

@@ -189,14 +174,17 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mSnapAlgorithm = snapAlgorithm;
        mFloatingContentCoordinator = floatingContentCoordinator;
        mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
        mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
                mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
        mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler(
                mSfAnimationHandlerThreadLocal.get());

        reloadResources();

        mResizePipUpdateListener = (target, values) -> {
            if (!mTemporaryBounds.isEmpty()) {
                mPipTaskOrganizer.scheduleUserResizePip(
                        getBounds(), mTemporaryBounds, null);
            if (mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(),
                        mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(), null);
            }
        };
    }
@@ -209,7 +197,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    @NonNull
    @Override
    public Rect getFloatingBoundsOnScreen() {
        return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : getBounds();
        return !mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds().isEmpty()
                ? mPipBoundsState.getAnimatingBoundsState().getAnimatingToBounds() : getBounds();
    }

    @NonNull
@@ -227,18 +216,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     * Synchronizes the current bounds with the pinned stack, cancelling any ongoing animations.
     */
    void synchronizePinnedStackBounds() {
        cancelAnimations();
        mTemporaryBounds.setEmpty();
        cancelPhysicsAnimation();
        mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();

        if (mPipTaskOrganizer.isInPip()) {
            mFloatingContentCoordinator.onContentMoved(this);
        }
    }

    boolean isAnimating() {
        return mTemporaryBoundsPhysicsAnimator.isRunning();
    }

    /**
     * Tries to move the pinned stack to the given {@param bounds}.
     */
@@ -261,14 +246,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        if (!mSpringingToTouch) {
            // If we are moving PIP directly to the touch event locations, cancel any animations and
            // move PIP to the given bounds.
            cancelAnimations();
            cancelPhysicsAnimation();

            if (!isDragging) {
                resizePipUnchecked(toBounds);
                mPipBoundsState.setBounds(toBounds);
            } else {
                mTemporaryBounds.set(toBounds);
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mTemporaryBounds,
                mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(toBounds);
                mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds,
                        (Rect newBounds) -> {
                            mMainHandler.post(() -> {
                                mMenuController.updateMenuLayout(newBounds);
@@ -303,9 +288,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        final float destinationY = targetCenter.y - (desiredHeight / 2f);

        // If we're already in the dismiss target area, then there won't be a move to set the
        // temporary bounds, so just initialize it to the current bounds
        if (mTemporaryBounds.isEmpty()) {
            mTemporaryBounds.set(getBounds());
        // temporary bounds, so just initialize it to the current bounds.
        if (!mPipBoundsState.getAnimatingBoundsState().isAnimating()) {
            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
        }
        mTemporaryBoundsPhysicsAnimator
                .spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
@@ -339,7 +324,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
            Log.d(TAG, "exitPip: skipAnimation=" + skipAnimation
                    + " callers=\n" + Debug.getCallers(5, "    "));
        }
        cancelAnimations();
        cancelPhysicsAnimation();
        mMenuController.hideMenuWithoutResize();
        mPipTaskOrganizer.getUpdateHandler().post(() -> {
            mPipTaskOrganizer.exitPip(skipAnimation
@@ -356,7 +341,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        if (DEBUG) {
            Log.d(TAG, "removePip: callers=\n" + Debug.getCallers(5, "    "));
        }
        cancelAnimations();
        cancelPhysicsAnimation();
        mMenuController.hideMenuWithoutResize();
        mPipTaskOrganizer.removePip();
    }
@@ -382,14 +367,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        return mPipBoundsState.getBounds();
    }

    /**
     * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
     * otherwise.
     */
    Rect getPossiblyAnimatingBounds() {
        return mTemporaryBounds.isEmpty() ? getBounds() : mTemporaryBounds;
    }

    /**
     * Flings the PiP to the closest snap target.
     */
@@ -428,9 +405,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
                : mMovementBounds.right;

        final float xEndValue = velocityX < 0 ? leftEdge : rightEdge;

        final int startValueY = mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds().top;
        final float estimatedFlingYEndValue =
                PhysicsAnimator.estimateFlingEndValue(
                        mTemporaryBounds.top, velocityY, mFlingConfigY);
                PhysicsAnimator.estimateFlingEndValue(startValueY, velocityY, mFlingConfigY);

        startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */,
                false /* dismiss */);
@@ -443,7 +421,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
        if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
            // Animate from the current bounds if we're not already animating.
            mTemporaryBounds.set(getBounds());
            mPipBoundsState.getAnimatingBoundsState().setTemporaryBounds(getBounds());
        }

        mTemporaryBoundsPhysicsAnimator
@@ -513,7 +491,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
            Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
                    + " callers=\n" + Debug.getCallers(5, "    "));
        }
        cancelAnimations();
        cancelPhysicsAnimation();
        mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
                mUpdateBoundsCallback);
    }
@@ -521,9 +499,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    /**
     * Cancels all existing animations.
     */
    private void cancelAnimations() {
    private void cancelPhysicsAnimation() {
        mTemporaryBoundsPhysicsAnimator.cancel();
        mAnimatingToBounds.setEmpty();
        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
    }

@@ -547,22 +525,19 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
     */
    private void startBoundsAnimator(float toX, float toY, boolean dismiss) {
        if (!mSpringingToTouch) {
            cancelAnimations();
            cancelPhysicsAnimation();
        }

        // Set animatingToBounds directly to avoid allocating a new Rect, but then call
        // setAnimatingToBounds to run the normal logic for changing animatingToBounds.
        mAnimatingToBounds.set(
        setAnimatingToBounds(new Rect(
                (int) toX,
                (int) toY,
                (int) toX + getBounds().width(),
                (int) toY + getBounds().height());
        setAnimatingToBounds(mAnimatingToBounds);
                (int) toY + getBounds().height()));

        if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
            mTemporaryBoundsPhysicsAnimator
                    .addUpdateListener(mResizePipUpdateListener)
                    .withEndActions(this::onBoundsAnimationEnd);
                    .withEndActions(this::onBoundsPhysicsAnimationEnd);
        }

        mTemporaryBoundsPhysicsAnimator.start();
@@ -576,31 +551,34 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
        mDismissalPending = true;
    }

    private void onBoundsAnimationEnd() {
    private void onBoundsPhysicsAnimationEnd() {
        // The physics animation ended, though we may not necessarily be done animating, such as
        // when we're still dragging after moving out of the magnetic target.
        if (!mDismissalPending
                && !mSpringingToTouch
                && !mMagnetizedPip.getObjectStuckToTarget()) {
            mPipBoundsState.setBounds(mTemporaryBounds);
            // All animations (including dragging) have actually finished.
            mPipBoundsState.setBounds(
                    mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds());
            mPipBoundsState.getAnimatingBoundsState().onAllAnimationsEnded();
            if (!mDismissalPending) {
                // do not schedule resize if PiP is dismissing, which may cause app re-open to
                // mBounds instead of it's normal bounds.
                mPipTaskOrganizer.scheduleFinishResizePip(getBounds());
            }
            mTemporaryBounds.setEmpty();
        }

        mAnimatingToBounds.setEmpty();
        mPipBoundsState.getAnimatingBoundsState().onPhysicsAnimationEnded();
        mSpringingToTouch = false;
        mDismissalPending = false;
    }

    /**
     * Notifies the floating coordinator that we're moving, and sets {@link #mAnimatingToBounds} so
     * Notifies the floating coordinator that we're moving, and sets the animating to bounds so
     * we return these bounds from
     * {@link FloatingContentCoordinator.FloatingContent#getFloatingBoundsOnScreen()}.
     */
    private void setAnimatingToBounds(Rect bounds) {
        mAnimatingToBounds.set(bounds);
        mPipBoundsState.getAnimatingBoundsState().setAnimatingToBounds(bounds);
        mFloatingContentCoordinator.onContentMoved(this);
    }

@@ -639,7 +617,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
    MagnetizedObject<Rect> getMagnetizedPip() {
        if (mMagnetizedPip == null) {
            mMagnetizedPip = new MagnetizedObject<Rect>(
                    mContext, mTemporaryBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
                    mContext, mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds(),
                    FloatProperties.RECT_X, FloatProperties.RECT_Y) {
                @Override
                public float getWidth(@NonNull Rect animatedPipBounds) {
                    return animatedPipBounds.width();
@@ -661,10 +640,4 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,

        return mMagnetizedPip;
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
        pw.println(innerPrefix + "mBounds=" + getBounds());
    }
}
+13 −4
Original line number Diff line number Diff line
@@ -710,7 +710,7 @@ public class PipTouchHandler {
                return;
            }

            Rect bounds = mMotionHelper.getPossiblyAnimatingBounds();
            Rect bounds = getPossiblyAnimatingBounds();
            mDelta.set(0f, 0f);
            mStartPosition.set(bounds.left, bounds.top);
            mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
@@ -747,7 +747,7 @@ public class PipTouchHandler {
                mDelta.x += left - lastX;
                mDelta.y += top - lastY;

                mTmpBounds.set(mMotionHelper.getPossiblyAnimatingBounds());
                mTmpBounds.set(getPossiblyAnimatingBounds());
                mTmpBounds.offsetTo((int) left, (int) top);
                mMotionHelper.movePip(mTmpBounds, true /* isDragging */);

@@ -783,7 +783,7 @@ public class PipTouchHandler {

                // Reset the touch state on up before the fling settles
                mTouchState.reset();
                final Rect animatingBounds = mMotionHelper.getPossiblyAnimatingBounds();
                final Rect animatingBounds = getPossiblyAnimatingBounds();
                // If User releases the PIP window while it's out of the display bounds, put
                // PIP into stashed mode.
                if (mEnableStash
@@ -872,6 +872,16 @@ public class PipTouchHandler {
                || mExpandedBounds.height() != mNormalBounds.height();
    }

    /**
     * Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
     * otherwise.
     */
    Rect getPossiblyAnimatingBounds() {
        return mPipBoundsState.getAnimatingBoundsState().isAnimating()
                ? mPipBoundsState.getAnimatingBoundsState().getTemporaryBounds()
                : mPipBoundsState.getBounds();
    }

    public void dump(PrintWriter pw, String prefix) {
        final String innerPrefix = prefix + "  ";
        pw.println(prefix + TAG);
@@ -889,7 +899,6 @@ public class PipTouchHandler {
        pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
        mPipBoundsHandler.dump(pw, innerPrefix);
        mTouchState.dump(pw, innerPrefix);
        mMotionHelper.dump(pw, innerPrefix);
        if (mPipResizeGestureHandler != null) {
            mPipResizeGestureHandler.dump(pw, innerPrefix);
        }