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

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

Merge "Allow for continuous scrolling to previous tasks" into ub-launcher3-master

parents bcadb7f4 f9812193
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -95,6 +95,11 @@ public class DeferredTouchConsumer implements TouchConsumer {
        return mTarget.forceToLauncherConsumer();
    }

    @Override
    public OtherActivityTouchConsumer.RecentsAnimationState getRecentsAnimationStateToReuse() {
        return mTarget.getRecentsAnimationStateToReuse();
    }

    @Override
    public boolean deferNextEventToMainThread() {
        // If our target is still null, defer the next target as well
+55 −20
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ 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;
@@ -102,18 +101,21 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
    private WindowTransformSwipeHandler mInteractionHandler;
    private int mDisplayRotation;
    private Rect mStableInsets = new Rect();
    private boolean mCanGestureBeContinued;

    private VelocityTracker mVelocityTracker;
    private MotionPauseDetector mMotionPauseDetector;
    private MotionEventQueue mEventQueue;
    private boolean mIsGoingToLauncher;
    private RecentsAnimationState mRecentsAnimationState;

    public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
            RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
            MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
            @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
            TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
            VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog) {
            VelocityTracker velocityTracker, TouchInteractionLog touchInteractionLog,
            @Nullable RecentsAnimationState recentsAnimationStateToReuse) {
        super(base);

        mRunningTask = runningTaskInfo;
@@ -130,6 +132,7 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        mTouchInteractionLog = touchInteractionLog;
        mTouchInteractionLog.setTouchConsumer(this);
        mInputConsumer = inputConsumer;
        mRecentsAnimationState = recentsAnimationStateToReuse;
    }

    @Override
@@ -150,7 +153,8 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
                mActivePointerId = ev.getPointerId(0);
                mDownPos.set(ev.getX(), ev.getY());
                mLastPos.set(mDownPos);
                mPassedInitialSlop = false;
                // If mRecentsAnimationState != null, we are continuing the previous gesture.
                mPassedInitialSlop = mRecentsAnimationState != null;
                mQuickStepDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();

                // Start the window animation on down to give more time for launcher to draw if the
@@ -256,10 +260,15 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        mTouchInteractionLog.startRecentsAnimation();

        // Create the shared handler
        RecentsAnimationState animationState = new RecentsAnimationState();
        boolean reuseOldAnimState = mRecentsAnimationState != null;
        if (reuseOldAnimState) {
            mRecentsAnimationState.changeParent(this);
        } else {
            mRecentsAnimationState = new RecentsAnimationState(this);
        }
        final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
                animationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper,
                mInputConsumer, mTouchInteractionLog);
                mRecentsAnimationState.id, mRunningTask, this, touchTimeMs, mActivityControlHelper,
                reuseOldAnimState, mInputConsumer, mTouchInteractionLog);

        // Preload the plan
        mRecentsModel.getTasks(null);
@@ -291,8 +300,18 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
                    }
                };

        Runnable startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
                mHomeIntent, assistDataReceiver, animationState, null, null);
        Runnable startActivity;
        if (reuseOldAnimState) {
            startActivity = () -> {
                handler.onRecentsAnimationStart(mRecentsAnimationState.mController,
                        mRecentsAnimationState.mTargets, mRecentsAnimationState.mHomeContentInsets,
                        mRecentsAnimationState.mMinimizedHomeBounds);
            };
        } else {
            startActivity = () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
                    mHomeIntent, assistDataReceiver, mRecentsAnimationState, null, null);
        }


        if (Looper.myLooper() != Looper.getMainLooper()) {
            startActivity.run();
@@ -353,8 +372,10 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        if (mInteractionHandler != null) {
            final WindowTransformSwipeHandler handler = mInteractionHandler;
            mInteractionHandler = null;
            mIsGoingToLauncher = handler.mIsGoingToRecents || handler.mIsGoingToHome;
            mMainThreadExecutor.execute(handler::reset);
            WindowTransformSwipeHandler.GestureEndTarget endTarget = handler.mGestureEndTarget;
            mIsGoingToLauncher = endTarget != null && endTarget.isLauncher;
            mCanGestureBeContinued = endTarget != null && endTarget.canBeContinued;
            mMainThreadExecutor.execute(mCanGestureBeContinued ? handler::cancel : handler::reset);
        }
    }

@@ -443,26 +464,34 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
        return mIsGoingToLauncher;
    }

    @Override
    public @Nullable RecentsAnimationState getRecentsAnimationStateToReuse() {
        return mCanGestureBeContinued ? mRecentsAnimationState : null;
    }

    @Override
    public boolean deferNextEventToMainThread() {
        // TODO: Consider also check if the eventQueue is using mainThread of not.
        return mInteractionHandler != null;
    }

    private class RecentsAnimationState implements RecentsAnimationListener {
    public static class RecentsAnimationState implements RecentsAnimationListener {

        private static final String ANIMATION_START_EVT = "RecentsAnimationState.onAnimationStart";
        private final int id;

        private OtherActivityTouchConsumer mParent;

        private RecentsAnimationControllerCompat mController;
        private RemoteAnimationTargetSet mTargets;
        private Rect mHomeContentInsets;
        private Rect mMinimizedHomeBounds;
        private boolean mCancelled;

        public RecentsAnimationState() {
            id = mAnimationStates.size();
            mAnimationStates.put(id, this);
        public RecentsAnimationState(OtherActivityTouchConsumer parent) {
            mParent = parent;
            id = mParent.mAnimationStates.size();
            mParent.mAnimationStates.put(id, this);
        }

        @Override
@@ -475,31 +504,37 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC
            mTargets = new RemoteAnimationTargetSet(apps, MODE_CLOSING);
            mHomeContentInsets = homeContentInsets;
            mMinimizedHomeBounds = minimizedHomeBounds;
            mEventQueue.onCommand(id);
            mParent.mEventQueue.onCommand(id);
            RaceConditionTracker.onEvent(ANIMATION_START_EVT, EXIT);
        }

        @Override
        public void onAnimationCanceled() {
            mCancelled = true;
            mEventQueue.onCommand(id);
            mParent.mEventQueue.onCommand(id);
        }

        public void execute() {
            if (mInteractionHandler == null || mInteractionHandler.id != id) {
            WindowTransformSwipeHandler handler = mParent.mInteractionHandler;
            if (handler == null || handler.id != id) {
                if (!mCancelled && mController != null) {
                    TraceHelper.endSection("RecentsController", "Finishing no handler");
                    mController.finish(false /* toHome */);
                }
            } else if (mCancelled) {
                TraceHelper.endSection("RecentsController",
                        "Cancelled: " + mInteractionHandler);
                mInteractionHandler.onRecentsAnimationCanceled();
                        "Cancelled: " + handler);
                handler.onRecentsAnimationCanceled();
            } else {
                TraceHelper.partitionSection("RecentsController", "Received");
                mInteractionHandler.onRecentsAnimationStart(mController, mTargets,
                handler.onRecentsAnimationStart(mController, mTargets,
                        mHomeContentInsets, mMinimizedHomeBounds);
            }
        }

        public void changeParent(OtherActivityTouchConsumer newParent) {
            mParent = newParent;
            mParent.mAnimationStates.put(id, this);
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@ import android.view.Choreographer;
import android.view.MotionEvent;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import com.android.quickstep.OtherActivityTouchConsumer.RecentsAnimationState;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -73,5 +76,12 @@ public interface TouchConsumer extends Consumer<MotionEvent> {
        return false;
    }

    /**
     * When continuing a gesture, return the current non-null animation state that hasn't finished.
     */
    default @Nullable RecentsAnimationState getRecentsAnimationStateToReuse() {
        return null;
    }

    default void onShowOverviewFromAltTab() {}
}
+10 −6
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.OtherActivityTouchConsumer.RecentsAnimationState;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -237,16 +238,18 @@ public class TouchInteractionService extends Service {
        if (oldConsumer.deferNextEventToMainThread()) {
            mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
                    new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
                            oldConsumer.forceToLauncherConsumer(), v)));
                            oldConsumer.forceToLauncherConsumer(),
                            oldConsumer.getRecentsAnimationStateToReuse(), v)));
            mEventQueue.deferInit();
        } else {
            mEventQueue = new MotionEventQueue(
                    mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null));
            mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
                    getCurrentTouchConsumer(downHitTarget, false, null, null));
        }
    }

    private TouchConsumer getCurrentTouchConsumer(
            @HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) {
    private TouchConsumer getCurrentTouchConsumer(@HitTarget int downHitTarget,
            boolean forceToLauncher, RecentsAnimationState recentsAnimationStateToReuse,
            VelocityTracker tracker) {
        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);

        if (runningTaskInfo == null && !forceToLauncher) {
@@ -269,7 +272,8 @@ public class TouchInteractionService extends Service {
                    mOverviewComponentObserver.getOverviewIntent(),
                    mOverviewComponentObserver.getActivityControlHelper(), mMainThreadExecutor,
                    mBackgroundThreadChoreographer, downHitTarget, mOverviewCallbacks,
                    mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog);
                    mTaskOverlayFactory, mInputConsumer, tracker, mTouchInteractionLog,
                    recentsAnimationStateToReuse);
        }
    }

+58 −31
Original line number Diff line number Diff line
@@ -197,19 +197,21 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    };

    enum GestureEndTarget {
        HOME(1, STATE_SCALED_CONTROLLER_HOME, true, ContainerType.WORKSPACE),
        HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE),

        RECENTS(1, STATE_SCALED_CONTROLLER_RECENTS | STATE_CAPTURE_SCREENSHOT
                | STATE_SCREENSHOT_VIEW_SHOWN, true, ContainerType.TASKSWITCHER),
                | STATE_SCREENSHOT_VIEW_SHOWN, true, false, ContainerType.TASKSWITCHER),

        NEW_TASK(0, STATE_START_NEW_TASK, false, ContainerType.APP),
        NEW_TASK(0, STATE_START_NEW_TASK, false, true, ContainerType.APP),

        LAST_TASK(0, STATE_SCALED_CONTROLLER_LAST_TASK, false, ContainerType.APP);
        LAST_TASK(0, STATE_SCALED_CONTROLLER_LAST_TASK, false, false, ContainerType.APP);

        GestureEndTarget(float endShift, int endState, boolean isLauncher, int containerType) {
        GestureEndTarget(float endShift, int endState, boolean isLauncher, boolean canBeContinued,
                int containerType) {
            this.endShift = endShift;
            this.endState = endState;
            this.isLauncher = isLauncher;
            this.canBeContinued = canBeContinued;
            this.containerType = containerType;
        }

@@ -217,6 +219,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        public final float endShift;
        public final int endState;
        public final boolean isLauncher;
        public final boolean canBeContinued;
        public final int containerType;
    }

@@ -235,8 +238,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    private final ClipAnimationHelper.TransformParams mTransformParams;

    protected Runnable mGestureEndCallback;
    protected boolean mIsGoingToRecents;
    protected boolean mIsGoingToHome;
    protected GestureEndTarget mGestureEndTarget;
    private boolean mIsShelfPeeking;
    private DeviceProfile mDp;
    private int mTransitionDragLength;
@@ -247,6 +249,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    // visible.
    private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
    private boolean mDispatchedDownEvent;
    private boolean mContinuingLastGesture;
    // To avoid UI jump when gesture is started, we offset the animation by the threshold.
    private float mShiftAtGestureStart = 0;

@@ -302,7 +305,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    private Bundle mAssistData;

    WindowTransformSwipeHandler(int id, RunningTaskInfo runningTaskInfo, Context context,
            long touchTimeMs, ActivityControlHelper<T> controller,
            long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
            InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog) {
        this.id = id;
        mContext = context;
@@ -312,6 +315,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        mActivityControlHelper = controller;
        mActivityInitListener = mActivityControlHelper
                .createActivityInitListener(this::onActivityInit);
        mContinuingLastGesture = continuingLastGesture;
        mTouchInteractionLog = touchInteractionLog;
        mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
                this::createNewTouchProxyHandler);
@@ -480,7 +484,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        });
        mRecentsView.setEnableFreeScroll(false);
        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            if (!mBgLongSwipeMode && !mIsGoingToHome) {
            if (!mBgLongSwipeMode && mGestureEndTarget != HOME) {
                updateFinalShift();
            }
        });
@@ -538,6 +542,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    }

    private void setupRecentsViewUi() {
        if (mContinuingLastGesture) {
            return;
        }
        mRecentsView.setEnableDrawingLiveTile(false);
        mRecentsView.showTask(mRunningTaskId);
        mRecentsView.setRunningTaskHidden(true);
@@ -767,16 +774,13 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
            }
        }
        // Update insets of the adjacent tasks, as we might switch to them.
        // Update insets of the non-running 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);
            for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
                if (i != runningTaskIndex) {
                    mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value);
                }
            if (prevTaskView != null) {
                prevTaskView.setFullscreenProgress(1 - mCurrentShift.value);
            }
        }

@@ -788,7 +792,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
    }

    private void updateLauncherTransitionProgress() {
        if (mIsGoingToHome) {
        if (mGestureEndTarget == HOME) {
            return;
        }
        float progress = mCurrentShift.value;
@@ -909,10 +913,18 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        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
                && mRecentsView.getTaskViewAt(nextPage) != null;
        int nextPage = 0;
        int taskToLaunch = 0;
        final boolean goingToNewTask;
        if (mRecentsView != null) {
            nextPage = mRecentsView.getNextPage();
            final int lastTaskIndex = mRecentsView.getTaskViewCount() - 1;
            final int runningTaskIndex = mRecentsView.getRunningTaskIndex();
            taskToLaunch = nextPage <= lastTaskIndex ? nextPage : lastTaskIndex;
            goingToNewTask = mRecentsView != null && taskToLaunch != runningTaskIndex;
        } else {
            goingToNewTask = false;
        }
        final boolean reachedOverviewThreshold = currentShift >= MIN_PROGRESS_FOR_OVERVIEW;
        if (!isFling) {
            if (SWIPE_HOME.get()) {
@@ -973,6 +985,11 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            }
        }

        if (mRecentsView != null && !endTarget.isLauncher && taskToLaunch != nextPage) {
            // Scrolled to Clear all button, snap back to last task and launch it.
            mRecentsView.snapToPage(taskToLaunch, Math.toIntExact(duration), interpolator);
        }

        if (endTarget == HOME) {
            setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
            duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
@@ -989,11 +1006,6 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
            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));
            }
        }
        animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
    }
@@ -1028,11 +1040,10 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {

    private void animateToProgressInternal(float start, float end, long duration,
            Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
        mIsGoingToHome = target == HOME;
        mIsGoingToRecents = target == RECENTS;
        mGestureEndTarget = target;
        ActivityControlHelper.HomeAnimationFactory homeAnimFactory;
        Animator windowAnim;
        if (mIsGoingToHome) {
        if (mGestureEndTarget == HOME) {
            if (mActivity != null) {
                homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
            } else {
@@ -1071,7 +1082,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        long startMillis = SystemClock.uptimeMillis();
        // Always play the entire launcher animation when going home, since it is separate from
        // the animation that has been controlled thus far.
        final float finalStart = mIsGoingToHome ? 0 : start;
        final float finalStart = mGestureEndTarget == HOME ? 0 : start;
        executeOnUiThread(() -> {
            // Animate the launcher components at the same time as the window, always on UI thread.
            // Adjust start progress and duration in case we are on a different thread.
@@ -1144,6 +1155,14 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
                    .setSyncTransactionApplier(syncTransactionApplier);
            mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
        });
        anim.addListener(new AnimationSuccessListener() {
            @Override
            public void onAnimationSuccess(Animator animator) {
                if (mRecentsView != null) {
                    mRecentsView.post(mRecentsView::resetTaskVisuals);
                }
            }
        });
        return anim;
    }

@@ -1187,6 +1206,14 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
        }
    }

    public void cancel() {
        mCurrentShift.cancelAnimation();
        if (mLauncherTransitionController != null && mLauncherTransitionController
                .getAnimationPlayer().isStarted()) {
            mLauncherTransitionController.getAnimationPlayer().cancel();
        }
    }

    private void invalidateHandler() {
        mCurrentShift.finishAnimation();