Modifies PIP to use the FloatingContentCoordinator.
Test: atest SystemUITests
Bug: 138115889
Change-Id: I639852a498676230e2318e4ba78c5a4333f6df02
diff --git a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
index 383d459..adee7f23 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/BasePipManager.java
@@ -20,6 +20,7 @@
import java.io.PrintWriter;
+
public interface BasePipManager {
void showPictureInPictureMenu();
default void expandPip() {}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index b5c8d66..cb94e28 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -46,6 +46,7 @@
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 @@
@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 @@
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);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 3ae627d..c6e2852 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -19,6 +19,7 @@
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.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 @@
/**
* 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 @@
/** 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 @@
*/
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 @@
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 @@
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 @@
}
/**
- * 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 @@
});
}
+ /** 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 @@
/**
* @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 @@
/**
* Animates the PiP to the minimized state, slightly offscreen.
*/
- void animateToClosestMinimizedState(Rect movementBounds, @Nullable Runnable updateAction) {
- final Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
+ void animateToClosestMinimizedState(@Nullable Runnable updateAction) {
+ final Rect toBounds = getClosestMinimizedBounds(mBounds);
- prepareForBoundsAnimation(movementBounds);
-
+ mAnimatedBounds.set(mBounds);
mAnimatedBoundsPhysicsAnimator
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
@@ -285,10 +350,8 @@
* 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 @@
.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 @@
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 @@
currentMovementBounds);
}
mSnapAlgorithm.applySnapFraction(normalBounds, normalMovementBounds, savedSnapFraction);
+
if (minimized) {
- normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
+ normalBounds = getClosestMinimizedBounds(normalBounds);
}
+
if (immediate) {
movePip(normalBounds);
} else {
@@ -400,19 +480,15 @@
*/
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);
}
/**
@@ -432,6 +508,16 @@
}
/**
+ * 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}.
*/
private void resizePipUnchecked(Rect toBounds) {
@@ -459,6 +545,7 @@
args.arg1 = toBounds;
args.argi1 = duration;
mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
+ setAnimatingToBounds(toBounds);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 924edb6..8e588e6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -47,6 +47,7 @@
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 @@
// 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 @@
@Override
public void onPipMinimize() {
setMinimizedStateInternal(true);
- mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateAction */);
+ mMotionHelper.animateToClosestMinimizedState(null /* updateAction */);
}
@Override
@@ -172,7 +174,8 @@
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 @@
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 @@
inputConsumerController.setRegistrationListener(this::onRegistrationChanged);
mPipBoundsHandler = pipBoundsHandler;
+ mFloatingContentCoordinator = floatingContentCoordinator;
mConnection = new PipAccessibilityInteractionConnection(mMotionHelper,
this::onAccessibilityShowMenu, mHandler);
}
@@ -228,15 +232,18 @@
}
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 @@
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 @@
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 @@
mMenuController.hideMenu();
} else {
mMotionHelper.animateToClosestMinimizedState(
- mMovementBounds,
PipTouchHandler.this::updateDismissFraction /* updateAction */);
}
return true;
@@ -748,16 +753,15 @@
}
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 @@
: mNormalMovementBounds;
mPipBoundsHandler.setMinEdgeSize(
isMenuExpanded ? mExpandedShortestEdgeSize : 0);
+ mMotionHelper.setCurrentMovementBounds(mMovementBounds);
}
/**
@@ -800,16 +805,6 @@
}
/**
- * 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.
*/
private boolean willResizeMenu() {