From 15b29c7bba95b6d8b6288af3f9dfd45efb85be5e Mon Sep 17 00:00:00 2001 From: Mady Mellor Date: Wed, 7 Jun 2017 14:53:58 -0700 Subject: Adjustments to PIP position while flinging Rather than using a scroller, find the actual edge intercept based on the trajectory of the fling. This is done by finding the two points it could intersect with and checking which point is 'closer' (i.e. would be hit first by the PIP). Bias towards using the intersection with the top / bottom edge if the PIP is being flung along the side it's currently on. Also increases the maximum time for the fling. Bug: 35358634 Test: manual - fling PIP around screen while in landscape and portrait Change-Id: I26e943a5ddbc726ab86bc11e4271d4db034f3d47 --- .../android/internal/policy/PipSnapAlgorithm.java | 110 +++++++++++++++++---- core/res/res/values/dimens.xml | 3 + core/res/res/values/symbols.xml | 1 + .../systemui/pip/phone/PipMotionHelper.java | 9 +- .../systemui/pip/phone/PipTouchHandler.java | 10 +- 5 files changed, 109 insertions(+), 24 deletions(-) diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java index 95d714f1c3c7..749d00c136ec 100644 --- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java +++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java @@ -49,9 +49,6 @@ public class PipSnapAlgorithm { // Allows snapping on the long edge in each orientation and magnets towards corners private static final int SNAP_MODE_LONG_EDGE_MAGNET_CORNERS = 4; - // The friction multiplier to control how slippery the PIP is when flung - private static final float SCROLL_FRICTION_MULTIPLIER = 8f; - // Threshold to magnet to a corner private static final float CORNER_MAGNET_THRESHOLD = 0.3f; @@ -64,8 +61,8 @@ public class PipSnapAlgorithm { private final float mDefaultSizePercent; private final float mMinAspectRatioForMinSize; private final float mMaxAspectRatioForMinSize; + private final int mFlingDeceleration; - private Scroller mScroller; private int mOrientation = Configuration.ORIENTATION_UNDEFINED; private final int mMinimizedVisibleSize; @@ -81,6 +78,8 @@ public class PipSnapAlgorithm { mMaxAspectRatioForMinSize = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize); mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize; + mFlingDeceleration = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.pip_fling_deceleration); onConfigurationChanged(); } @@ -107,20 +106,97 @@ public class PipSnapAlgorithm { * those for the given {@param stackBounds}. */ public Rect findClosestSnapBounds(Rect movementBounds, Rect stackBounds, float velocityX, - float velocityY) { - final Rect finalStackBounds = new Rect(stackBounds); - if (mScroller == null) { - final ViewConfiguration viewConfig = ViewConfiguration.get(mContext); - mScroller = new Scroller(mContext); - mScroller.setFriction(viewConfig.getScrollFriction() * SCROLL_FRICTION_MULTIPLIER); + float velocityY, Point dragStartPosition) { + final Rect intersectStackBounds = new Rect(stackBounds); + final Point intersect = getEdgeIntersect(stackBounds, movementBounds, velocityX, velocityY, + dragStartPosition); + intersectStackBounds.offsetTo(intersect.x, intersect.y); + return findClosestSnapBounds(movementBounds, intersectStackBounds); + } + + /** + * @return The point along the {@param movementBounds} that the PIP would intersect with based + * on the provided {@param velX}, {@param velY} along with the position of the PIP when + * the gesture started, {@param dragStartPosition}. + */ + public Point getEdgeIntersect(Rect stackBounds, Rect movementBounds, float velX, float velY, + Point dragStartPosition) { + final boolean isLandscape = mOrientation == Configuration.ORIENTATION_LANDSCAPE; + final int x = stackBounds.left; + final int y = stackBounds.top; + + // Find the line of movement the PIP is on. Line defined by: y = slope * x + yIntercept + final float slope = velY / velX; // slope = rise / run + final float yIntercept = y - slope * x; // rearrange line equation for yIntercept + // The PIP can have two intercept points: + // 1) Where the line intersects with one of the edges of the screen (vertical line) + Point vertPoint = new Point(); + // 2) Where the line intersects with the top or bottom of the screen (horizontal line) + Point horizPoint = new Point(); + + // Find the vertical line intersection, x will be one of the edges + vertPoint.x = velX > 0 ? movementBounds.right : movementBounds.left; + // Sub in x in our line equation to determine y position + vertPoint.y = findY(slope, yIntercept, vertPoint.x); + + // Find the horizontal line intersection, y will be the top or bottom of the screen + horizPoint.y = velY > 0 ? movementBounds.bottom : movementBounds.top; + // Sub in y in our line equation to determine x position + horizPoint.x = findX(slope, yIntercept, horizPoint.y); + + // Now pick one of these points -- first determine if we're flinging along the current edge. + // Only fling along current edge if it's a direction with space for the PIP to move to + int maxDistance; + if (isLandscape) { + maxDistance = velX > 0 + ? movementBounds.right - stackBounds.left + : stackBounds.left - movementBounds.left; + } else { + maxDistance = velY > 0 + ? movementBounds.bottom - stackBounds.top + : stackBounds.top - movementBounds.top; } - mScroller.fling(stackBounds.left, stackBounds.top, - (int) velocityX, (int) velocityY, - movementBounds.left, movementBounds.right, - movementBounds.top, movementBounds.bottom); - finalStackBounds.offsetTo(mScroller.getFinalX(), mScroller.getFinalY()); - mScroller.abortAnimation(); - return findClosestSnapBounds(movementBounds, finalStackBounds); + if (maxDistance > 0) { + // Only fling along the current edge if the start and end point are on the same side + final int startPoint = isLandscape ? dragStartPosition.y : dragStartPosition.x; + final int endPoint = isLandscape ? horizPoint.y : horizPoint.x; + final int center = movementBounds.centerX(); + if ((startPoint < center && endPoint < center) + || (startPoint > center && endPoint > center)) { + // We are flinging along the current edge, figure out how far it should travel + // based on velocity and assumed deceleration. + int distance = (int) (0 - Math.pow(isLandscape ? velX : velY, 2)) + / (2 * mFlingDeceleration); + distance = Math.min(distance, maxDistance); + // Adjust the point for the distance + if (isLandscape) { + horizPoint.x = stackBounds.left + (velX > 0 ? distance : -distance); + } else { + horizPoint.y = stackBounds.top + (velY > 0 ? distance : -distance); + } + return horizPoint; + } + } + // If we're not flinging along the current edge, find the closest point instead. + final double distanceVert = Math.hypot(vertPoint.x - x, vertPoint.y - y); + final double distanceHoriz = Math.hypot(horizPoint.x - x, horizPoint.y - y); + // Ensure that we're actually going somewhere + if (distanceVert == 0) { + return horizPoint; + } + if (distanceHoriz == 0) { + return vertPoint; + } + // Otherwise use the closest point + return Math.abs(distanceVert) > Math.abs(distanceHoriz) ? horizPoint : vertPoint; + } + + private int findY(float slope, float yIntercept, float x) { + return (int) ((slope * x) + yIntercept); + } + + private int findX(float slope, float yIntercept, float y) { + return (int) ((y - yIntercept) / slope); } /** diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 5e2334d20da1..fa33d567983e 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -67,6 +67,9 @@ 48dp + + -3000dp + 800dp diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 241886c97ec1..662b14dc0cd0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1583,6 +1583,7 @@ + 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 b8771d7e0fb6..cebb22f07aaf 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -241,14 +241,14 @@ public class PipMotionHelper implements Handler.Callback { /** * Flings the minimized PiP to the closest minimized snap target. */ - Rect flingToMinimizedState(float velocityY, Rect movementBounds) { + Rect flingToMinimizedState(float velocityY, Rect movementBounds, Point dragStartPosition) { cancelAnimations(); // We currently only allow flinging the minimized stack up and down, so just lock the // movement bounds to the current stack bounds horizontally movementBounds = new Rect(mBounds.left, movementBounds.top, mBounds.left, movementBounds.bottom); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds, - 0 /* velocityX */, velocityY); + 0 /* velocityX */, velocityY, dragStartPosition); if (!mBounds.equals(toBounds)) { mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN); mFlingAnimationUtils.apply(mBoundsAnimator, 0, @@ -281,10 +281,11 @@ public class PipMotionHelper implements Handler.Callback { * Flings the PiP to the closest snap target. */ Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds, - AnimatorUpdateListener updateListener, AnimatorListener listener) { + AnimatorUpdateListener updateListener, AnimatorListener listener, + Point startPosition) { cancelAnimations(); Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds, - velocityX, velocityY); + velocityX, velocityY, startPosition); if (!mBounds.equals(toBounds)) { mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN); mFlingAnimationUtils.apply(mBoundsAnimator, 0, 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 ddaeb04f8443..ab8e1b07f89e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -184,7 +184,7 @@ public class PipTouchHandler { mDismissViewController = new PipDismissViewController(context); mSnapAlgorithm = new PipSnapAlgorithm(mContext); mTouchState = new PipTouchState(mViewConfig); - mFlingAnimationUtils = new FlingAnimationUtils(context, 2f); + mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f); mGestures = new PipTouchGesture[] { mDefaultMovementGesture }; @@ -532,6 +532,7 @@ public class PipTouchHandler { private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() { // Whether the PiP was on the left side of the screen at the start of the gesture private boolean mStartedOnLeft; + private Point mStartPosition; @Override public void onDown(PipTouchState touchState) { @@ -539,7 +540,9 @@ public class PipTouchHandler { return; } - mStartedOnLeft = mMotionHelper.getBounds().left < mMovementBounds.centerX(); + Rect bounds = mMotionHelper.getBounds(); + mStartPosition = new Point(bounds.left, bounds.top); + mStartedOnLeft = bounds.left < mMovementBounds.centerX(); mMovementWithinMinimize = true; mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom; @@ -685,7 +688,8 @@ public class PipTouchHandler { if (isFling) { mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds, - mUpdateScrimListener, postAnimationCallback); + mUpdateScrimListener, postAnimationCallback, + mStartPosition); } else { mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener, postAnimationCallback); -- cgit v1.2.3-59-g8ed1b