diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index 8b4fddbec03ca..6314fcbde3331 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -394,6 +394,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String PIP_PINCH_RESIZE = "pip_pinch_resize"; + /** + * (boolean) Whether to enable stashing for PIP. + */ + public static final String PIP_STASHING = "pip_stashing"; + /** * (float) Bottom height in DP for Back Gesture. */ 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 dcee2a5b4b20e..e24121928808d 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -58,6 +58,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private static final int EXPAND_STACK_TO_MENU_DURATION = 250; private static final int LEAVE_PIP_DURATION = 300; private static final int SHIFT_DURATION = 300; + private static final float STASH_RATIO = 0.25f; /** Friction to use for PIP when it moves via physics fling animations. */ private static final float DEFAULT_FRICTION = 2f; @@ -120,6 +121,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** FlingConfig instances provided to PhysicsAnimator for fling gestures. */ private PhysicsAnimator.FlingConfig mFlingConfigX; private PhysicsAnimator.FlingConfig mFlingConfigY; + /** FlingConfig instances proviced to PhysicsAnimator for stashing. */ + private PhysicsAnimator.FlingConfig mStashConfigX; /** SpringConfig to use for fling-then-spring animations. */ private final PhysicsAnimator.SpringConfig mSpringConfig = @@ -383,6 +386,21 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, void flingToSnapTarget( float velocityX, float velocityY, @Nullable Runnable updateAction, @Nullable Runnable endAction) { + movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */); + } + + /** + * Stash PiP to the closest edge. + */ + void stashToEdge( + float velocityX, float velocityY, + @Nullable Runnable updateAction, @Nullable Runnable endAction) { + movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */); + } + + private void movetoTarget( + float velocityX, float velocityY, + @Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) { // If we're flinging to a snap target now, we're not springing to catch up to the touch // location now. mSpringingToTouch = false; @@ -391,8 +409,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, .spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig) .spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig) .flingThenSpring( - FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig, - true /* flingMustReachMinOrMax */) + FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX, + mSpringConfig, true /* flingMustReachMinOrMax */) .flingThenSpring( FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig) .withEndActions(endAction); @@ -402,7 +420,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, (target, values) -> updateAction.run()); } - final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right; + final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO); + final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left; + final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right; + + final float xEndValue = velocityX < 0 ? leftEdge : rightEdge; final float estimatedFlingYEndValue = PhysicsAnimator.estimateFlingEndValue( mTemporaryBounds.top, velocityY, mFlingConfigY); @@ -506,6 +528,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, DEFAULT_FRICTION, mMovementBounds.left, mMovementBounds.right); mFlingConfigY = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, mMovementBounds.top, mMovementBounds.bottom); + final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO); + mStashConfigX = new PhysicsAnimator.FlingConfig( + DEFAULT_FRICTION, mMovementBounds.left - offset, mMovementBounds.right + offset); } /** 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 97b3484292c61..858683c4e2d4c 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -16,6 +16,7 @@ package com.android.systemui.pip.phone; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE; import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL; @@ -33,6 +34,7 @@ import android.graphics.Rect; import android.graphics.drawable.TransitionDrawable; import android.os.Handler; import android.os.RemoteException; +import android.provider.DeviceConfig; import android.util.Log; import android.util.Size; import android.view.Gravity; @@ -101,6 +103,13 @@ public class PipTouchHandler { private final AccessibilityManager mAccessibilityManager; private boolean mShowPipMenuOnAnimationEnd = false; + /** + * Whether PIP stash is enabled or not. When enabled, if at the time of fling-release the + * PIP bounds is outside the left/right edge of the screen, it will be shown in "stashed" mode, + * where PIP will only show partially. + */ + private boolean mEnableStash = false; + /** * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move * PIP. @@ -306,6 +315,22 @@ public class PipTouchHandler { }); mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); + + mEnableStash = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, + PIP_STASHING, + /* defaultValue = */ false); + deviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + context.getMainExecutor(), + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (properties.getKeyset().contains(PIP_STASHING)) { + mEnableStash = properties.getBoolean( + PIP_STASHING, /* defaultValue = */ false); + } + } + }); } private void reloadResources() { @@ -986,9 +1011,20 @@ public class PipTouchHandler { // Reset the touch state on up before the fling settles mTouchState.reset(); - mMotionHelper.flingToSnapTarget(vel.x, vel.y, - PipTouchHandler.this::updateDismissFraction /* updateAction */, - this::flingEndAction /* endAction */); + final Rect animatingBounds = mMotionHelper.getPossiblyAnimatingBounds(); + // If User releases the PIP window while it's out of the display bounds, put + // PIP into stashed mode. + if (mEnableStash + && (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right + || animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) { + mMotionHelper.stashToEdge(vel.x, vel.y, + PipTouchHandler.this::updateDismissFraction /* updateAction */, + this::flingEndAction /* endAction */); + } else { + mMotionHelper.flingToSnapTarget(vel.x, vel.y, + PipTouchHandler.this::updateDismissFraction /* updateAction */, + this::flingEndAction /* endAction */); + } } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap // the PiP should be frozen until the transition ends