diff options
author | Tony <twickham@google.com> | 2019-02-01 22:21:11 -0500 |
---|---|---|
committer | Tony <twickham@google.com> | 2019-02-05 15:47:13 -0500 |
commit | f98121939d99a9dc3d20f9f9ca4e8c65a37cb5ff (patch) | |
tree | 41c2dc30142bb3f7d9f79772a2d629749a9f9765 | |
parent | 51745853cb5eaf7c44c08061e7e8de4d4345ac7e (diff) | |
download | android_packages_apps_Trebuchet-f98121939d99a9dc3d20f9f9ca4e8c65a37cb5ff.tar.gz android_packages_apps_Trebuchet-f98121939d99a9dc3d20f9f9ca4e8c65a37cb5ff.tar.bz2 android_packages_apps_Trebuchet-f98121939d99a9dc3d20f9f9ca4e8c65a37cb5ff.zip |
Allow for continuous scrolling to previous tasks
Starting a new touch interaction on the nav bar while going to a new task
cancels that animation to allow for continuous scrolling.
Specifically, the new interaction still creates a new touch consumer and
WindowTransformSwipeHandler, but reuses the previous RecentsAnimationState
in order to defer finishing it.
Bug: 111926330
Change-Id: Ia4f5f8dbb2b3ae70791676f1e3e5ce84deb22f74
5 files changed, 138 insertions, 57 deletions
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java index 5996df78c..1633485f4 100644 --- a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java @@ -96,6 +96,11 @@ public class DeferredTouchConsumer implements TouchConsumer { } @Override + public OtherActivityTouchConsumer.RecentsAnimationState getRecentsAnimationStateToReuse() { + return mTarget.getRecentsAnimationStateToReuse(); + } + + @Override public boolean deferNextEventToMainThread() { // If our target is still null, defer the next target as well TouchConsumer target = mTarget; diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index a7f5f0b93..13a934140 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -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); } } @@ -444,25 +465,33 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC } @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); + } } } diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java index 057a2ee1d..1d5ffe76e 100644 --- a/quickstep/src/com/android/quickstep/TouchConsumer.java +++ b/quickstep/src/com/android/quickstep/TouchConsumer.java @@ -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() {} } diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index d5de3ff87..7da7bcd75 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -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); } } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index c60752a1e..f9495a4d2 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -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); - } - if (prevTaskView != null) { - prevTaskView.setFullscreenProgress(1 - mCurrentShift.value); + for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) { + if (i != runningTaskIndex) { + mRecentsView.getTaskViewAt(i).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(); |