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

Commit 023f404a authored by Tony Wickham's avatar Tony Wickham
Browse files

Make quick switch ("hook") more reliable

- Add max orthogonal displacement to MotionPauseDetector to avoid
  pausing when swiping left or right
- When gesture in ambiguous between swipe up for home or swipe over
  for the new task, base the decision on the faster velocity component
- Disable recents freescroll mode when dispatching motion from the nav
  bar. This way recents handles it naturally and we don't need custom
  logic to snap to the next page at the end of the gesture.
- Fix a bug where you couldn't hook to start a new task when SWIPE_HOME
  was disabled

Bug: 111926330
Change-Id: If63aa2bb32b57c3f401c5df8b3f6f4efec97b1fa
parent eefa08b8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {

    @Override
    public boolean onDrag(float displacement) {
        mMotionPauseDetector.addPosition(displacement);
        mMotionPauseDetector.addPosition(displacement, 0);
        return super.onDrag(displacement);
    }

+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
    <dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
    <dimen name="motion_pause_detector_speed_fast">0.5dp</dimen>
    <dimen name="motion_pause_detector_min_displacement">48dp</dimen>
    <dimen name="motion_pause_detector_max_orthogonal_displacement">48dp</dimen>

    <!-- Launcher app transition -->
    <dimen name="content_trans_y">50dp</dimen>
+5 −1
Original line number Diff line number Diff line
@@ -201,7 +201,11 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
                    dispatchMotion(ev, displacement - mStartDisplacement, null);

                    if (FeatureFlags.SWIPE_HOME.get()) {
                        mMotionPauseDetector.addPosition(displacement);
                        boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
                        float orthogonalDisplacement = !isLandscape
                                ? ev.getX() - mDownPos.x
                                : ev.getY() - mDownPos.y;
                        mMotionPauseDetector.addPosition(displacement, orthogonalDisplacement);
                    }
                }
                break;
+21 −18
Original line number Diff line number Diff line
@@ -478,6 +478,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
            mSyncTransactionApplier = applier;
        });
        mRecentsView.setEnableFreeScroll(false);
        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            if (!mBgLongSwipeMode && !mIsGoingToHome) {
                updateFinalShift();
@@ -910,7 +911,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        Interpolator interpolator = DEACCEL;
        final int nextPage = mRecentsView != null ? mRecentsView.getNextPage() : -1;
        final int runningTaskIndex = mRecentsView != null ? mRecentsView.getRunningTaskIndex() : -1;
        boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex;
        boolean goingToNewTask = mRecentsView != null && nextPage != runningTaskIndex
                && mRecentsView.getTaskViewAt(nextPage) != null;
        final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
        if (!isFling) {
            if (SWIPE_HOME.get()) {
@@ -922,7 +924,11 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                    endTarget = currentShift < MIN_PROGRESS_FOR_OVERVIEW ? LAST_TASK : HOME;
                }
            } else {
                endTarget = reachedOverviewThreshold && mGestureStarted ? RECENTS : LAST_TASK;
                endTarget = reachedOverviewThreshold && mGestureStarted
                        ? RECENTS
                        : goingToNewTask
                                ? NEW_TASK
                                : LAST_TASK;
            }
            endShift = endTarget.endShift;
            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
@@ -932,7 +938,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
        } else {
            if (SWIPE_HOME.get() && endVelocity < 0 && !mIsShelfPeeking) {
                endTarget = HOME;
                // If swiping at a diagonal, base end target on the faster velocity.
                endTarget = goingToNewTask && Math.abs(velocityX) > Math.abs(endVelocity)
                        ? NEW_TASK : HOME;
            } else if (endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold)) {
                // If user scrolled to a new task, only go to recents if they already passed
                // the overview threshold. Otherwise, we'll snap to the new task and launch it.
@@ -970,27 +978,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
        } else if (endTarget == RECENTS) {
            mRecentsAnimationWrapper.enableTouchProxy();
            if (mRecentsView != null) {
                duration = Math.max(duration, mRecentsView.getScroller().getDuration());
            }
            if (SWIPE_HOME.get()) {
                setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
            }
        } else if (endTarget == NEW_TASK) {
            // We aren't goingToRecents, and user scrolled/flung to a new task; snap to the closest
            // task in that direction and launch it (in startNewTask()).
            int taskToLaunch = runningTaskIndex + (nextPage > runningTaskIndex ? 1 : -1);
            if (taskToLaunch >= mRecentsView.getTaskViewCount()) {
            // Let RecentsView handle the scrolling to the task, which we launch in startNewTask().
            if (mRecentsView != null) {
                duration = Math.max(duration, mRecentsView.getScroller().getDuration());
            }
        } else if (endTarget == LAST_TASK) {
            if (mRecentsView != null && nextPage != runningTaskIndex) {
                // Scrolled to Clear all button, snap back to current task and resume it.
                mRecentsView.snapToPage(runningTaskIndex, Math.toIntExact(duration));
                goingToNewTask = false;
            } else {
                float distance = Math.abs(mRecentsView.getScrollForPage(taskToLaunch)
                        - mRecentsView.getScrollX());
                int durationX = (int) Math.abs(distance / velocityXPxPerMs);
                if (durationX > MAX_SWIPE_DURATION) {
                    durationX = Math.toIntExact(MAX_SWIPE_DURATION);
                }
                interpolator = Interpolators.scrollInterpolatorForVelocity(velocityXPxPerMs);
                mRecentsView.snapToPage(taskToLaunch, durationX, interpolator);
                duration = Math.max(duration, durationX);
            }
        }
        animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
@@ -1195,6 +1197,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mLayoutListener.finish();
        mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);

        mRecentsView.setEnableFreeScroll(true);
        mRecentsView.setRunningTaskIconScaledDown(false);
        mRecentsView.setOnScrollChangeListener(null);
        mQuickScrubController.cancelActiveQuickscrub();
+35 −5
Original line number Diff line number Diff line
@@ -36,12 +36,15 @@ public class MotionPauseDetector {
    private final float mSpeedSomewhatFast;
    private final float mSpeedFast;
    private final float mMinDisplacementForPause;
    private final float mMaxOrthogonalDisplacementForPause;

    private Long mPreviousTime = null;
    private Float mPreviousPosition = null;
    private Float mPreviousVelocity = null;

    private TotalDisplacement mTotalDisplacement = new TotalDisplacement();
    private Float mFirstPosition = null;
    private Float mFirstOrthogonalPosition = null;

    private OnMotionPauseListener mOnMotionPauseListener;
    private boolean mIsPaused;
@@ -54,6 +57,8 @@ public class MotionPauseDetector {
        mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
        mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
        mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
        mMaxOrthogonalDisplacementForPause = res.getDimension(
                R.dimen.motion_pause_detector_max_orthogonal_displacement);
    }

    /**
@@ -70,20 +75,26 @@ public class MotionPauseDetector {
    /**
     * Computes velocity and acceleration to determine whether the motion is paused.
     * @param position The x or y component of the motion being tracked.
     * @param orthogonalPosition The x or y component (opposite of {@param position}) of the motion.
     *
     * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
     */
    public void addPosition(float position) {
    public void addPosition(float position, float orthogonalPosition) {
        if (mFirstPosition == null) {
            mFirstPosition = position;
        }
        if (mFirstOrthogonalPosition == null) {
            mFirstOrthogonalPosition = orthogonalPosition;
        }
        long time = SystemClock.uptimeMillis();
        if (mPreviousTime != null && mPreviousPosition != null) {
            long changeInTime = Math.max(1, time - mPreviousTime);
            float changeInPosition = position - mPreviousPosition;
            float velocity = changeInPosition / changeInTime;
            if (mPreviousVelocity != null) {
                checkMotionPaused(velocity, mPreviousVelocity, Math.abs(position - mFirstPosition));
                mTotalDisplacement.set(Math.abs(position - mFirstPosition),
                        Math.abs(orthogonalPosition - mFirstOrthogonalPosition));
                checkMotionPaused(velocity, mPreviousVelocity, mTotalDisplacement);
            }
            mPreviousVelocity = velocity;
        }
@@ -91,7 +102,8 @@ public class MotionPauseDetector {
        mPreviousPosition = position;
    }

    private void checkMotionPaused(float velocity, float prevVelocity, float totalDisplacement) {
    private void checkMotionPaused(float velocity, float prevVelocity,
            TotalDisplacement totalDisplacement) {
        float speed = Math.abs(velocity);
        float previousSpeed = Math.abs(prevVelocity);
        boolean isPaused;
@@ -113,8 +125,10 @@ public class MotionPauseDetector {
                }
            }
        }
        boolean passedMinDisplacement = totalDisplacement >= mMinDisplacementForPause;
        isPaused &= passedMinDisplacement;
        boolean passedMinDisplacement = totalDisplacement.primary >= mMinDisplacementForPause;
        boolean passedMaxOrthogonalDisplacement =
                totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
        isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
        if (mIsPaused != isPaused) {
            mIsPaused = isPaused;
            if (mIsPaused) {
@@ -131,6 +145,8 @@ public class MotionPauseDetector {
        mPreviousPosition = null;
        mPreviousVelocity = null;
        mFirstPosition = null;
        mFirstOrthogonalPosition = null;
        mTotalDisplacement.set(0, 0);
        setOnMotionPauseListener(null);
        mIsPaused = mHasEverBeenPaused = false;
    }
@@ -142,4 +158,18 @@ public class MotionPauseDetector {
    public interface OnMotionPauseListener {
        void onMotionPauseChanged(boolean isPaused);
    }

    /**
     * Contains the displacement from the first tracked position,
     * along both the primary and orthogonal axes.
     */
    private class TotalDisplacement {
        public float primary;
        public float orthogonal;

        public void set(float primaryDisplacement, float orthogonalDisplacement) {
            this.primary = primaryDisplacement;
            this.orthogonal = orthogonalDisplacement;
        }
    }
}
Loading