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

Commit c81ff3d4 authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Modifies PIP to use the FloatingContentCoordinator.

Test: atest SystemUITests
Bug: 138115889
Change-Id: I639852a498676230e2318e4ba78c5a4333f6df02
parent 7155bf10
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.content.res.Configuration;

import java.io.PrintWriter;


public interface BasePipManager {
    void showPictureInPictureMenu();
    default void expandPip() {}
+5 −2
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.wm.DisplayChangeController;
import com.android.systemui.wm.DisplayController;

@@ -229,7 +230,8 @@ public class PipManager implements BasePipManager {

    @Inject
    public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
            DisplayController displayController) {
            DisplayController displayController,
            FloatingContentCoordinator floatingContentCoordinator) {
        mContext = context;
        mActivityManager = ActivityManager.getService();
        mActivityTaskManager = ActivityTaskManager.getService();
@@ -247,7 +249,8 @@ public class PipManager implements BasePipManager {
        mMenuController = new PipMenuActivityController(context, mActivityManager, mMediaController,
                mInputConsumerController);
        mTouchHandler = new PipTouchHandler(context, mActivityManager, mActivityTaskManager,
                mMenuController, mInputConsumerController, mPipBoundsHandler);
                mMenuController, mInputConsumerController, mPipBoundsHandler,
                floatingContentCoordinator);
        mAppOpsListener = new PipAppOpsListener(context, mActivityManager,
                mTouchHandler.getMotionHelper());
        displayController.addDisplayChangingController(mRotationController);
+120 −33
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.pip.phone;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.StackInfo;
import android.app.IActivityManager;
@@ -41,6 +42,7 @@ import com.android.internal.os.SomeArgs;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.shared.system.WindowManagerWrapper;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.util.FloatingContentCoordinator;
import com.android.systemui.util.animation.FloatProperties;
import com.android.systemui.util.animation.PhysicsAnimator;

@@ -49,7 +51,8 @@ import java.io.PrintWriter;
/**
 * A helper to animate and manipulate the PiP.
 */
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback {
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback,
        FloatingContentCoordinator.FloatingContent {

    private static final String TAG = "PipMotionHelper";
    private static final boolean DEBUG = false;
@@ -85,6 +88,12 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    /** PIP's current bounds on the screen. */
    private final Rect mBounds = new Rect();

    /** The bounds within which PIP's top-left coordinate is allowed to move. */
    private Rect mMovementBounds = new Rect();

    /** The region that all of PIP must stay within. */
    private Rect mFloatingAllowedArea = new Rect();

    private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
            new SfVsyncFrameCallbackProvider();

@@ -93,6 +102,12 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
     */
    private final Rect mAnimatedBounds = new Rect();

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

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

    /**
     * PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
     */
@@ -119,9 +134,15 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
            new PhysicsAnimator.SpringConfig(
                    SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY);

    /** SpringConfig to use for springing PIP away from conflicting floating content. */
    private final PhysicsAnimator.SpringConfig mConflictResolutionSpringConfig =
                new PhysicsAnimator.SpringConfig(
                        SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);

    public PipMotionHelper(Context context, IActivityManager activityManager,
            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
            PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils) {
            PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
            FloatingContentCoordinator floatingContentCoordinator) {
        mContext = context;
        mHandler = new Handler(ForegroundThread.get().getLooper(), this);
        mActivityManager = activityManager;
@@ -129,9 +150,27 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        mMenuController = menuController;
        mSnapAlgorithm = snapAlgorithm;
        mFlingAnimationUtils = flingAnimationUtils;
        mFloatingContentCoordinator = floatingContentCoordinator;
        onConfigurationChanged();
    }

    @NonNull
    @Override
    public Rect getFloatingBoundsOnScreen() {
        return !mAnimatingToBounds.isEmpty() ? mAnimatingToBounds : mBounds;
    }

    @NonNull
    @Override
    public Rect getAllowedFloatingBoundsRegion() {
        return mFloatingAllowedArea;
    }

    @Override
    public void moveToBounds(@NonNull Rect bounds) {
        animateToBounds(bounds, mConflictResolutionSpringConfig);
    }

    /**
     * Updates whenever the configuration changes.
     */
@@ -157,9 +196,24 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    }

    /**
     * Tries to the move the pinned stack to the given {@param bounds}.
     * Tries to move the pinned stack to the given {@param bounds}.
     */
    void movePip(Rect toBounds) {
        movePip(toBounds, false /* isDragging */);
    }

    /**
     * Tries to move the pinned stack to the given {@param bounds}.
     *
     * @param isDragging Whether this movement is the result of a drag touch gesture. If so, we
     *                   won't notify the floating content coordinator of this move, since that will
     *                   happen when the gesture ends.
     */
    void movePip(Rect toBounds, boolean isDragging) {
        if (!isDragging) {
            mFloatingContentCoordinator.onContentMoved(this);
        }

        cancelAnimations();
        resizePipUnchecked(toBounds);
        mBounds.set(toBounds);
@@ -211,6 +265,18 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        });
    }

    /** Sets the movement bounds to use to constrain PIP position animations. */
    void setCurrentMovementBounds(Rect movementBounds) {
        mMovementBounds.set(movementBounds);
        rebuildFlingConfigs();

        // The movement bounds represent the area within which we can move PIP's top-left position.
        // The allowed area for all of PIP is those bounds plus PIP's width and height.
        mFloatingAllowedArea.set(mMovementBounds);
        mFloatingAllowedArea.right += mBounds.width();
        mFloatingAllowedArea.bottom += mBounds.height();
    }

    /**
     * @return the PiP bounds.
     */
@@ -221,11 +287,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    /**
     * @return the closest minimized PiP bounds.
     */
    Rect getClosestMinimizedBounds(Rect stackBounds, Rect movementBounds) {
    Rect getClosestMinimizedBounds(Rect stackBounds) {
        Point displaySize = new Point();
        mContext.getDisplay().getRealSize(displaySize);
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, stackBounds);
        mSnapAlgorithm.applyMinimizedOffset(toBounds, movementBounds, displaySize, mStableInsets);
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, stackBounds);
        mSnapAlgorithm.applyMinimizedOffset(toBounds, mMovementBounds, displaySize, mStableInsets);
        return toBounds;
    }

@@ -264,11 +330,10 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
    /**
     * Animates the PiP to the minimized state, slightly offscreen.
     */
    void animateToClosestMinimizedState(Rect movementBounds, @Nullable Runnable updateAction) {
        final Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);

        prepareForBoundsAnimation(movementBounds);
    void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
        final Rect toBounds = getClosestMinimizedBounds(mBounds);

        mAnimatedBounds.set(mBounds);
        mAnimatedBoundsPhysicsAnimator
                .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
                .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
@@ -285,10 +350,8 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
     * Flings the PiP to the closest snap target.
     */
    void flingToSnapTarget(
            float velocityX, float velocityY, Rect movementBounds, Runnable updateAction,
            @Nullable Runnable endAction) {
        prepareForBoundsAnimation(movementBounds);

            float velocityX, float velocityY, Runnable updateAction, @Nullable Runnable endAction) {
        mAnimatedBounds.set(mBounds);
        mAnimatedBoundsPhysicsAnimator
                .flingThenSpring(
                        FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
@@ -298,21 +361,39 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                .addUpdateListener((target, values) -> updateAction.run())
                .withEndActions(endAction);

        final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
        final float estimatedFlingYEndValue =
                PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);

        setAnimatingToBounds(new Rect(
                (int) xEndValue,
                (int) estimatedFlingYEndValue,
                (int) xEndValue + mBounds.width(),
                (int) estimatedFlingYEndValue + mBounds.height()));

        startBoundsAnimation();
    }

    /**
     * Animates the PiP to the closest snap target.
     */
    void animateToClosestSnapTarget(Rect movementBounds) {
        prepareForBoundsAnimation(movementBounds);
    void animateToClosestSnapTarget() {
        final Rect newBounds = mSnapAlgorithm.findClosestSnapBounds(mMovementBounds, mBounds);
        animateToBounds(newBounds, mSpringConfig);
    }

        final Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
    /**
     * Animates PIP to the provided bounds, using physics animations and the given spring
     * configuration
     */
    void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
        mAnimatedBounds.set(mBounds);
        mAnimatedBoundsPhysicsAnimator
                .spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
                .spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);

                .spring(FloatProperties.RECT_X, bounds.left, springConfig)
                .spring(FloatProperties.RECT_Y, bounds.top, springConfig);
        startBoundsAnimation();

        setAnimatingToBounds(bounds);
    }

    /**
@@ -323,9 +404,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
        final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond();
        final Point dismissEndPoint = getDismissEndPoint(mBounds, velocityX, velocityY, isFling);

        // Set the animated bounds to start at the current bounds. We don't need to rebuild the
        // fling configs here via prepareForBoundsAnimation, since animateDismiss isn't provided
        // with new movement bounds.
        mAnimatedBounds.set(mBounds);

        // Animate to the dismiss end point, and then dismiss PIP.
@@ -366,9 +444,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                    currentMovementBounds);
        }
        mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);

        if (minimized) {
            normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
            normalBounds = getClosestMinimizedBounds(normalBounds);
        }

        if (immediate) {
            movePip(normalBounds);
        } else {
@@ -400,19 +480,15 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
     */
    private void cancelAnimations() {
        mAnimatedBoundsPhysicsAnimator.cancel();
        mAnimatingToBounds.setEmpty();
    }

    /**
     * Set new fling configs whose min/max values respect the given movement bounds, and set the
     * animated bounds to PIP's current 'real' bounds.
     */
    private void prepareForBoundsAnimation(Rect movementBounds) {
    /** Set new fling configs whose min/max values respect the given movement bounds. */
    private void rebuildFlingConfigs() {
        mFlingConfigX = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, movementBounds.left, movementBounds.right);
                DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right);
        mFlingConfigY = new PhysicsAnimator.FlingConfig(
                DEFAULT_FRICTION, movementBounds.top, movementBounds.bottom);

        mAnimatedBounds.set(mBounds);
                DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom);
    }

    /**
@@ -431,6 +507,16 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
                .start();
    }

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

    /**
     * Directly resizes the PiP to the given {@param bounds}.
     */
@@ -459,6 +545,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
            args.arg1 = toBounds;
            args.argi1 = duration;
            mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
            setAnimatingToBounds(toBounds);
        }
    }

+18 −23
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.systemui.pip.PipBoundsHandler;
import com.android.systemui.pip.PipSnapAlgorithm;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.util.FloatingContentCoordinator;

import java.io.PrintWriter;

@@ -127,6 +128,7 @@ public class PipTouchHandler {
    // Touch state
    private final PipTouchState mTouchState;
    private final FlingAnimationUtils mFlingAnimationUtils;
    private final FloatingContentCoordinator mFloatingContentCoordinator;
    private final PipMotionHelper mMotionHelper;
    private PipTouchGesture mGesture;

@@ -152,7 +154,7 @@ public class PipTouchHandler {
        @Override
        public void onPipMinimize() {
            setMinimizedStateInternal(true);
            mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateAction */);
            mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
        }

        @Override
@@ -172,7 +174,8 @@ public class PipTouchHandler {
    public PipTouchHandler(Context context, IActivityManager activityManager,
            IActivityTaskManager activityTaskManager, PipMenuActivityController menuController,
            InputConsumerController inputConsumerController,
            PipBoundsHandler pipBoundsHandler) {
            PipBoundsHandler pipBoundsHandler,
            FloatingContentCoordinator floatingContentCoordinator) {

        // Initialize the Pip input consumer
        mContext = context;
@@ -188,7 +191,7 @@ public class PipTouchHandler {
                2.5f);
        mGesture = new DefaultPipTouchGesture();
        mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
                mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
                mMenuController, mSnapAlgorithm, mFlingAnimationUtils, floatingContentCoordinator);
        mPipResizeGestureHandler =
                new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
        mTouchState = new PipTouchState(mViewConfig, mHandler,
@@ -207,6 +210,7 @@ public class PipTouchHandler {
        inputConsumerController.setRegistrationListener(this::onRegistrationChanged);

        mPipBoundsHandler = pipBoundsHandler;
        mFloatingContentCoordinator = floatingContentCoordinator;
        mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
                this::onAccessibilityShowMenu, mHandler);
    }
@@ -228,15 +232,18 @@ public class PipTouchHandler {
    }

    public void onActivityPinned() {
        cleanUp();
        cleanUpDismissTarget();
        mShowPipMenuOnAnimationEnd = true;
        mPipResizeGestureHandler.onActivityPinned();
        mFloatingContentCoordinator.onContentAdded(mMotionHelper);
    }

    public void onActivityUnpinned(ComponentName topPipActivity) {
        if (topPipActivity == null) {
            // Clean up state after the last PiP activity is removed
            cleanUp();
            cleanUpDismissTarget();

            mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
        }
        mPipResizeGestureHandler.onActivityUnpinned();
    }
@@ -501,8 +508,7 @@ public class PipTouchHandler {
        if (fromController) {
            if (isMinimized) {
                // Move the PiP to the new bounds immediately if minimized
                mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds,
                        mMovementBounds));
                mMotionHelper.movePip(mMotionHelper.getClosestMinimizedBounds(mNormalBounds));
            }
        } else if (mPinnedStackController != null) {
            try {
@@ -654,7 +660,7 @@ public class PipTouchHandler {

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

                if (mEnableDimissDragToEdge) {
                    updateDismissFraction();
@@ -724,7 +730,6 @@ public class PipTouchHandler {
                        mMenuController.hideMenu();
                    } else {
                        mMotionHelper.animateToClosestMinimizedState(
                                mMovementBounds,
                                PipTouchHandler.this::updateDismissFraction /* updateAction */);
                    }
                    return true;
@@ -748,16 +753,15 @@ public class PipTouchHandler {
                }

                if (isFling) {
                    mMotionHelper.flingToSnapTarget(
                            vel.x, vel.y, mMovementBounds,
                    mMotionHelper.flingToSnapTarget(vel.x, vel.y,
                            PipTouchHandler.this::updateDismissFraction /* updateAction */,
                            endAction /* endAction */);
                } else {
                    mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
                    mMotionHelper.animateToClosestSnapTarget();
                }
            } else if (mIsMinimized) {
                // This was a tap, so no longer minimized
                mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
                mMotionHelper.animateToClosestSnapTarget();
                setMinimizedStateInternal(false);
            } else if (mTouchState.isDoubleTap()) {
                // Expand to fullscreen if this is a double tap
@@ -789,6 +793,7 @@ public class PipTouchHandler {
                : mNormalMovementBounds;
        mPipBoundsHandler.setMinEdgeSize(
                isMenuExpanded ? mExpandedShortestEdgeSize : 0);
        mMotionHelper.setCurrentMovementBounds(mMovementBounds);
    }

    /**
@@ -799,16 +804,6 @@ public class PipTouchHandler {
        mDismissViewController.destroyDismissTarget();
    }

    /**
     * Resets some states related to the touch handling.
     */
    private void cleanUp() {
        if (mIsMinimized) {
            setMinimizedStateInternal(false);
        }
        cleanUpDismissTarget();
    }

    /**
     * @return whether the menu will resize as a part of showing the full menu.
     */