diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java index 624b3dc779..5e77e0adee 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java @@ -68,7 +68,7 @@ final class AppToOverviewAnimationProvider imple * @param wasVisible true if it was visible before */ boolean onActivityReady(T activity, Boolean wasVisible) { - activity.getOverviewPanel().setCurrentTask(mTargetTaskId); + activity.getOverviewPanel().showCurrentTask(mTargetTaskId); AbstractFloatingView.closeAllOpenViews(activity, wasVisible); ActivityControlHelper.AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible, diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java index 7fc5d502f9..f8721744cb 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java @@ -382,7 +382,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC mSwipeSharedState.canGestureBeContinued = endTarget != null && endTarget.canBeContinued; mSwipeSharedState.goingToLauncher = endTarget != null && endTarget.isLauncher; if (mSwipeSharedState.canGestureBeContinued) { - mInteractionHandler.cancelCurrentAnimation(); + mInteractionHandler.cancelCurrentAnimation(mSwipeSharedState); } else { mInteractionHandler.reset(); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index ced9afab78..96d2dca971 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -59,6 +59,10 @@ public class RecentsAnimationWrapper { mInputProxySupplier = inputProxySupplier; } + public boolean hasTargets() { + return targetSet != null && targetSet.hasTargets(); + } + @UiThread public synchronized void setController(SwipeAnimationTargetSet targetSet) { Preconditions.assertUIThread(); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index f393387a7f..194d073359 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -38,6 +38,8 @@ public class SwipeSharedState implements SwipeAnimationListener { public boolean canGestureBeContinued; public boolean goingToLauncher; + public boolean recentsAnimationFinishInterrupted; + public int nextRunningTaskId = -1; public SwipeSharedState(OverviewComponentObserver overviewComponentObserver) { mOverviewComponentObserver = overviewComponentObserver; @@ -114,9 +116,22 @@ public class SwipeSharedState implements SwipeAnimationListener { } } + /** + * Called when a recents animation has finished, but was interrupted before the next task was + * launched. The given {@param runningTaskId} should be used as the running task for the + * continuing input consumer. + */ + public void setRecentsAnimationFinishInterrupted(int runningTaskId) { + recentsAnimationFinishInterrupted = true; + nextRunningTaskId = runningTaskId; + mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets(); + } + public void clearAllState() { clearListenerState(); canGestureBeContinued = false; + recentsAnimationFinishInterrupted = false; + nextRunningTaskId = -1; goingToLauncher = false; } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index f95f9c27e5..d0ea73a6fe 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -150,6 +150,7 @@ public final class TaskViewUtils { @Override public void onUpdate(float percent) { + // TODO: Take into account the current fullscreen progress for animating the insets params.setProgress(1 - percent); RectF taskBounds = inOutHelper.applyTransform(targetSet, params); if (!skipViewChanges) { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index b25865e640..61ae880cef 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -26,6 +26,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_N import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; import android.annotation.TargetApi; +import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.KeyguardManager; import android.app.Service; @@ -456,7 +457,7 @@ public class TouchInteractionService extends Service implements return new DeviceLockedInputConsumer(this); } - RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); + final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { mSwipeSharedState.clearAllState(); } @@ -465,8 +466,15 @@ public class TouchInteractionService extends Service implements mOverviewComponentObserver.getActivityControlHelper(); InputConsumer base; - if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) { + if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher + && !mSwipeSharedState.recentsAnimationFinishInterrupted) { base = InputConsumer.NO_OP; + } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) { + // If the finish animation was interrupted, then continue using the other activity input + // consumer but with the next task as the running task + RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); + info.id = mSwipeSharedState.nextRunningTaskId; + base = createOtherActivityInputConsumer(event, info); } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { base = OverviewInputConsumer.newInstance(activityControl, mInputMonitorCompat, false); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java index 0caef6fae7..936f0aad06 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -256,7 +256,9 @@ public class WindowTransformSwipeHandler private AnimationFactory mAnimationFactory = (t) -> { }; private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay(); + private boolean mCanceled; private boolean mWasLauncherAlreadyVisible; + private int mFinishingRecentsAnimationForNewTaskId = -1; private boolean mPassedOverviewThreshold; private boolean mGestureStarted; @@ -412,7 +414,6 @@ public class WindowTransformSwipeHandler mRecentsAnimationWrapper.runOnInit(() -> mRecentsAnimationWrapper.targetSet.addDependentTransactionApplier(applier)); }); - mRecentsView.setEnableFreeScroll(false); mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { if (mGestureEndTarget != HOME) { @@ -500,10 +501,7 @@ public class WindowTransformSwipeHandler if (mContinuingLastGesture) { return; } - mRecentsView.setEnableDrawingLiveTile(false); - mRecentsView.showTask(mRunningTaskId); - mRecentsView.setRunningTaskHidden(true); - mRecentsView.setRunningTaskIconScaledDown(true); + mRecentsView.onGestureAnimationStart(mRunningTaskId); } private void launcherFrameDrawn() { @@ -679,7 +677,7 @@ public class WindowTransformSwipeHandler int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex(); if (runningTaskIndex >= 0) { for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) { - if (i != runningTaskIndex) { + if (i != runningTaskIndex || !mRecentsAnimationWrapper.hasTargets()) { mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value); } } @@ -838,9 +836,15 @@ public class WindowTransformSwipeHandler Interpolator interpolator = DEACCEL; final boolean goingToNewTask; if (mRecentsView != null) { - final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); - final int taskToLaunch = mRecentsView.getNextPage(); - goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; + if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no running tasks, then we can assume that this is a continuation of + // the last gesture, but after the recents animation has finished + goingToNewTask = true; + } else { + final int runningTaskIndex = mRecentsView.getRunningTaskIndex(); + final int taskToLaunch = mRecentsView.getNextPage(); + goingToNewTask = runningTaskIndex >= 0 && taskToLaunch != runningTaskIndex; + } } else { goingToNewTask = false; } @@ -1124,14 +1128,22 @@ public class WindowTransformSwipeHandler mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */, true /* freezeTaskList */); } else { + int taskId = mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).getTask().key.id; + mFinishingRecentsAnimationForNewTaskId = taskId; mRecentsAnimationWrapper.finish(true /* toRecents */, () -> { - mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask( - false /* animate */, true /* freezeTaskList */); + if (!mCanceled) { + TaskView nextTask = mRecentsView.getTaskView(taskId); + if (nextTask != null) { + nextTask.launchTask(false /* animate */, true /* freezeTaskList */); + doLogGesture(NEW_TASK); + } + reset(); + } + mCanceled = false; + mFinishingRecentsAnimationForNewTaskId = -1; }); } TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", true); - doLogGesture(NEW_TASK); - reset(); } public void reset() { @@ -1142,12 +1154,26 @@ public class WindowTransformSwipeHandler * Cancels any running animation so that the active target can be overriden by a new swipe * handle (in case of quick switch). */ - public void cancelCurrentAnimation() { + public void cancelCurrentAnimation(SwipeSharedState sharedState) { + mCanceled = true; mCurrentShift.cancelAnimation(); if (mLauncherTransitionController != null && mLauncherTransitionController .getAnimationPlayer().isStarted()) { mLauncherTransitionController.getAnimationPlayer().cancel(); } + + if (mFinishingRecentsAnimationForNewTaskId != -1) { + // If we are canceling mid-starting a new task, switch to the screenshot since the + // recents animation has finished + switchToScreenshot(); + TaskView newRunningTaskView = mRecentsView.getTaskView( + mFinishingRecentsAnimationForNewTaskId); + int newRunningTaskId = newRunningTaskView != null + ? newRunningTaskView.getTask().key.id + : -1; + mRecentsView.setCurrentTask(newRunningTaskId); + sharedState.setRecentsAnimationFinishInterrupted(newRunningTaskId); + } } private void invalidateHandler() { @@ -1169,11 +1195,7 @@ public class WindowTransformSwipeHandler mLauncherTransitionController = null; } - mRecentsView.setEnableFreeScroll(true); - mRecentsView.setRunningTaskIconScaledDown(false); - mRecentsView.setOnScrollChangeListener(null); - mRecentsView.setRunningTaskHidden(false); - mRecentsView.setEnableDrawingLiveTile(true); + mRecentsView.onGestureAnimationEnd(); mActivity.getRootView().setOnApplyWindowInsetsListener(null); mActivity.getRootView().getOverlay().remove(mLiveTileOverlay); @@ -1194,6 +1216,9 @@ public class WindowTransformSwipeHandler private void switchToScreenshot() { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); + } else if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no targets, then we don't need to capture anything + setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); } else { boolean finishTransitionPosted = false; SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController(); @@ -1241,6 +1266,9 @@ public class WindowTransformSwipeHandler private void finishCurrentTransitionToRecents() { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); + } else if (!mRecentsAnimationWrapper.hasTargets()) { + // If there are no targets, then there is nothing to finish + setStateOnUiThread(STATE_CURRENT_TASK_FINISHED); } else { synchronized (mRecentsAnimationWrapper) { mRecentsAnimationWrapper.finish(true /* toRecents */, @@ -1267,10 +1295,7 @@ public class WindowTransformSwipeHandler } mActivityControlHelper.onSwipeUpComplete(mActivity); mRecentsAnimationWrapper.setCancelWithDeferredScreenshot(true); - - // Animate the first icon. - mRecentsView.animateUpRunningTaskIconScale(mLiveTileOverlay.cancelIconAnimation()); - mRecentsView.setSwipeDownShouldLaunchApp(true); + mRecentsView.onSwipeUpAnimationSuccess(); RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index 83973fa7ec..f5a9e8a058 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -53,6 +53,20 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { this.mOnFinishListener = onFinishListener; } + public boolean hasTargets() { + return unfilteredApps.length != 0; + } + + /** + * Clones the target set without any actual targets. Used only when continuing a gesture after + * the actual recents animation has finished. + */ + public SwipeAnimationTargetSet cloneWithoutTargets() { + return new SwipeAnimationTargetSet(controller, new RemoteAnimationTargetCompat[0], + homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen, + mOnFinishListener); + } + public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) { mOnFinishListener.accept(this); BACKGROUND_EXECUTOR.execute(() -> { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index b5f90a5550..525ead836a 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -744,9 +744,7 @@ public abstract class RecentsView extends PagedView impl public abstract void startHome(); public void reset() { - setRunningTaskViewShowScreenshot(false); - mRunningTaskId = -1; - mRunningTaskTileHidden = false; + setCurrentTask(-1); mIgnoreResetTaskId = -1; mTaskListChangeId = -1; @@ -757,6 +755,15 @@ public abstract class RecentsView extends PagedView impl setCurrentPage(0); } + public @Nullable TaskView getRunningTaskView() { + return getTaskView(mRunningTaskId); + } + + public int getRunningTaskIndex() { + TaskView tv = getRunningTaskView(); + return tv == null ? -1 : indexOfChild(tv); + } + /** * Reloads the view if anything in recents changed. */ @@ -767,14 +774,50 @@ public abstract class RecentsView extends PagedView impl } /** - * Ensures that the first task in the view represents {@param task} and reloads the view - * if needed. This allows the swipe-up gesture to assume that the first tile always - * corresponds to the correct task. - * All subsequent calls to reload will keep the task as the first item until {@link #reset()} - * is called. - * Also scrolls the view to this task + * Called when a gesture from an app is starting. */ - public void showTask(int runningTaskId) { + public void onGestureAnimationStart(int runningTaskId) { + // This needs to be called before the other states are set since it can create the task view + showCurrentTask(runningTaskId); + setEnableFreeScroll(false); + setEnableDrawingLiveTile(false); + setRunningTaskHidden(true); + setRunningTaskIconScaledDown(true); + } + + /** + * Called only when a swipe-up gesture from an app has completed. Only called after + * {@link #onGestureAnimationStart} and {@link #onGestureAnimationEnd()}. + */ + public void onSwipeUpAnimationSuccess() { + if (getRunningTaskView() != null) { + float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() + ? mLiveTileOverlay.cancelIconAnimation() + : 0f; + animateUpRunningTaskIconScale(startProgress); + } + setSwipeDownShouldLaunchApp(true); + } + + /** + * Called when a gesture from an app has finished. + */ + public void onGestureAnimationEnd() { + setEnableFreeScroll(true); + setEnableDrawingLiveTile(true); + setOnScrollChangeListener(null); + setRunningTaskViewShowScreenshot(true); + setRunningTaskHidden(false); + animateUpRunningTaskIconScale(); + } + + /** + * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}. + * + * All subsequent calls to reload will keep the task as the first item until {@link #reset()} + * is called. Also scrolls the view to this task. + */ + public void showCurrentTask(int runningTaskId) { if (getChildCount() == 0) { // Add an empty view for now until the task plan is loaded and applied final TaskView taskView = mTaskViewPool.getView(); @@ -789,16 +832,33 @@ public abstract class RecentsView extends PagedView impl new ComponentName("", ""), false); taskView.bind(mTmpRunningTask); } + + boolean runningTaskTileHidden = mRunningTaskTileHidden; setCurrentTask(runningTaskId); + setCurrentPage(getRunningTaskIndex()); + setRunningTaskViewShowScreenshot(false); + setRunningTaskHidden(runningTaskTileHidden); + + // Reload the task list + mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); } - public @Nullable TaskView getRunningTaskView() { - return getTaskView(mRunningTaskId); - } + /** + * Sets the running task id, cleaning up the old running task if necessary. + * @param runningTaskId + */ + public void setCurrentTask(int runningTaskId) { + if (mRunningTaskId == runningTaskId) { + return; + } - public int getRunningTaskIndex() { - TaskView tv = getRunningTaskView(); - return tv == null ? -1 : indexOfChild(tv); + if (mRunningTaskId != -1) { + // Reset the state on the old running task view + setRunningTaskIconScaledDown(false); + setRunningTaskViewShowScreenshot(true); + setRunningTaskHidden(false); + } + mRunningTaskId = runningTaskId; } /** @@ -812,27 +872,6 @@ public abstract class RecentsView extends PagedView impl } } - /** - * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile. - */ - public void setCurrentTask(int runningTaskId) { - boolean runningTaskTileHidden = mRunningTaskTileHidden; - boolean runningTaskIconScaledDown = mRunningTaskIconScaledDown; - - setRunningTaskIconScaledDown(false); - setRunningTaskHidden(false); - setRunningTaskViewShowScreenshot(true); - mRunningTaskId = runningTaskId; - setRunningTaskViewShowScreenshot(false); - setRunningTaskIconScaledDown(runningTaskIconScaledDown); - setRunningTaskHidden(runningTaskTileHidden); - - setCurrentPage(getRunningTaskIndex()); - - // Load the tasks (if the loading is already - mTaskListChangeId = mModel.getTasks(this::applyLoadPlan); - } - private void setRunningTaskViewShowScreenshot(boolean showScreenshot) { if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { TaskView runningTaskView = getRunningTaskView(); @@ -1520,7 +1559,9 @@ public abstract class RecentsView extends PagedView impl public abstract boolean shouldUseMultiWindowTaskSizeStrategy(); protected void onTaskLaunched(boolean success) { - resetTaskVisuals(); + if (success) { + resetTaskVisuals(); + } } @Override diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index b6404301dd..80e17c9ff1 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1072,6 +1072,10 @@ public abstract class PagedView extends ViewGrou public void setEnableFreeScroll(boolean freeScroll) { + if (mFreeScroll == freeScroll) { + return; + } + boolean wasFreeScroll = mFreeScroll; mFreeScroll = freeScroll;