diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 33a9f48a82d693fd3b950e7badf4d7048bfd4524..dfea5159eb506cc954d5ccb3854403ca352b42d8 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -144,6 +144,7 @@ import com.android.quickstep.util.StaggeredWorkspaceAnim; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; import com.android.quickstep.util.SurfaceTransactionApplier; +import com.android.quickstep.util.TaskRestartedDuringLaunchListener; import com.android.quickstep.util.WorkspaceRevealAnim; import com.android.quickstep.views.FloatingWidgetView; import com.android.quickstep.views.RecentsView; @@ -299,6 +300,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener boolean fromRecents = isLaunchingFromRecents(v, null /* targets */); RunnableList onEndCallback = new RunnableList(); + // Handle the case where an already visible task is launched which results in no transition + TaskRestartedDuringLaunchListener restartedListener = + new TaskRestartedDuringLaunchListener(); + restartedListener.register(onEndCallback::executeAllAndDestroy); + onEndCallback.add(restartedListener::unregister); + mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback); ItemInfo tag = (ItemInfo) v.getTag(); if (tag != null && tag.shouldUseBackgroundAnimation()) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 658bf2d865a8d60378bcbba9fa26534dde099621..2f13c5de44d026a5199adcfcfb5fe94ea6159a00 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -345,11 +345,13 @@ public class QuickstepLauncher extends Launcher { @Override public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) { - // Only pause is taskbar controller is not present + // Only pause is taskbar controller is not present until the transition (if it exists) ends mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null); RunnableList result = super.startActivitySafely(v, intent, item); - if (getTaskbarUIController() == null && result == null) { - mHotseatPredictionController.setPauseUIUpdate(false); + if (result == null) { + if (getTaskbarUIController() == null) { + mHotseatPredictionController.setPauseUIUpdate(false); + } } else { result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false)); } diff --git a/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java new file mode 100644 index 0000000000000000000000000000000000000000..91e8376990102492ea1a5fc4c35ec72c44b80154 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.util; + +import static android.app.ActivityTaskManager.INVALID_TASK_ID; + +import android.app.Activity; +import android.app.ActivityManager; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter; +import com.android.quickstep.RecentsModel; +import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.TaskStackChangeListeners; + +/** + * This class tracks the failure of a task launch through the Launcher.startActivitySafely() call, + * in an edge case in which a task may already be visible on screen (ie. in PIP) and no transition + * will be run in WM, which results in expected callbacks to not be processed. + * + * We transiently register a task stack listener during a task launch and if the restart signal is + * received, then the registered callback will be notified. + */ +public class TaskRestartedDuringLaunchListener implements TaskStackChangeListener { + + private static final String TAG = "TaskRestartedDuringLaunchListener"; + + private @NonNull Runnable mTaskRestartedCallback = null; + + /** + * Registers a failure listener callback if it detects a scenario in which an app launch + * resulted in an already existing task to be "restarted". + */ + public void register(@NonNull Runnable taskRestartedCallback) { + TaskStackChangeListeners.getInstance().registerTaskStackListener(this); + mTaskRestartedCallback = taskRestartedCallback; + } + + /** + * Unregisters the failure listener. + */ + public void unregister() { + TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this); + mTaskRestartedCallback = null; + } + + @Override + public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, + boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { + if (wasVisible) { + Log.d(TAG, "Detected activity restart during launch for task=" + task.taskId); + mTaskRestartedCallback.run(); + unregister(); + } + } +}