From df51c1ada8622d78b7ed80b7e1c0f7438d3d1545 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 25 Apr 2023 04:27:52 +0000 Subject: [PATCH] Try to detect and handle delayed quickswitch task launch failure - The main issue arises when a task is successfully launched from overview, but the activity later finishes (ie. during resume) which prevents the usual logic of resetting Launcher to a good state (ie. it can get stuck in overview with a blank or empty snapshot) In this case, the Launch will "succeed" so that onTaskLaunchFailed is not called, but then also silently fail (launched task finishes and Launcher is again resumed) before Launcher stops, which does the usual resetting of the state back to normal state after quickswitching. This change checks for this case by listening for the activity and transition state, and in the case where Launcher has not been stopped or is resumed again after the transition finishes, returns the user to the default home state. This primarily only affects quickswitch for now, as other launch failures leave the user in a valid state (ie. overview) while this issue will leave the user in background state while quickswitching. Bug: 268448123 Test: Quickswitch to an activity that finishes when resumed Change-Id: I7d554f8fd521f7bc480dc06930ad91eeef0f1a1a --- .../TaskbarLauncherStateController.java | 4 + .../com/android/quickstep/RecentsModel.java | 3 + .../fallback/FallbackRecentsView.java | 2 +- .../util/TaskRemovedDuringLaunchListener.java | 105 ++++++++++++++++++ .../quickstep/views/DesktopTaskView.java | 2 +- .../quickstep/views/GroupedTaskView.java | 4 +- .../quickstep/views/LauncherRecentsView.java | 9 +- .../android/quickstep/views/RecentsView.java | 6 +- .../com/android/quickstep/views/TaskView.java | 32 +++++- 9 files changed, 154 insertions(+), 13 deletions(-) create mode 100644 quickstep/src/com/android/quickstep/util/TaskRemovedDuringLaunchListener.java diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index ed78e2d238..75cfd05d41 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -207,6 +207,10 @@ public class TaskbarLauncherStateController { com.android.launcher3.taskbar.Utilities.setOverviewDragState( mControllers, finalState.disallowTaskbarGlobalDrag(), disallowLongClick, finalState.allowTaskbarInitialSplitSelection()); + // LauncherTaskbarUIController depends on the state when checking whether + // to handle resume, so it should also be poked if current state changes + mLauncher.getTaskbarUIController().onLauncherResumedOrPaused( + mLauncher.hasBeenResumed()); } }; diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java index 913f08f41d..d798e62d02 100644 --- a/quickstep/src/com/android/quickstep/RecentsModel.java +++ b/quickstep/src/com/android/quickstep/RecentsModel.java @@ -145,6 +145,9 @@ public class RecentsModel implements IconChangeListener, TaskStackChangeListener * @param filter Returns true if GroupTask should be in the list of considerations */ public void isTaskRemoved(int taskId, Consumer callback, Predicate filter) { + // Invalidate the existing list before checking to ensure this reflects the current state in + // the system + mTaskList.onRecentTasksChanged(); mTaskList.getTasks(true /* loadKeysOnly */, (taskGroups) -> { for (GroupTask group : taskGroups) { if (group.containsTask(taskId)) { diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 4b1dd439f3..074aedd3c7 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -79,7 +79,7 @@ public class FallbackRecentsView extends RecentsView { + if (taskRemoved) { + ActiveGestureLog.INSTANCE.addLog("Launch failed, task (id=" + launchedTaskId + + ") finished mid transition"); + taskLaunchFailedCallback.run(); + } + }, (task) -> true /* filter */); + unregister(); + } + } +} diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java index 379722b87d..1cfaf14224 100644 --- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java +++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java @@ -321,7 +321,7 @@ public class DesktopTaskView extends TaskView { } @Override - public void launchTask(@NonNull Consumer callback, boolean freezeTaskList) { + public void launchTask(@NonNull Consumer callback, boolean isQuickswitch) { launchTasks(); callback.accept(true); } diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java index 5bfd0350b5..c6c84bd17d 100644 --- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java +++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java @@ -237,9 +237,9 @@ public class GroupedTaskView extends TaskView { } @Override - public void launchTask(@NonNull Consumer callback, boolean freezeTaskList) { + public void launchTask(@NonNull Consumer callback, boolean isQuickswitch) { getRecentsView().getSplitSelectController().launchTasks(mTask.key.id, mSecondaryTask.key.id, - SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, freezeTaskList, + SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, callback, isQuickswitch, getSplitRatio()); } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 8ff0e9b0c5..2008129de1 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -39,6 +39,7 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DesktopVisibilityController; +import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.statemanager.StateManager.StateListener; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.PendingSplitSelectInfo; @@ -79,9 +80,11 @@ public class LauncherRecentsView extends RecentsView callback) { - launchTask(callback, false /* freezeTaskList */); + launchTask(callback, false /* isQuickswitch */); } /** * Starts the task associated with this view without any animation */ - public void launchTask(@NonNull Consumer callback, boolean freezeTaskList) { + public void launchTask(@NonNull Consumer callback, boolean isQuickswitch) { if (mTask != null) { TestLogging.recordEvent( TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask); + final TaskRemovedDuringLaunchListener + failureListener = new TaskRemovedDuringLaunchListener(); + if (isQuickswitch) { + // We only listen for failures to launch in quickswitch because the during this + // gesture launcher is in the background state, vs other launches which are in + // the actual overview state + failureListener.register(mActivity, mTask.key.id, () -> { + notifyTaskLaunchFailed(TAG); + // Disable animations for now, as it is an edge case and the app usually covers + // launcher and also any state transition animation also gets clobbered by + // QuickstepTransitionManager.createWallpaperOpenAnimations when launcher + // shows again + getRecentsView().startHome(false /* animated */); + }); + } // Indicate success once the system has indicated that the transition has started - ActivityOptions opts = makeCustomAnimation(getContext(), 0, 0, - () -> callback.accept(true), MAIN_EXECUTOR.getHandler()); + ActivityOptions opts = ActivityOptions.makeCustomTaskAnimation(getContext(), 0, 0, + MAIN_EXECUTOR.getHandler(), + elapsedRealTime -> { + callback.accept(true); + }, + elapsedRealTime -> { + failureListener.onTransitionFinished(); + }); opts.setLaunchDisplayId( getDisplay() == null ? DEFAULT_DISPLAY : getDisplay().getDisplayId()); - if (freezeTaskList) { + if (isQuickswitch) { opts.setFreezeRecentTasksReordering(); } opts.setDisableStartingWindow(mSnapshotView.shouldShowSplashView());