Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6b9cb3f5 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge "Add drag down to dismiss pip as a dismiss option"

parents 4763c1b5 57d2255c
Loading
Loading
Loading
Loading
+41 −3
Original line number Diff line number Diff line
@@ -56,7 +56,8 @@ public class PipMotionHelper {

    private static final int DEFAULT_MOVE_STACK_DURATION = 225;
    private static final int SNAP_STACK_DURATION = 225;
    private static final int DISMISS_STACK_DURATION = 375;
    private static final int DRAG_TO_TARGET_DISMISS_STACK_DURATION = 375;
    private static final int DRAG_TO_DISMISS_STACK_DURATION = 175;
    private static final int SHRINK_STACK_FROM_MENU_DURATION = 250;
    private static final int EXPAND_STACK_TO_MENU_DURATION = 250;
    private static final int EXPAND_STACK_TO_FULLSCREEN_DURATION = 300;
@@ -65,6 +66,8 @@ public class PipMotionHelper {

    // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
    // The fraction of the stack height that the user has to drag offscreen to minimize the PiP
    private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f;

    private Context mContext;
    private IActivityManager mActivityManager;
@@ -193,6 +196,19 @@ public class PipMotionHelper {
        }
    }

    /**
     * @return whether the PiP at the current bounds should be dismissed.
     */
    boolean shouldDismissPip() {
        Point displaySize = new Point();
        mContext.getDisplay().getRealSize(displaySize);
        if (mBounds.bottom > displaySize.y) {
            float offscreenFraction = (float) (mBounds.bottom - displaySize.y) / mBounds.height();
            return offscreenFraction >= DISMISS_OFFSCREEN_FRACTION;
        }
        return false;
    }

    /**
     * Flings the minimized PiP to the closest minimized snap target.
     */
@@ -297,16 +313,38 @@ public class PipMotionHelper {
        resizeAndAnimatePipUnchecked(toBounds, IME_SHIFT_DURATION);
    }

    /**
     * Animates the dismissal of the PiP off the edge of the screen.
     */
    Rect animateDragToEdgeDismiss(Rect pipBounds) {
        cancelAnimations();
        Point displaySize = new Point();
        mContext.getDisplay().getRealSize(displaySize);
        Rect toBounds = new Rect(pipBounds);
        toBounds.offset(0, displaySize.y - pipBounds.top);
        mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DRAG_TO_DISMISS_STACK_DURATION,
                FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
        mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                dismissPip();
            }
        });
        mBoundsAnimator.start();
        return toBounds;
    }

    /**
     * Animates the dismissal of the PiP over the dismiss target bounds.
     */
    Rect animateDismissFromDrag(Rect dismissBounds) {
    Rect animateDragToTargetDismiss(Rect dismissBounds) {
        cancelAnimations();
        Rect toBounds = new Rect(dismissBounds.centerX(),
                dismissBounds.centerY(),
                dismissBounds.centerX() + 1,
                dismissBounds.centerY() + 1);
        mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, DISMISS_STACK_DURATION,
        mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
                DRAG_TO_TARGET_DISMISS_STACK_DURATION,
                FAST_OUT_LINEAR_IN, mUpdateBoundsListener);
        mBoundsAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
+57 −32
Original line number Diff line number Diff line
@@ -52,7 +52,8 @@ public class PipTouchHandler {
    private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;

    // Allow dragging the PIP to a location to close it
    private static final boolean ENABLE_DRAG_TO_DISMISS = false;
    private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false;
    private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = false;

    private final Context mContext;
    private final IActivityManager mActivityManager;
@@ -78,7 +79,7 @@ public class PipTouchHandler {
    private Runnable mShowDismissAffordance = new Runnable() {
        @Override
        public void run() {
            if (ENABLE_DRAG_TO_DISMISS) {
            if (ENABLE_DISMISS_DRAG_TO_TARGET) {
                mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
            }
        }
@@ -378,7 +379,7 @@ public class PipTouchHandler {
                mMenuController.pokeMenu();
            }

            if (ENABLE_DRAG_TO_DISMISS) {
            if (ENABLE_DISMISS_DRAG_TO_TARGET) {
                mDismissViewController.createDismissTarget();
                mHandler.postDelayed(mShowDismissAffordance, SHOW_DISMISS_AFFORDANCE_DELAY);
            }
@@ -394,7 +395,7 @@ public class PipTouchHandler {
                mSavedSnapFraction = -1f;
            }

            if (touchState.startedDragging() && ENABLE_DRAG_TO_DISMISS) {
            if (touchState.startedDragging() && ENABLE_DISMISS_DRAG_TO_TARGET) {
                mHandler.removeCallbacks(mShowDismissAffordance);
                mDismissViewController.showDismissTarget(mMotionHelper.getBounds());
            }
@@ -408,11 +409,16 @@ public class PipTouchHandler {
                if (!touchState.allowDraggingOffscreen()) {
                    left = Math.max(mMovementBounds.left, Math.min(mMovementBounds.right, left));
                }
                if (ENABLE_DISMISS_DRAG_TO_EDGE) {
                    // Allow pip to move past bottom bounds
                    top = Math.max(mMovementBounds.top, top);
                } else {
                    top = Math.max(mMovementBounds.top, Math.min(mMovementBounds.bottom, top));
                }
                mTmpBounds.offsetTo((int) left, (int) top);
                mMotionHelper.movePip(mTmpBounds);

                if (ENABLE_DRAG_TO_DISMISS) {
                if (ENABLE_DISMISS_DRAG_TO_TARGET) {
                    mDismissViewController.updateDismissTarget(mTmpBounds);
                }
                return true;
@@ -427,7 +433,7 @@ public class PipTouchHandler {
            }

            try {
                if (ENABLE_DRAG_TO_DISMISS) {
                if (ENABLE_DISMISS_DRAG_TO_TARGET) {
                    mHandler.removeCallbacks(mShowDismissAffordance);
                    PointF vel = mTouchState.getVelocity();
                    final float velocity = PointF.length(vel.x, vel.y);
@@ -435,7 +441,7 @@ public class PipTouchHandler {
                            && velocity < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
                        if (mDismissViewController.shouldDismiss(mMotionHelper.getBounds())) {
                            Rect dismissBounds = mDismissViewController.getDismissBounds();
                            mMotionHelper.animateDismissFromDrag(dismissBounds);
                            mMotionHelper.animateDragToTargetDismiss(dismissBounds);
                            MetricsLogger.action(mContext,
                                    MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
                                    METRIC_VALUE_DISMISSED_BY_DRAG);
@@ -448,9 +454,17 @@ public class PipTouchHandler {
            }

            if (touchState.isDragging()) {
                PointF vel = mTouchState.getVelocity();
                if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
                        || isHorizontalFlingTowardsCurrentEdge(vel))) {
                final boolean onLeft = mMotionHelper.getBounds().left < mMovementBounds.centerX();
                boolean isFlingToBot = isFlingTowardsEdge(touchState, 4 /* bottom */);
                if (ENABLE_DISMISS_DRAG_TO_EDGE
                        && (mMotionHelper.shouldDismissPip() || isFlingToBot)) {
                    mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds());
                    MetricsLogger.action(mContext,
                            MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
                            METRIC_VALUE_DISMISSED_BY_DRAG);
                    return true;
                } else if (!mIsMinimized && (mMotionHelper.shouldMinimizePip()
                        || isFlingTowardsEdge(touchState, onLeft ? 2 : 3))) {
                    // Pip should be minimized
                    setMinimizedStateInternal(true);
                    if (mMenuController.isMenuVisible()) {
@@ -475,6 +489,7 @@ public class PipTouchHandler {
                    mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
                }

                final PointF vel = mTouchState.getVelocity();
                final float velocity = PointF.length(vel.x, vel.y);
                if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
                    mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
@@ -495,29 +510,39 @@ public class PipTouchHandler {
    };

    /**
     * @return whether the gesture ending in the {@param vel} is fast enough to be a fling towards
     *         the same edge the PIP is on. Used to identify a minimize gesture.
     * @return whether the gesture ending in {@param vel} is fast enough to be a fling and towards
     *         the provided {@param edge} where:
     *
     *         1 = top
     *         2 = left
     *         3 = right
     *         4 = bottom
     */
    private boolean isHorizontalFlingTowardsCurrentEdge(PointF vel) {
    private boolean isFlingTowardsEdge(PipTouchState touchState, int edge) {
        final PointF vel = touchState.getVelocity();
        final PointF downPos = touchState.getDownTouchPosition();
        final Rect bounds = mMotionHelper.getBounds();
        final boolean isHorizontal = Math.abs(vel.x) > Math.abs(vel.y);
        final boolean isFling = PointF.length(vel.x, vel.y) > mFlingAnimationUtils
                .getMinVelocityPxPerSecond();
        final boolean towardsCurrentEdge = isOverEdge(true /* left */) && vel.x < 0
                || isOverEdge(false /* right */) && vel.x > 0;
        return towardsCurrentEdge && isHorizontal && isFling;
        final boolean isFling =
                PointF.length(vel.x, vel.y) > mFlingAnimationUtils.getMinVelocityPxPerSecond();
        if (!isFling) {
            return false;
        }

    /**
     * @return whether the given bounds are on the left or right edge (depending on
     *         {@param checkLeft})
     */
    private boolean isOverEdge(boolean checkLeft) {
        final Rect bounds = mMotionHelper.getBounds();
        if (checkLeft) {
            return bounds.left <= mMovementBounds.left;
        } else {
            return bounds.right >= mMovementBounds.right + bounds.width();
        switch (edge) {
            case 1: // top
                return !isHorizontal && vel.y < 0
                        && downPos.y <= mMovementBounds.top + bounds.height();
            case 2: // left
                return isHorizontal && vel.x < 0
                        && downPos.x <= mMovementBounds.left + bounds.width();
            case 3: // right
                return isHorizontal && vel.x > 0
                        && downPos.x >= mMovementBounds.right;
            case 4: // bottom
                return !isHorizontal && vel.y > 0
                        && downPos.y >= mMovementBounds.bottom;
        }
        return false;
    }

    /**
@@ -542,7 +567,7 @@ public class PipTouchHandler {
        pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
        pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
        pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
        pw.println(innerPrefix + "mEnableDragToDismiss=" + ENABLE_DRAG_TO_DISMISS);
        pw.println(innerPrefix + "mEnableDragToDismiss=" + ENABLE_DISMISS_DRAG_TO_TARGET);
        mSnapAlgorithm.dump(pw, innerPrefix);
        mTouchState.dump(pw, innerPrefix);
        mMotionHelper.dump(pw, innerPrefix);
+7 −0
Original line number Diff line number Diff line
@@ -169,6 +169,13 @@ public class PipTouchState {
        return mLastDelta;
    }

    /**
     * @return the down touch position.
     */
    public PointF getDownTouchPosition() {
        return mDownTouch;
    }

    /**
     * @return the movement delta between the last handled touch event and the down touch
     *         position.