From 06927c22d847c7b68a7c8375b13c9e4e6a6c2a3a Mon Sep 17 00:00:00 2001 From: Joshua Tsuji Date: Thu, 7 May 2020 12:15:24 -0400 Subject: [PATCH] Fix PIP snapping to incorrect location. animateToClosestSnapTarget is no longer used, we always use flingToSnapTarget now. If velocity is low/zero, the fling animation will end and a spring animation will spring it to the nearest allowable location. This also incorporates a fix to PhysicsAnimator (cl/296316037) to more accurately decide if a fling is strong enough to reach the other side of the screen. This fixes an issue where pushing PIP off-screen and then very gently moving it back on screen caused it to jump to the other side of the screen. Fixes: 154937295 Test: manual Change-Id: I6f0284195bcb802f4cdd58cc06457023dcf63aa1 --- .../systemui/pip/phone/PipMotionHelper.java | 9 ------- .../systemui/pip/phone/PipTouchHandler.java | 11 +++----- .../util/animation/PhysicsAnimator.kt | 25 ++++++++++++++++--- 3 files changed, 24 insertions(+), 21 deletions(-) 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 b1e4d67585453..39da55c8dd54b 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -327,15 +327,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, startBoundsAnimator(xEndValue /* toX */, estimatedFlingYEndValue /* toY */); } - /** - * Animates the PiP to the closest snap target. - */ - void animateToClosestSnapTarget() { - final Rect newBounds = new Rect(); - mSnapAlgorithm.snapRectToClosestEdge(mBounds, mMovementBounds, newBounds); - animateToBounds(newBounds, mSpringConfig); - } - /** * Animates PIP to the provided bounds, using physics animations and the given spring * configuration 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 f5c83c1fffd71..3bdedc14ad2a7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -885,7 +885,6 @@ public class PipTouchHandler { final PointF vel = touchState.getVelocity(); final float velocity = PointF.length(vel.x, vel.y); - final boolean isFling = velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond(); if (touchState.isDragging()) { Runnable endAction = null; @@ -900,13 +899,9 @@ public class PipTouchHandler { endAction = mMenuController::hideMenu; } - if (isFling) { - mMotionHelper.flingToSnapTarget(vel.x, vel.y, - PipTouchHandler.this::updateDismissFraction /* updateAction */, - endAction /* endAction */); - } else { - mMotionHelper.animateToClosestSnapTarget(); - } + mMotionHelper.flingToSnapTarget(vel.x, vel.y, + PipTouchHandler.this::updateDismissFraction /* updateAction */, + endAction /* endAction */); } else if (mTouchState.isDoubleTap()) { // Expand to fullscreen if this is a double tap // the PiP should be frozen until the transition ends diff --git a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt index db08d64acc109..5df5f405b1695 100644 --- a/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/util/animation/PhysicsAnimator.kt @@ -311,10 +311,25 @@ class PhysicsAnimator private constructor (val target: T) { val springConfigCopy = springConfig.copy() val toAtLeast = if (startVelocity < 0) flingConfig.min else flingConfig.max - // If the fling needs to reach min/max, calculate the velocity required to do so and use - // that if the provided start velocity is not sufficient. - if (flingMustReachMinOrMax && - toAtLeast != -Float.MAX_VALUE && toAtLeast != Float.MAX_VALUE) { + if (flingMustReachMinOrMax && isValidValue(toAtLeast)) { + val currentValue = property.getValue(target) + val flingTravelDistance = + startVelocity / (flingConfig.friction * FLING_FRICTION_SCALAR_MULTIPLIER) + val projectedFlingEndValue = currentValue + flingTravelDistance + val midpoint = (flingConfig.min + flingConfig.max) / 2 + + // If fling velocity is too low to push the target past the midpoint between min and + // max, then spring back towards the nearest edge, starting with the current velocity. + if ((startVelocity < 0 && projectedFlingEndValue > midpoint) || + (startVelocity > 0 && projectedFlingEndValue < midpoint)) { + val toPosition = + if (projectedFlingEndValue < midpoint) flingConfig.min else flingConfig.max + if (isValidValue(toPosition)) { + return spring(property, toPosition, startVelocity, springConfig) + } + } + + // Projected fling end value is past the midpoint, so fling forward. val distanceToDestination = toAtLeast - property.getValue(target) // The minimum velocity required for the fling to end up at the given destination, @@ -345,6 +360,8 @@ class PhysicsAnimator private constructor (val target: T) { return this } + private fun isValidValue(value: Float) = value < Float.MAX_VALUE && value > -Float.MAX_VALUE + /** * Adds a listener that will be called whenever any property on the animated object is updated. * This will be called on every animation frame, with the current value of the animated object