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

Commit 5cd26ff4 authored by Winson Chung's avatar Winson Chung
Browse files

Adding option to swipe to dismiss.

- Adding tuner settings for PIP

Test: Manual, open PIP and swipe offscreen to dismiss

Change-Id: I62f8e4c4b96984b9f266dde5efc3c511cf37f7dd
parent cd1ff646
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1667,4 +1667,24 @@
         not appear on production builds ever. -->
    <string name="plugins" translatable="false">Plugins</string>

    <!-- PIP section of the tuner. Non-translatable since it should
        not appear on production builds ever. -->
    <string name="picture_in_picture" translatable="false">Picture-in-Picture</string>

    <!-- PIP swipe to dismiss title. Non-translatable since it should
        not appear on production builds ever. -->
    <string name="pip_swipe_to_dismiss_title" translatable="false">Swipe to dismiss</string>

    <!-- PIP swipe to dismiss description. Non-translatable since it should
        not appear on production builds ever. -->
    <string name="pip_swipe_to_dismiss_summary" translatable="false">Swipe left or right off screen to close the PIP</string>

    <!-- PIP drag to dismiss title. Non-translatable since it should
        not appear on production builds ever. -->
    <string name="pip_drag_to_dismiss_title" translatable="false">Drag to dismiss</string>

    <!-- PIP drag to dismiss description. Non-translatable since it should
        not appear on production builds ever. -->
    <string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string>

</resources>
+18 −0
Original line number Diff line number Diff line
@@ -121,6 +121,24 @@

    </PreferenceScreen>

    <PreferenceScreen
      android:key="picture_in_picture"
      android:title="@string/picture_in_picture">

        <com.android.systemui.tuner.TunerSwitch
          android:key="pip_swipe_to_dismiss"
          android:title="@string/pip_swipe_to_dismiss_title"
          android:summary="@string/pip_swipe_to_dismiss_summary"
          sysui:defValue="true" />

        <com.android.systemui.tuner.TunerSwitch
          android:key="pip_drag_to_dismiss"
          android:title="@string/pip_drag_to_dismiss_title"
          android:summary="@string/pip_drag_to_dismiss_summary"
          sysui:defValue="true" />

    </PreferenceScreen>

    <!--
    <Preference
        android:key="nav_bar"
+115 −32
Original line number Diff line number Diff line
@@ -46,15 +46,19 @@ import android.view.animation.Interpolator;
import com.android.internal.os.BackgroundThread;
import com.android.internal.policy.PipSnapAlgorithm;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.tuner.TunerService;

/**
 * Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
 * the PIP.
 */
public class PipTouchHandler {
public class PipTouchHandler implements TunerService.Tunable {
    private static final String TAG = "PipTouchHandler";
    private static final boolean DEBUG_ALLOW_OUT_OF_BOUNDS_STACK = false;

    private static final String TUNER_KEY_SWIPE_TO_DISMISS = "pip_swipe_to_dismiss";
    private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss";

    private static final int SNAP_STACK_DURATION = 225;
    private static final int DISMISS_STACK_DURATION = 375;
    private static final int EXPAND_STACK_DURATION = 225;
@@ -65,9 +69,12 @@ public class PipTouchHandler {
    private final InputChannel mInputChannel = new InputChannel();

    private final PipInputEventReceiver mInputEventReceiver;
    private final PipDismissViewController mDismissViewController;
    private PipDismissViewController mDismissViewController;
    private PipSnapAlgorithm mSnapAlgorithm;

    private boolean mEnableSwipeToDismiss = true;
    private boolean mEnableDragToDismiss = true;

    private final Rect mPinnedStackBounds = new Rect();
    private final Rect mBoundedPinnedStackBounds = new Rect();
    private ValueAnimator mPinnedStackBoundsAnimator = null;
@@ -75,6 +82,7 @@ public class PipTouchHandler {
    private final PointF mDownTouch = new PointF();
    private final PointF mLastTouch = new PointF();
    private boolean mIsDragging;
    private boolean mIsSwipingToDismiss;
    private int mActivePointerId;

    private final FlingAnimationUtils mFlingAnimationUtils;
@@ -117,8 +125,26 @@ public class PipTouchHandler {
        mActivityManager = activityManager;
        mViewConfig = ViewConfiguration.get(context);
        mInputEventReceiver = new PipInputEventReceiver(mInputChannel, Looper.myLooper());
        if (mEnableDragToDismiss) {
            mDismissViewController = new PipDismissViewController(context);
        }
        mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);

        // Register any tuner settings changes
        TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS,
            TUNER_KEY_DRAG_TO_DISMISS);
    }

    @Override
    public void onTuningChanged(String key, String newValue) {
        switch (key) {
            case TUNER_KEY_SWIPE_TO_DISMISS:
                mEnableSwipeToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
                break;
            case TUNER_KEY_DRAG_TO_DISMISS:
                mEnableDragToDismiss = (newValue != null) && Integer.parseInt(newValue) != 0;
                break;
        }
    }

    public void onConfigurationChanged() {
@@ -140,9 +166,11 @@ public class PipTouchHandler {
                mLastTouch.set(ev.getX(), ev.getY());
                mDownTouch.set(mLastTouch);
                mIsDragging = false;
                // TODO: Consider setting a timer such at after X time, we show the dismiss target
                //       if the user hasn't already dragged some distance
                if (mEnableDragToDismiss) {
                    // TODO: Consider setting a timer such at after X time, we show the dismiss
                    //       target if the user hasn't already dragged some distance
                    mDismissViewController.createDismissTarget();
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {
@@ -150,27 +178,40 @@ public class PipTouchHandler {
                mVelocityTracker.addMovement(ev);

                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                float x = ev.getX(activePointerIndex);
                float y = ev.getY(activePointerIndex);
                float left = mPinnedStackBounds.left + (x - mLastTouch.x);
                float top = mPinnedStackBounds.top + (y - mLastTouch.y);

                if (!mIsDragging) {
                    // Check if the pointer has moved far enough
                    float movement = PointF.length(mDownTouch.x - ev.getX(activePointerIndex),
                            mDownTouch.y - ev.getY(activePointerIndex));
                    float movement = PointF.length(mDownTouch.x - x, mDownTouch.y - y);
                    if (movement > mViewConfig.getScaledTouchSlop()) {
                        mIsDragging = true;
                        if (mEnableSwipeToDismiss) {
                            // TODO: this check can have some buffer so that we only start swiping
                            //       after a significant move out of bounds
                            mIsSwipingToDismiss = !(mBoundedPinnedStackBounds.left <= left &&
                                    left <= mBoundedPinnedStackBounds.right) &&
                                    Math.abs(mDownTouch.x - x) > Math.abs(y - mLastTouch.y);
                        }
                        if (mEnableDragToDismiss) {
                            mDismissViewController.showDismissTarget();
                        }
                    }
                }

                if (mIsDragging) {
                if (mIsSwipingToDismiss) {
                    // Ignore the vertical movement
                    top = mPinnedStackBounds.top;
                    movePinnedStack(left, top);
                } else if (mIsDragging) {
                    // Move the pinned stack
                    float dx = ev.getX(activePointerIndex) - mLastTouch.x;
                    float dy = ev.getY(activePointerIndex) - mLastTouch.y;
                    float left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
                            mBoundedPinnedStackBounds.right, mPinnedStackBounds.left + dx));
                    float top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
                            mBoundedPinnedStackBounds.bottom, mPinnedStackBounds.top + dy));
                    if (DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
                        left = mPinnedStackBounds.left + dx;
                        top = mPinnedStackBounds.top + dy;
                    if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
                        left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
                                mBoundedPinnedStackBounds.right, left));
                        top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
                                mBoundedPinnedStackBounds.bottom, top));
                    }
                    movePinnedStack(left, top);
                }
@@ -194,21 +235,29 @@ public class PipTouchHandler {
            case MotionEvent.ACTION_UP: {
                // Update the velocity tracker
                mVelocityTracker.addMovement(ev);

                if (mIsDragging) {
                mVelocityTracker.computeCurrentVelocity(1000,
                    ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
                float velocityX = mVelocityTracker.getXVelocity();
                float velocityY = mVelocityTracker.getYVelocity();
                float velocity = PointF.length(velocityX, velocityY);

                if (mIsSwipingToDismiss) {
                    if (Math.abs(velocityX) > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
                        flingToDismiss(velocityX);
                    } else {
                        animateToClosestSnapTarget();
                    }
                } else if (mIsDragging) {
                    if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
                        flingToSnapTarget(velocity, velocityX, velocityY);
                    } else {
                        int activePointerIndex = ev.findPointerIndex(mActivePointerId);
                        int x = (int) ev.getX(activePointerIndex);
                        int y = (int) ev.getY(activePointerIndex);
                        Rect dismissBounds = mDismissViewController.getDismissBounds();
                        if (dismissBounds.contains(x, y)) {
                        Rect dismissBounds = mEnableDragToDismiss
                                ? mDismissViewController.getDismissBounds()
                                : null;
                        if (dismissBounds != null && dismissBounds.contains(x, y)) {
                            animateDismissPinnedStack(dismissBounds);
                        } else {
                            animateToClosestSnapTarget();
@@ -217,12 +266,15 @@ public class PipTouchHandler {
                } else {
                    expandPinnedStackToFullscreen();
                }
                if (mEnableDragToDismiss) {
                    mDismissViewController.destroyDismissTarget();
                }

                // Fall through to clean up
            }
            case MotionEvent.ACTION_CANCEL: {
                mIsDragging = false;
                mIsSwipingToDismiss = false;
                recycleVelocityTracker();
                break;
            }
@@ -245,7 +297,7 @@ public class PipTouchHandler {
    }

    /**
     * Creates an animation that continues the fling to a snap target.
     * Flings the PIP to the closest snap target.
     */
    private void flingToSnapTarget(float velocity, float velocityX, float velocityY) {
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
@@ -261,7 +313,7 @@ public class PipTouchHandler {
    }

    /**
     * Animates the pinned stack to the closest snap target.
     * Animates the PIP to the closest snap target.
     */
    private void animateToClosestSnapTarget() {
        Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
@@ -274,7 +326,38 @@ public class PipTouchHandler {
    }

    /**
     * Animates the dismissal of the pinned stack into the given bounds.
     * Flings the PIP to dismiss it offscreen.
     */
    private void flingToDismiss(float velocityX) {
        float offsetX = velocityX > 0
            ? mBoundedPinnedStackBounds.right + 2 * mPinnedStackBounds.width()
            : mBoundedPinnedStackBounds.left - 2 * mPinnedStackBounds.width();
        Rect toBounds = new Rect(mPinnedStackBounds);
        toBounds.offsetTo((int) offsetX, toBounds.top);
        if (!mPinnedStackBounds.equals(toBounds)) {
            mPinnedStackBoundsAnimator = createResizePinnedStackAnimation(
                toBounds, 0, FAST_OUT_SLOW_IN);
            mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
                distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
                velocityX);
            mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    BackgroundThread.getHandler().post(() -> {
                        try {
                            mActivityManager.removeStack(PINNED_STACK_ID);
                        } catch (RemoteException e) {
                            Log.e(TAG, "Failed to remove PIP", e);
                        }
                    });
                }
            });
            mPinnedStackBoundsAnimator.start();
        }
    }

    /**
     * Animates the dismissal of the PIP over the dismiss target bounds.
     */
    private void animateDismissPinnedStack(Rect dismissBounds) {
        Rect toBounds = new Rect(dismissBounds.centerX(),