Merge "Scale down PIP when it's dragged into the dismiss target." into rvc-dev am: 7a1cd3f757 am: 22c5e25a5b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/11886098 Change-Id: I2ece9747075a3c6e5a0901664c37807c0ef91106
This commit is contained in:
@@ -708,6 +708,12 @@ public class PipTaskOrganizer extends TaskOrganizer implements
|
|||||||
Log.w(TAG, "Abort animation, invalid leash");
|
Log.w(TAG, "Abort animation, invalid leash");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (startBounds.isEmpty() || destinationBounds.isEmpty()) {
|
||||||
|
Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
|
final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
|
||||||
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
|
mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
|
||||||
tx.apply();
|
tx.apply();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ 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.content.Context;
|
||||||
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Debug;
|
import android.os.Debug;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -38,6 +39,9 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject;
|
|||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import kotlin.Unit;
|
||||||
|
import kotlin.jvm.functions.Function0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper to animate and manipulate the PiP.
|
* A helper to animate and manipulate the PiP.
|
||||||
*/
|
*/
|
||||||
@@ -74,9 +78,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
new SfVsyncFrameCallbackProvider();
|
new SfVsyncFrameCallbackProvider();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bounds that are animated using the physics animator.
|
* 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 mAnimatedBounds = new Rect();
|
private final Rect mTemporaryBounds = new Rect();
|
||||||
|
|
||||||
/** The destination bounds to which PIP is animating. */
|
/** The destination bounds to which PIP is animating. */
|
||||||
private final Rect mAnimatingToBounds = new Rect();
|
private final Rect mAnimatingToBounds = new Rect();
|
||||||
@@ -85,20 +95,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
private FloatingContentCoordinator mFloatingContentCoordinator;
|
private FloatingContentCoordinator mFloatingContentCoordinator;
|
||||||
|
|
||||||
/** Callback that re-sizes PIP to the animated bounds. */
|
/** Callback that re-sizes PIP to the animated bounds. */
|
||||||
private final Choreographer.FrameCallback mResizePipVsyncCallback =
|
private final Choreographer.FrameCallback mResizePipVsyncCallback;
|
||||||
l -> resizePipUnchecked(mAnimatedBounds);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PhysicsAnimator instance for animating {@link #mAnimatedBounds} using physics animations.
|
* PhysicsAnimator instance for animating {@link #mTemporaryBounds} using physics animations.
|
||||||
*/
|
*/
|
||||||
private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
|
private PhysicsAnimator<Rect> mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
|
||||||
mAnimatedBounds);
|
mTemporaryBounds);
|
||||||
|
|
||||||
|
private MagnetizedObject<Rect> mMagnetizedPip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update listener that resizes the PIP to {@link #mAnimatedBounds}.
|
* Update listener that resizes the PIP to {@link #mTemporaryBounds}.
|
||||||
*/
|
*/
|
||||||
final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
|
private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener;
|
||||||
(target, values) -> mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
|
|
||||||
|
|
||||||
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
|
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
|
||||||
private PhysicsAnimator.FlingConfig mFlingConfigX;
|
private PhysicsAnimator.FlingConfig mFlingConfigX;
|
||||||
@@ -123,6 +133,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
*/
|
*/
|
||||||
private boolean mSpringingToTouch = false;
|
private boolean mSpringingToTouch = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether PIP was released in the dismiss target, and will be animated out and dismissed
|
||||||
|
* shortly.
|
||||||
|
*/
|
||||||
|
private boolean mDismissalPending = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
|
* Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is
|
||||||
* used to show menu activity when the expand animation is completed.
|
* used to show menu activity when the expand animation is completed.
|
||||||
@@ -155,6 +171,16 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
mSnapAlgorithm = snapAlgorithm;
|
mSnapAlgorithm = snapAlgorithm;
|
||||||
mFloatingContentCoordinator = floatingContentCoordinator;
|
mFloatingContentCoordinator = floatingContentCoordinator;
|
||||||
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
|
mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback);
|
||||||
|
|
||||||
|
mResizePipVsyncCallback = l -> {
|
||||||
|
if (!mTemporaryBounds.isEmpty()) {
|
||||||
|
mPipTaskOrganizer.scheduleUserResizePip(
|
||||||
|
mBounds, mTemporaryBounds, null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mResizePipUpdateListener = (target, values) ->
|
||||||
|
mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -186,19 +212,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
boolean isAnimating() {
|
||||||
* Synchronizes the current bounds with either the pinned stack, or the ongoing animation. This
|
return mTemporaryBoundsPhysicsAnimator.isRunning();
|
||||||
* is done to prepare for a touch gesture.
|
|
||||||
*/
|
|
||||||
void synchronizePinnedStackBoundsForTouchGesture() {
|
|
||||||
if (mAnimatingToBounds.isEmpty()) {
|
|
||||||
// If we're not animating anywhere, sync normally.
|
|
||||||
synchronizePinnedStackBounds();
|
|
||||||
} else {
|
|
||||||
// If we're animating, set the current bounds to the animated bounds. That way, the
|
|
||||||
// touch gesture will begin at the most recent animated location of the bounds.
|
|
||||||
mBounds.set(mAnimatedBounds);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -224,32 +239,54 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
// If we are moving PIP directly to the touch event locations, cancel any animations and
|
// If we are moving PIP directly to the touch event locations, cancel any animations and
|
||||||
// move PIP to the given bounds.
|
// move PIP to the given bounds.
|
||||||
cancelAnimations();
|
cancelAnimations();
|
||||||
resizePipUnchecked(toBounds);
|
|
||||||
mBounds.set(toBounds);
|
if (!isDragging) {
|
||||||
|
resizePipUnchecked(toBounds);
|
||||||
|
mBounds.set(toBounds);
|
||||||
|
} else {
|
||||||
|
mTemporaryBounds.set(toBounds);
|
||||||
|
mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, null);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
|
// If PIP is 'catching up' after being stuck in the dismiss target, update the animation
|
||||||
// to spring towards the new touch location.
|
// to spring towards the new touch location.
|
||||||
mAnimatedBoundsPhysicsAnimator
|
mTemporaryBoundsPhysicsAnimator
|
||||||
|
.spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
|
||||||
|
.spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
|
||||||
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
|
.spring(FloatProperties.RECT_X, toBounds.left, mSpringConfig)
|
||||||
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig)
|
.spring(FloatProperties.RECT_Y, toBounds.top, mSpringConfig);
|
||||||
.withEndActions(() -> mSpringingToTouch = false);
|
|
||||||
|
|
||||||
startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */,
|
startBoundsAnimator(toBounds.left /* toX */, toBounds.top /* toY */,
|
||||||
false /* dismiss */);
|
false /* dismiss */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
|
/** Animates the PIP into the dismiss target, scaling it down. */
|
||||||
void setSpringingToTouch(boolean springingToTouch) {
|
void animateIntoDismissTarget(
|
||||||
if (springingToTouch) {
|
MagnetizedObject.MagneticTarget target,
|
||||||
mAnimatedBounds.set(mBounds);
|
float velX, float velY,
|
||||||
}
|
boolean flung, Function0<Unit> after) {
|
||||||
|
final PointF targetCenter = target.getCenterOnScreen();
|
||||||
|
|
||||||
mSpringingToTouch = springingToTouch;
|
final float desiredWidth = mBounds.width() / 2;
|
||||||
|
final float desiredHeight = mBounds.height() / 2;
|
||||||
|
|
||||||
|
final float destinationX = targetCenter.x - (desiredWidth / 2f);
|
||||||
|
final float destinationY = targetCenter.y - (desiredHeight / 2f);
|
||||||
|
|
||||||
|
mTemporaryBoundsPhysicsAnimator
|
||||||
|
.spring(FloatProperties.RECT_X, destinationX, velX, mSpringConfig)
|
||||||
|
.spring(FloatProperties.RECT_Y, destinationY, velY, mSpringConfig)
|
||||||
|
.spring(FloatProperties.RECT_WIDTH, desiredWidth, mSpringConfig)
|
||||||
|
.spring(FloatProperties.RECT_HEIGHT, desiredHeight, mSpringConfig)
|
||||||
|
.withEndActions(after);
|
||||||
|
|
||||||
|
startBoundsAnimator(destinationX, destinationY, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void prepareForAnimation() {
|
/** Set whether we're springing-to-touch to catch up after being stuck in the dismiss target. */
|
||||||
mAnimatedBounds.set(mBounds);
|
void setSpringingToTouch(boolean springingToTouch) {
|
||||||
|
mSpringingToTouch = springingToTouch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -308,14 +345,23 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
return mBounds;
|
return mBounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the PIP bounds if we're not animating, or the current, temporary animating bounds
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
Rect getPossiblyAnimatingBounds() {
|
||||||
|
return mTemporaryBounds.isEmpty() ? mBounds : mTemporaryBounds;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flings the PiP to the closest snap target.
|
* Flings the PiP to the closest snap target.
|
||||||
*/
|
*/
|
||||||
void flingToSnapTarget(
|
void flingToSnapTarget(
|
||||||
float velocityX, float velocityY,
|
float velocityX, float velocityY,
|
||||||
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
|
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
|
||||||
mAnimatedBounds.set(mBounds);
|
mTemporaryBoundsPhysicsAnimator
|
||||||
mAnimatedBoundsPhysicsAnimator
|
.spring(FloatProperties.RECT_WIDTH, mBounds.width(), mSpringConfig)
|
||||||
|
.spring(FloatProperties.RECT_HEIGHT, mBounds.height(), mSpringConfig)
|
||||||
.flingThenSpring(
|
.flingThenSpring(
|
||||||
FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
|
FloatProperties.RECT_X, velocityX, mFlingConfigX, mSpringConfig,
|
||||||
true /* flingMustReachMinOrMax */)
|
true /* flingMustReachMinOrMax */)
|
||||||
@@ -324,13 +370,14 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
.withEndActions(endAction);
|
.withEndActions(endAction);
|
||||||
|
|
||||||
if (updateAction != null) {
|
if (updateAction != null) {
|
||||||
mAnimatedBoundsPhysicsAnimator.addUpdateListener(
|
mTemporaryBoundsPhysicsAnimator.addUpdateListener(
|
||||||
(target, values) -> updateAction.run());
|
(target, values) -> updateAction.run());
|
||||||
}
|
}
|
||||||
|
|
||||||
final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
|
final float xEndValue = velocityX < 0 ? mMovementBounds.left : mMovementBounds.right;
|
||||||
final float estimatedFlingYEndValue =
|
final float estimatedFlingYEndValue =
|
||||||
PhysicsAnimator.estimateFlingEndValue(mBounds.top, velocityY, mFlingConfigY);
|
PhysicsAnimator.estimateFlingEndValue(
|
||||||
|
mTemporaryBounds.top, velocityY, mFlingConfigY);
|
||||||
|
|
||||||
startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */,
|
startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */,
|
||||||
false /* dismiss */);
|
false /* dismiss */);
|
||||||
@@ -341,8 +388,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
* configuration
|
* configuration
|
||||||
*/
|
*/
|
||||||
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
|
void animateToBounds(Rect bounds, PhysicsAnimator.SpringConfig springConfig) {
|
||||||
mAnimatedBounds.set(mBounds);
|
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
|
||||||
mAnimatedBoundsPhysicsAnimator
|
// Animate from the current bounds if we're not already animating.
|
||||||
|
mTemporaryBounds.set(mBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
mTemporaryBoundsPhysicsAnimator
|
||||||
.spring(FloatProperties.RECT_X, bounds.left, springConfig)
|
.spring(FloatProperties.RECT_X, bounds.left, springConfig)
|
||||||
.spring(FloatProperties.RECT_Y, bounds.top, springConfig);
|
.spring(FloatProperties.RECT_Y, bounds.top, springConfig);
|
||||||
startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */,
|
startBoundsAnimator(bounds.left /* toX */, bounds.top /* toY */,
|
||||||
@@ -353,18 +404,19 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
* Animates the dismissal of the PiP off the edge of the screen.
|
* Animates the dismissal of the PiP off the edge of the screen.
|
||||||
*/
|
*/
|
||||||
void animateDismiss() {
|
void animateDismiss() {
|
||||||
mAnimatedBounds.set(mBounds);
|
|
||||||
|
|
||||||
// Animate off the bottom of the screen, then dismiss PIP.
|
// Animate off the bottom of the screen, then dismiss PIP.
|
||||||
mAnimatedBoundsPhysicsAnimator
|
mTemporaryBoundsPhysicsAnimator
|
||||||
.spring(FloatProperties.RECT_Y,
|
.spring(FloatProperties.RECT_Y,
|
||||||
mBounds.bottom + mBounds.height(),
|
mMovementBounds.bottom + mBounds.height() * 2,
|
||||||
0,
|
0,
|
||||||
mSpringConfig)
|
mSpringConfig)
|
||||||
.withEndActions(this::dismissPip);
|
.withEndActions(this::dismissPip);
|
||||||
|
|
||||||
startBoundsAnimator(mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */,
|
startBoundsAnimator(
|
||||||
|
mBounds.left /* toX */, mBounds.bottom + mBounds.height() /* toY */,
|
||||||
true /* dismiss */);
|
true /* dismiss */);
|
||||||
|
|
||||||
|
mDismissalPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -415,7 +467,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
* Cancels all existing animations.
|
* Cancels all existing animations.
|
||||||
*/
|
*/
|
||||||
private void cancelAnimations() {
|
private void cancelAnimations() {
|
||||||
mAnimatedBoundsPhysicsAnimator.cancel();
|
mTemporaryBoundsPhysicsAnimator.cancel();
|
||||||
mAnimatingToBounds.setEmpty();
|
mAnimatingToBounds.setEmpty();
|
||||||
mSpringingToTouch = false;
|
mSpringingToTouch = false;
|
||||||
}
|
}
|
||||||
@@ -449,15 +501,36 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
(int) toY + mBounds.height());
|
(int) toY + mBounds.height());
|
||||||
setAnimatingToBounds(mAnimatingToBounds);
|
setAnimatingToBounds(mAnimatingToBounds);
|
||||||
|
|
||||||
mAnimatedBoundsPhysicsAnimator
|
if (!mTemporaryBoundsPhysicsAnimator.isRunning()) {
|
||||||
.withEndActions(() -> {
|
mTemporaryBoundsPhysicsAnimator
|
||||||
if (!dismiss) {
|
.addUpdateListener(mResizePipUpdateListener)
|
||||||
mPipTaskOrganizer.scheduleFinishResizePip(mAnimatedBounds);
|
.withEndActions(this::onBoundsAnimationEnd);
|
||||||
}
|
}
|
||||||
mAnimatingToBounds.setEmpty();
|
|
||||||
})
|
mTemporaryBoundsPhysicsAnimator.start();
|
||||||
.addUpdateListener(mResizePipUpdateListener)
|
}
|
||||||
.start();
|
|
||||||
|
/**
|
||||||
|
* Notify that PIP was released in the dismiss target and will be animated out and dismissed
|
||||||
|
* shortly.
|
||||||
|
*/
|
||||||
|
void notifyDismissalPending() {
|
||||||
|
mDismissalPending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onBoundsAnimationEnd() {
|
||||||
|
if (!mDismissalPending
|
||||||
|
&& !mSpringingToTouch
|
||||||
|
&& !mMagnetizedPip.getObjectStuckToTarget()) {
|
||||||
|
mBounds.set(mTemporaryBounds);
|
||||||
|
mPipTaskOrganizer.scheduleFinishResizePip(mBounds);
|
||||||
|
|
||||||
|
mTemporaryBounds.setEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
mAnimatingToBounds.setEmpty();
|
||||||
|
mSpringingToTouch = false;
|
||||||
|
mDismissalPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -503,25 +576,29 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
|||||||
* magnetic dismiss target so it can calculate PIP's size and position.
|
* magnetic dismiss target so it can calculate PIP's size and position.
|
||||||
*/
|
*/
|
||||||
MagnetizedObject<Rect> getMagnetizedPip() {
|
MagnetizedObject<Rect> getMagnetizedPip() {
|
||||||
return new MagnetizedObject<Rect>(
|
if (mMagnetizedPip == null) {
|
||||||
mContext, mAnimatedBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
|
mMagnetizedPip = new MagnetizedObject<Rect>(
|
||||||
@Override
|
mContext, mTemporaryBounds, FloatProperties.RECT_X, FloatProperties.RECT_Y) {
|
||||||
public float getWidth(@NonNull Rect animatedPipBounds) {
|
@Override
|
||||||
return animatedPipBounds.width();
|
public float getWidth(@NonNull Rect animatedPipBounds) {
|
||||||
}
|
return animatedPipBounds.width();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getHeight(@NonNull Rect animatedPipBounds) {
|
public float getHeight(@NonNull Rect animatedPipBounds) {
|
||||||
return animatedPipBounds.height();
|
return animatedPipBounds.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void getLocationOnScreen(
|
public void getLocationOnScreen(
|
||||||
@NonNull Rect animatedPipBounds, @NonNull int[] loc) {
|
@NonNull Rect animatedPipBounds, @NonNull int[] loc) {
|
||||||
loc[0] = animatedPipBounds.left;
|
loc[0] = animatedPipBounds.left;
|
||||||
loc[1] = animatedPipBounds.top;
|
loc[1] = animatedPipBounds.top;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return mMagnetizedPip;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dump(PrintWriter pw, String prefix) {
|
public void dump(PrintWriter pw, String prefix) {
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ import com.android.systemui.util.magnetictarget.MagnetizedObject;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import kotlin.Unit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
|
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
|
||||||
* the PIP.
|
* the PIP.
|
||||||
@@ -262,12 +264,14 @@ public class PipTouchHandler {
|
|||||||
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
|
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
|
||||||
updateMagneticTargetSize();
|
updateMagneticTargetSize();
|
||||||
|
|
||||||
mMagnetizedPip.setPhysicsAnimatorUpdateListener(mMotionHelper.mResizePipUpdateListener);
|
mMagnetizedPip.setAnimateStuckToTarget(
|
||||||
|
(target, velX, velY, flung, after) -> {
|
||||||
|
mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
});
|
||||||
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
|
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||||
mMotionHelper.prepareForAnimation();
|
|
||||||
|
|
||||||
// Show the dismiss target, in case the initial touch event occurred within the
|
// Show the dismiss target, in case the initial touch event occurred within the
|
||||||
// magnetic field radius.
|
// magnetic field radius.
|
||||||
showDismissTargetMaybe();
|
showDismissTargetMaybe();
|
||||||
@@ -286,12 +290,13 @@ public class PipTouchHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||||
|
mMotionHelper.notifyDismissalPending();
|
||||||
|
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
mMotionHelper.animateDismiss();
|
mMotionHelper.animateDismiss();
|
||||||
hideDismissTarget();
|
hideDismissTarget();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
|
MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext,
|
||||||
PipUtils.getTopPipActivity(mContext, mActivityManager));
|
PipUtils.getTopPipActivity(mContext, mActivityManager));
|
||||||
}
|
}
|
||||||
@@ -617,11 +622,16 @@ public class PipTouchHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MotionEvent ev = (MotionEvent) inputEvent;
|
MotionEvent ev = (MotionEvent) inputEvent;
|
||||||
|
if (!mTouchState.isDragging()
|
||||||
if (mPipResizeGestureHandler.isWithinTouchRegion((int) ev.getRawX(), (int) ev.getRawY())) {
|
&& !mMagnetizedPip.getObjectStuckToTarget()
|
||||||
|
&& !mMotionHelper.isAnimating()
|
||||||
|
&& mPipResizeGestureHandler.isWithinTouchRegion(
|
||||||
|
(int) ev.getRawX(), (int) ev.getRawY())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
|
|
||||||
|
if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
|
||||||
|
&& mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
|
||||||
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
|
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
|
||||||
// to the touch state. Touch state needs a DOWN event in order to later process MOVE
|
// to the touch state. Touch state needs a DOWN event in order to later process MOVE
|
||||||
// events it'll receive if the object is dragged out of the magnetic field.
|
// events it'll receive if the object is dragged out of the magnetic field.
|
||||||
@@ -643,7 +653,6 @@ public class PipTouchHandler {
|
|||||||
|
|
||||||
switch (ev.getAction()) {
|
switch (ev.getAction()) {
|
||||||
case MotionEvent.ACTION_DOWN: {
|
case MotionEvent.ACTION_DOWN: {
|
||||||
mMotionHelper.synchronizePinnedStackBoundsForTouchGesture();
|
|
||||||
mGesture.onDown(mTouchState);
|
mGesture.onDown(mTouchState);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -872,7 +881,7 @@ public class PipTouchHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect bounds = mMotionHelper.getBounds();
|
Rect bounds = mMotionHelper.getPossiblyAnimatingBounds();
|
||||||
mDelta.set(0f, 0f);
|
mDelta.set(0f, 0f);
|
||||||
mStartPosition.set(bounds.left, bounds.top);
|
mStartPosition.set(bounds.left, bounds.top);
|
||||||
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
|
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
|
||||||
@@ -914,7 +923,7 @@ public class PipTouchHandler {
|
|||||||
mDelta.x += left - lastX;
|
mDelta.x += left - lastX;
|
||||||
mDelta.y += top - lastY;
|
mDelta.y += top - lastY;
|
||||||
|
|
||||||
mTmpBounds.set(mMotionHelper.getBounds());
|
mTmpBounds.set(mMotionHelper.getPossiblyAnimatingBounds());
|
||||||
mTmpBounds.offsetTo((int) left, (int) top);
|
mTmpBounds.offsetTo((int) left, (int) top);
|
||||||
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
|
mMotionHelper.movePip(mTmpBounds, true /* isDragging */);
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,40 @@ class FloatProperties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the width of a [Rect]. Typically used to animate resizing a Rect horizontally.
|
||||||
|
*
|
||||||
|
* This property's getter returns [Rect.width], and its setter changes the value of
|
||||||
|
* [Rect.right] by adding the animated width value to [Rect.left].
|
||||||
|
*/
|
||||||
|
@JvmField
|
||||||
|
val RECT_WIDTH = object : FloatPropertyCompat<Rect>("RectWidth") {
|
||||||
|
override fun getValue(rect: Rect): Float {
|
||||||
|
return rect.width().toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(rect: Rect, value: Float) {
|
||||||
|
rect.right = rect.left + value.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the height of a [Rect]. Typically used to animate resizing a Rect vertically.
|
||||||
|
*
|
||||||
|
* This property's getter returns [Rect.height], and its setter changes the value of
|
||||||
|
* [Rect.bottom] by adding the animated height value to [Rect.top].
|
||||||
|
*/
|
||||||
|
@JvmField
|
||||||
|
val RECT_HEIGHT = object : FloatPropertyCompat<Rect>("RectHeight") {
|
||||||
|
override fun getValue(rect: Rect): Float {
|
||||||
|
return rect.height().toFloat()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(rect: Rect, value: Float) {
|
||||||
|
rect.bottom = rect.top + value.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the x-coordinate of a [RectF]. Typically used to animate moving a RectF
|
* Represents the x-coordinate of a [RectF]. Typically used to animate moving a RectF
|
||||||
* horizontally.
|
* horizontally.
|
||||||
|
|||||||
@@ -177,6 +177,18 @@ abstract class MagnetizedObject<T : Any>(
|
|||||||
*/
|
*/
|
||||||
var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
|
var physicsAnimatorEndListener: PhysicsAnimator.EndListener<T>? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that is called when the object should be animated stuck to the target. The default
|
||||||
|
* implementation uses the object's x and y properties to animate the object centered inside the
|
||||||
|
* target. You can override this if you need custom animation.
|
||||||
|
*
|
||||||
|
* The method is invoked with the MagneticTarget that the object is sticking to, the X and Y
|
||||||
|
* velocities of the gesture that brought the object into the magnetic radius, whether or not it
|
||||||
|
* was flung, and a callback you must call after your animation completes.
|
||||||
|
*/
|
||||||
|
var animateStuckToTarget: (MagneticTarget, Float, Float, Boolean, (() -> Unit)?) -> Unit =
|
||||||
|
::animateStuckToTargetInternal
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether forcefully flinging the object vertically towards a target causes it to be
|
* Sets whether forcefully flinging the object vertically towards a target causes it to be
|
||||||
* attracted to the target and then released immediately, despite never being dragged within the
|
* attracted to the target and then released immediately, despite never being dragged within the
|
||||||
@@ -373,7 +385,7 @@ abstract class MagnetizedObject<T : Any>(
|
|||||||
targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
|
targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
|
||||||
cancelAnimations()
|
cancelAnimations()
|
||||||
magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
|
magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
|
||||||
animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false)
|
animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null)
|
||||||
|
|
||||||
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
|
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
|
||||||
} else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
|
} else if (targetObjectIsInMagneticFieldOf == null && objectStuckToTarget) {
|
||||||
@@ -430,8 +442,8 @@ abstract class MagnetizedObject<T : Any>(
|
|||||||
targetObjectIsStuckTo = flungToTarget
|
targetObjectIsStuckTo = flungToTarget
|
||||||
|
|
||||||
animateStuckToTarget(flungToTarget, velX, velY, true) {
|
animateStuckToTarget(flungToTarget, velX, velY, true) {
|
||||||
targetObjectIsStuckTo = null
|
|
||||||
magnetListener.onReleasedInTarget(flungToTarget)
|
magnetListener.onReleasedInTarget(flungToTarget)
|
||||||
|
targetObjectIsStuckTo = null
|
||||||
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
|
vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +477,7 @@ abstract class MagnetizedObject<T : Any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Animates sticking the object to the provided target with the given start velocities. */
|
/** Animates sticking the object to the provided target with the given start velocities. */
|
||||||
private fun animateStuckToTarget(
|
private fun animateStuckToTargetInternal(
|
||||||
target: MagneticTarget,
|
target: MagneticTarget,
|
||||||
velX: Float,
|
velX: Float,
|
||||||
velY: Float,
|
velY: Float,
|
||||||
@@ -581,10 +593,10 @@ abstract class MagnetizedObject<T : Any>(
|
|||||||
* multiple objects.
|
* multiple objects.
|
||||||
*/
|
*/
|
||||||
class MagneticTarget(
|
class MagneticTarget(
|
||||||
internal val targetView: View,
|
val targetView: View,
|
||||||
var magneticFieldRadiusPx: Int
|
var magneticFieldRadiusPx: Int
|
||||||
) {
|
) {
|
||||||
internal val centerOnScreen = PointF()
|
val centerOnScreen = PointF()
|
||||||
|
|
||||||
private val tempLoc = IntArray(2)
|
private val tempLoc = IntArray(2)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user