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

Commit 1ff1231b authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge changes I13dfced7,I1ff160cf into ub-launcher3-master

* changes:
  Scale adjacent task icon during swipe up
  Scroll recents side to side during swipe up from an app
parents d6d50382 678e295c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
    private static final String TAG = "OverviewSwipeController";

    // Progress after which the transition is assumed to be a success in case user does not fling
    private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
    public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;

    protected final T mActivity;
    private final SwipeDetector mDetector;
+2 −3
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
@@ -77,9 +79,6 @@ import java.util.Objects;
import java.util.function.BiPredicate;
import java.util.function.Consumer;

import androidx.annotation.Nullable;
import androidx.annotation.UiThread;

/**
 * Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
 */
+15 −8
Original line number Diff line number Diff line
@@ -21,11 +21,9 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;

import static com.android.launcher3.util.RaceConditionTracker.ENTER;
import static com.android.launcher3.util.RaceConditionTracker.EXIT;
import static com.android.systemui.shared.system.ActivityManagerWrapper
        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;

import android.annotation.TargetApi;
@@ -192,7 +190,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC

                if (mPassedInitialSlop && mInteractionHandler != null) {
                    // Move
                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
                    dispatchMotion(ev, displacement - mStartDisplacement);
                }
                break;
            }
@@ -207,6 +205,14 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        }
    }

    private void dispatchMotion(MotionEvent ev, float displacement) {
        mInteractionHandler.updateDisplacement(displacement);
        boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
        if (!isLandscape) {
            mInteractionHandler.dispatchMotionEventToRecentsView(ev);
        }
    }

    private void notifyGestureStarted() {
        if (mInteractionHandler == null) {
            return;
@@ -297,15 +303,16 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
     */
    private void finishTouchTracking(MotionEvent ev) {
        if (mPassedInitialSlop && mInteractionHandler != null) {
            mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
            dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement);

            mVelocityTracker.computeCurrentVelocity(1000,
                    ViewConfiguration.get(this).getScaledMaximumFlingVelocity());

            float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
                    : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
            float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
            float velocity = isNavBarOnRight() ? velocityX
                    : isNavBarOnLeft() ? -velocityX
                            : mVelocityTracker.getYVelocity(mActivePointerId);
            mInteractionHandler.onGestureEnded(velocity);
            mInteractionHandler.onGestureEnded(velocity, velocityX);
        } else {
            // Since we start touch tracking on DOWN, we may reach this state without actually
            // starting the gesture. In that case, just cleanup immediately.
+3 −3
Original line number Diff line number Diff line
@@ -97,8 +97,8 @@ public class RecentsAnimationWrapper {
    }

    /**
     * @param onFinishComplete A callback that runs after the animation controller has finished
     *                         on the background thread.
     * @param onFinishComplete A callback that runs on the main thread after the animation
     *                         controller has finished on the background thread.
     */
    public void finish(boolean toHome, Runnable onFinishComplete) {
        if (!toHome) {
@@ -128,7 +128,7 @@ public class RecentsAnimationWrapper {
            controller.finish(toHome);

            if (onFinishComplete != null) {
                onFinishComplete.run();
                mMainThreadExecutor.execute(onFinishComplete);
            }
        }
    }
+129 −30
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowManager;
import android.view.animation.Interpolator;

import androidx.annotation.AnyThread;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
@@ -109,7 +111,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {

    // Interaction finish states
    private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
    private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
    private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 6;

    private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
    private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 8;
@@ -127,7 +129,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 17;

    private static final int STATE_RESUME_LAST_TASK = 1 << 18;
    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 19;
    private static final int STATE_START_NEW_TASK = 1 << 19;
    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 20;


    private static final int LAUNCHER_UI_STATES =
@@ -153,7 +156,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
            "STATE_APP_CONTROLLER_RECEIVED",
            "STATE_SCALED_CONTROLLER_RECENTS",
            "STATE_SCALED_CONTROLLER_APP",
            "STATE_SCALED_CONTROLLER_LAST_TASK",
            "STATE_HANDLER_INVALIDATED",
            "STATE_GESTURE_STARTED_QUICKSTEP",
            "STATE_GESTURE_STARTED_QUICKSCRUB",
@@ -166,6 +169,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            "STATE_SCREENSHOT_CAPTURED",
            "STATE_SCREENSHOT_VIEW_SHOWN",
            "STATE_RESUME_LAST_TASK",
            "STATE_START_NEW_TASK",
            "STATE_ASSIST_DATA_RECEIVED",
    };

@@ -173,7 +177,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    public static final long MIN_SWIPE_DURATION = 80;
    public static final long MIN_OVERSHOOT_DURATION = 120;

    public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
    public static final float MIN_PROGRESS_FOR_OVERVIEW = 0.7f;
    private static final float SWIPE_DURATION_MULTIPLIER =
            Math.min(1 / MIN_PROGRESS_FOR_OVERVIEW, 1 / (1 - MIN_PROGRESS_FOR_OVERVIEW));

@@ -190,6 +194,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
    // visible.
    private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
    private boolean mDispatchedDownEvent;
    // To avoid UI jump when gesture is started, we offset the animation by the threshold.
    private float mShiftAtGestureStart = 0;

@@ -298,10 +303,12 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_APP_CONTROLLER_RECEIVED,
                this::sendRemoteAnimationsToAnimationFactory);

        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_LAST_TASK,
                this::resumeLastTaskForQuickstep);
        mStateCallback.addCallback(STATE_RESUME_LAST_TASK | STATE_APP_CONTROLLER_RECEIVED,
                this::resumeLastTask);
        mStateCallback.addCallback(STATE_START_NEW_TASK | STATE_APP_CONTROLLER_RECEIVED,
                this::startNewTask);

        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE
@@ -327,7 +334,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                this::invalidateHandlerWithLauncher);
        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED
                | STATE_SCALED_CONTROLLER_APP,
                | STATE_SCALED_CONTROLLER_LAST_TASK,
                this::notifyTransitionCancelled);

        mStateCallback.addCallback(QUICK_SCRUB_START_UI_STATE, this::onQuickScrubStartUi);
@@ -410,6 +417,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
            mSyncTransactionApplier = applier;
        });
        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            updateFinalShift();
        });
        mQuickScrubController = mRecentsView.getQuickScrubController();
        mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);

@@ -535,15 +545,39 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        } else {
            offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
                    + tempRect.rect.width();
            float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 +
                    res.getDimensionPixelSize(R.dimen.recents_page_spacing);
            float interpolation = Math.min(1, offsetX / distanceToReachEdge);
            scale = TaskView.getCurveScaleForInterpolation(interpolation);
            scale = getTaskCurveScaleForOffsetX(offsetX, tempRect.rect.width());
        }
        mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
                QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
    }

    private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
        float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
                mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
        float interpolation = Math.min(1, offsetX / distanceToReachEdge);
        return TaskView.getCurveScaleForInterpolation(interpolation);
    }

    @WorkerThread
    public void dispatchMotionEventToRecentsView(MotionEvent event) {
        if (mRecentsView == null) {
            return;
        }
        // Pass the motion events to RecentsView to allow scrolling during swipe up.
        if (mDispatchedDownEvent) {
            mRecentsView.dispatchTouchEvent(event);
        } else {
            // The first event we dispatch should be ACTION_DOWN.
            mDispatchedDownEvent = true;
            MotionEvent downEvent = MotionEvent.obtain(event);
            downEvent.setAction(MotionEvent.ACTION_DOWN);
            int flags = downEvent.getEdgeFlags();
            downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
            mRecentsView.dispatchTouchEvent(downEvent);
            downEvent.recycle();
        }
    }

    @WorkerThread
    public void updateDisplacement(float displacement) {
        // We are moving in the negative x/y direction
@@ -588,11 +622,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {

        RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
        if (controller != null) {
            float offsetX = 0;
            if (mRecentsView != null && mInteractionType == INTERACTION_NORMAL) {
                int startScroll = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(
                        mRecentsView.getRunningTaskView()));
                offsetX = startScroll - mRecentsView.getScrollX();
                offsetX *= mRecentsView.getScaleX();
            }
            float offsetScale = getTaskCurveScaleForOffsetX(offsetX,
                    mClipAnimationHelper.getTargetRect().width());
            SyncRtSurfaceTransactionApplierCompat syncTransactionApplier
                    = Looper.myLooper() == mMainThreadHandler.getLooper()
                            ? mSyncTransactionApplier
                            : null;
            mTransformParams.setProgress(shift).setSyncTransactionApplier(syncTransactionApplier);
            mTransformParams.setProgress(shift).setOffsetX(offsetX).setOffsetScale(offsetScale)
                    .setSyncTransactionApplier(syncTransactionApplier);
            mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
                    mTransformParams);

@@ -621,11 +665,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
            }
        }
        // Update insets of the next previous task, as we might switch to it.
        TaskView nextTaskView = mRecentsView == null ? null : mRecentsView.getNextTaskView();
        if (mInteractionType == INTERACTION_NORMAL && nextTaskView != null) {
        // Update insets of the adjacent tasks, as we might switch to them.
        int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
        if (mInteractionType == INTERACTION_NORMAL && runningTaskIndex >= 0) {
            TaskView nextTaskView = mRecentsView.getTaskViewAt(runningTaskIndex + 1);
            TaskView prevTaskView = mRecentsView.getTaskViewAt(runningTaskIndex - 1);
            if (nextTaskView != null) {
                nextTaskView.setFullscreenProgress(1 - mCurrentShift.value);
            }
            if (prevTaskView != null) {
                prevTaskView.setFullscreenProgress(1 - mCurrentShift.value);
            }
        }

        if (mLauncherTransitionController == null || mLauncherTransitionController
                .getAnimationPlayer().isStarted()) {
@@ -714,7 +765,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    }

    @WorkerThread
    public void onGestureEnded(float endVelocity) {
    public void onGestureEnded(float endVelocity, float velocityX) {
        float flingThreshold = mContext.getResources()
                .getDimension(R.dimen.quickstep_fling_threshold_velocity);
        boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
@@ -723,9 +774,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mLogAction = isFling ? Touch.FLING : Touch.SWIPE;

        if (mBgLongSwipeMode) {
            executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling));
            executeOnUiThread(() -> onLongSwipeGestureFinishUi(endVelocity, isFling, velocityX));
        } else {
            handleNormalGestureEnd(endVelocity, isFling);
            handleNormalGestureEnd(endVelocity, isFling, velocityX);
        }
    }

@@ -742,16 +793,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                mTouchInteractionLog);
    }

    private void handleNormalGestureEnd(float endVelocity, boolean isFling) {
    private void handleNormalGestureEnd(float endVelocity, boolean isFling, float velocityX) {
        float velocityPxPerMs = endVelocity / 1000;
        float velocityXPxPerMs = velocityX / 1000;
        long duration = MAX_SWIPE_DURATION;
        float currentShift = mCurrentShift.value;
        final boolean goingToHome;
        float endShift;
        final float startShift;
        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;
        final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
        if (!isFling) {
            goingToHome = currentShift >= MIN_PROGRESS_FOR_OVERVIEW && mGestureStarted;
            goingToHome = reachedOverviewThreshold && mGestureStarted;
            endShift = goingToHome ? 1 : 0;
            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
@@ -759,7 +815,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            startShift = currentShift;
            interpolator = goingToHome ? OVERSHOOT_1_2 : DEACCEL;
        } else {
            goingToHome = endVelocity < 0;
            // If user scrolled to a new task, only go to home (overview) if they already passed
            // the overview threshold. Otherwise, we'll snap to the new task and launch it.
            goingToHome = endVelocity < 0 && (!goingToNewTask || reachedOverviewThreshold);
            endShift = goingToHome ? 1 : 0;
            startShift = Utilities.boundToRange(currentShift - velocityPxPerMs
                    * SINGLE_FRAME_MS / mTransitionDragLength, 0, 1);
@@ -786,9 +844,29 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        }
        if (goingToHome) {
            mRecentsAnimationWrapper.enableTouchProxy();
        } else if (goingToNewTask) {
            // We aren't goingToHome, 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()) {
                // 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, goingToHome);
        animateToProgress(startShift, endShift, duration, interpolator, goingToHome,
                goingToNewTask);
    }

    private void doLogGesture(boolean toLauncher) {
@@ -814,22 +892,26 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {

    /** Animates to the given progress, where 0 is the current app and 1 is overview. */
    private void animateToProgress(float start, float end, long duration,
            Interpolator interpolator, boolean goingToHome) {
            Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) {
        mRecentsAnimationWrapper.runOnInit(() -> animateToProgressInternal(start, end, duration,
                interpolator, goingToHome));
                interpolator, goingToHome, goingToNewTask));
    }

    private void animateToProgressInternal(float start, float end, long duration,
            Interpolator interpolator, boolean goingToHome) {
            Interpolator interpolator, boolean goingToHome, boolean goingToNewTask) {
        mIsGoingToHome = goingToHome;
        ObjectAnimator anim = mCurrentShift.animateToValue(start, end).setDuration(duration);
        anim.setInterpolator(interpolator);
        anim.addListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationSuccess(Animator animator) {
                int recentsState = STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
                        | STATE_SCREENSHOT_VIEW_SHOWN;
                setStateOnUiThread(mIsGoingToHome
                        ? (STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
                        | STATE_SCREENSHOT_VIEW_SHOWN) : STATE_SCALED_CONTROLLER_APP);
                        ? recentsState
                        : goingToNewTask
                            ? STATE_START_NEW_TASK
                            : STATE_SCALED_CONTROLLER_LAST_TASK);
            }
        });
        anim.start();
@@ -872,6 +954,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mTouchInteractionLog.finishRecentsAnimation(false);
    }

    @UiThread
    private void startNewTask() {
        // Launch the task user scrolled to (mRecentsView.getNextPage()).
        mRecentsAnimationWrapper.finish(true /* toHome */, () -> {
            mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false,
                    result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED),
                    mMainThreadHandler);
        });
        mTouchInteractionLog.finishRecentsAnimation(false);
        doLogGesture(false /* toLauncher */);
    }

    public void reset() {
        if (mInteractionType != INTERACTION_QUICK_SCRUB) {
            // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
@@ -889,6 +983,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {

        mActivityInitListener.unregister();
        mTaskSnapshot = null;

        if (mRecentsView != null) {
            mRecentsView.setOnScrollChangeListener(null);
        }
    }

    private void invalidateHandlerWithLauncher() {
@@ -999,7 +1097,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        long duration = FeatureFlags.QUICK_SWITCH.get()
                ? QUICK_SWITCH_FROM_APP_START_DURATION
                : QUICK_SCRUB_FROM_APP_START_DURATION;
        animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */);
        animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, true /* goingToHome */,
                false /* goingToNewTask */);
    }

    private void onQuickScrubStartUi() {
@@ -1152,10 +1251,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
    }

    private void onLongSwipeGestureFinishUi(float velocity, boolean isFling) {
    private void onLongSwipeGestureFinishUi(float velocity, boolean isFling, float velocityX) {
        if (!mUiLongSwipeMode || mLongSwipeController == null) {
            mUiLongSwipeMode = false;
            handleNormalGestureEnd(velocity, isFling);
            handleNormalGestureEnd(velocity, isFling, velocityX);
            return;
        }
        mUiLongSwipeMode = false;
Loading