From b6ecf5889e25965508edd333857d61c64ec39d3f Mon Sep 17 00:00:00 2001 From: Ahmed Fakhry Date: Tue, 28 Jan 2025 02:55:59 +0000 Subject: [PATCH] Remove all external usages of `GroupTask.task1/2` from Launcher3 See go/refactor-group-task for details. This CL removes all the usages of `task1` and `task2` from `GroupTask` in Launcher3. Follow-up CLs will remove it from NexusLauncher and remove the fields altogether. Bug: 388593902 Test: m Flag: EXEMPT pure refactor with no behavior change. Change-Id: I902c8135b3a0aae95acf25267b3bcbf286bd4e7d --- .../KeyboardQuickSwitchController.java | 3 +- .../taskbar/KeyboardQuickSwitchView.java | 22 ++++-- .../KeyboardQuickSwitchViewController.java | 8 +- .../taskbar/LauncherTaskbarUIController.java | 6 +- .../taskbar/TaskbarActivityContext.java | 27 ++++--- .../taskbar/TaskbarDragController.java | 6 +- .../taskbar/TaskbarRecentAppsController.kt | 64 +++++++++------- .../taskbar/TaskbarUIController.java | 4 +- .../launcher3/taskbar/TaskbarView.java | 22 ++++-- .../taskbar/TaskbarViewController.java | 5 +- .../uioverrides/QuickstepLauncher.java | 24 ++---- .../com/android/quickstep/util/DesktopTask.kt | 2 - .../com/android/quickstep/util/GroupTask.kt | 21 +++++- .../util/SplitSelectStateController.java | 17 +++-- .../android/quickstep/views/RecentsView.java | 30 ++++---- .../util/SplitSelectStateControllerTest.kt | 74 +++++++++---------- 16 files changed, 191 insertions(+), 144 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java index 3736e6dc03..23065b5f60 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java @@ -233,7 +233,8 @@ public final class KeyboardQuickSwitchController implements } private boolean shouldExcludeTask(GroupTask task, Set taskIdsToExclude) { - return Flags.taskbarOverflow() && taskIdsToExclude.contains(task.task1.key.id); + return Flags.taskbarOverflow() && task.getTasks().stream().anyMatch( + t -> taskIdsToExclude.contains(t.key.id)); } private void processLoadedTasks(List tasks, Set taskIdsToExclude) { diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java index 306443ea31..73f9bea668 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java @@ -51,6 +51,9 @@ import com.android.launcher3.anim.AnimatedFloat; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SingleTask; +import com.android.quickstep.util.SplitTask; +import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import java.util.HashMap; @@ -255,14 +258,21 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { layoutInflater, previousTaskView); - final boolean firstTaskIsLeftTopTask = - groupTask.mSplitBounds == null - || groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id - || groupTask.task2 == null; + Task task1; + Task task2; + if (groupTask instanceof SplitTask splitTask) { + task1 = splitTask.getTopLeftTask(); + task2 = splitTask.getBottomRightTask(); + } else if (groupTask instanceof SingleTask singleTask) { + task1 = singleTask.getTask(); + task2 = null; + } else { + continue; + } currentTaskView.setThumbnailsForSplitTasks( - firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2, - firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1, + task1, + task2, updateTasks ? mViewCallbacks::updateThumbnailInBackground : null, updateTasks ? mViewCallbacks::updateIconInBackground : null, groupTask.mSplitBounds); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java index 8cb43d285c..5af7ff8933 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java @@ -44,6 +44,7 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.views.BaseDragLayer; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SingleTask; import com.android.quickstep.util.SlideInRemoteTransition; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.ThumbnailData; @@ -281,9 +282,10 @@ public class KeyboardQuickSwitchViewController { return -1; } RemoteTransition remoteTransition = slideInTransition; - if (mOnDesktop - && mControllers.taskbarActivityContext.canUnminimizeDesktopTask(task.task1.key.id) - ) { + boolean canUnminimizeDesktopTask = task instanceof SingleTask singleTask + && mControllers.taskbarActivityContext.canUnminimizeDesktopTask( + singleTask.getTask().key.id); + if (mOnDesktop && canUnminimizeDesktopTask) { // This app is being unminimized - use our own transition runner. remoteTransition = new RemoteTransition( new DesktopAppLaunchTransition( diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 5a8fba68ec..4143157637 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -49,7 +49,7 @@ import com.android.launcher3.util.OnboardingPrefs; import com.android.quickstep.HomeVisibilityState; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.SystemUiProxy; -import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SplitTask; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; @@ -480,8 +480,8 @@ public class LauncherTaskbarUIController extends TaskbarUIController { @Override public void launchSplitTasks( - @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { - mLauncher.launchSplitTasks(groupTask, remoteTransition); + @NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) { + mLauncher.launchSplitTasks(splitTask, remoteTransition); } @Override diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index e8a0c45da6..6bfbfddc56 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -155,6 +155,8 @@ import com.android.quickstep.RecentsModel; import com.android.quickstep.SystemUiProxy; import com.android.quickstep.util.DesktopTask; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SingleTask; +import com.android.quickstep.util.SplitTask; import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -1293,11 +1295,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { mControllers.keyboardQuickSwitchController.closeQuickSwitchView(false); - if (tag instanceof GroupTask groupTask) { + // TODO: b/316004172, b/343289567: Handle `DesktopTask` and `SplitTask`. + if (tag instanceof SingleTask singleTask) { RemoteTransition remoteTransition = - (areDesktopTasksVisible() && canUnminimizeDesktopTask(groupTask.task1.key.id)) + (areDesktopTasksVisible() && canUnminimizeDesktopTask( + singleTask.getTask().key.id)) ? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE, - Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON) + Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON) : null; if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) { RunnableList runnableList = recents.launchRunningDesktopTaskView(); @@ -1305,12 +1309,12 @@ public class TaskbarActivityContext extends BaseTaskbarContext { // launch. if (runnableList != null) { runnableList.add(() -> UI_HELPER_EXECUTOR.execute( - () -> handleGroupTaskLaunch(groupTask, remoteTransition, + () -> handleGroupTaskLaunch(singleTask, remoteTransition, areDesktopTasksVisible(), DesktopTaskToFrontReason.TASKBAR_TAP))); } } else { - handleGroupTaskLaunch(groupTask, remoteTransition, areDesktopTasksVisible(), + handleGroupTaskLaunch(singleTask, remoteTransition, areDesktopTasksVisible(), DesktopTaskToFrontReason.TASKBAR_TAP); } mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true); @@ -1480,13 +1484,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { remoteTransition)); return; } - if (onDesktop) { - boolean useRemoteTransition = canUnminimizeDesktopTask(task.task1.key.id); + if (onDesktop && task instanceof SingleTask singleTask) { + boolean useRemoteTransition = canUnminimizeDesktopTask(singleTask.getTask().key.id); UI_HELPER_EXECUTOR.execute(() -> { if (onStartCallback != null) { onStartCallback.run(); } - SystemUiProxy.INSTANCE.get(this).showDesktopApp(task.task1.key.id, + SystemUiProxy.INSTANCE.get(this).showDesktopApp(singleTask.getTask().key.id, useRemoteTransition ? remoteTransition : null, toFrontReason); if (onFinishCallback != null) { onFinishCallback.run(); @@ -1494,18 +1498,19 @@ public class TaskbarActivityContext extends BaseTaskbarContext { }); return; } - if (task.task2 == null) { + if (task instanceof SingleTask singleTask) { UI_HELPER_EXECUTOR.execute(() -> { ActivityOptions activityOptions = makeDefaultActivityOptions(SPLASH_SCREEN_STYLE_UNDEFINED).options; activityOptions.setRemoteTransition(remoteTransition); ActivityManagerWrapper.getInstance().startActivityFromRecents( - task.task1.key, activityOptions); + singleTask.getTask().key, activityOptions); }); return; } - mControllers.uiController.launchSplitTasks(task, remoteTransition); + assert task instanceof SplitTask; + mControllers.uiController.launchSplitTasks((SplitTask) task, remoteTransition); } /** Returns whether the given task is minimized and can be unminimized. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java index a9e8d6dc7b..3a83db2959 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java @@ -80,9 +80,9 @@ import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.IntSet; import com.android.launcher3.util.ItemInfoMatcher; import com.android.launcher3.views.BubbleTextHolder; -import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LogUtils; import com.android.quickstep.util.MultiValueUpdateListener; +import com.android.quickstep.util.SingleTask; import com.android.systemui.shared.recents.model.Task; import com.android.wm.shell.shared.draganddrop.DragAndDropConstants; @@ -433,8 +433,8 @@ public class TaskbarDragController extends DragController im null, item.user)); } intent.putExtra(Intent.EXTRA_USER, item.user); - } else if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) { - Task task = groupTask.task1; + } else if (tag instanceof SingleTask singleTask) { + Task task = singleTask.getTask(); clipDescription = new ClipDescription(task.titleDescription, new String[] { ClipDescription.MIMETYPE_APPLICATION_TASK diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt index 6047999db2..417ef7edef 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentAppsController.kt @@ -76,12 +76,14 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re var shownTasks: List = emptyList() private set + val shownTaskIds: List + get() = shownTasks.flatMap { shownTask -> shownTask.tasks }.map { it.key.id } + /** - * The task-state of an app, i.e. whether the app has a task and what state - * that task is in. + * The task-state of an app, i.e. whether the app has a task and what state that task is in. * - * @property taskId The ID of the task if one exists (i.e. if the state is - * RUNNING or MINIMIZED), null otherwise (NOT_RUNNING). + * @property taskId The ID of the task if one exists (i.e. if the state is RUNNING or + * MINIMIZED), null otherwise (NOT_RUNNING). */ data class TaskState(val runningAppState: RunningAppState, val taskId: Int? = null) @@ -214,9 +216,9 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re return shownHotseatItems.toTypedArray() } - private fun getOrderedAndWrappedDesktopTasks(): List { + private fun getOrderedAndWrappedDesktopTasks(): List { val tasks = desktopTask?.tasks ?: emptyList() - // Kind of hacky, we wrap each single task in the Desktop as a GroupTask. + // We wrap each task in the Desktop as a `SingleTask`. val orderFromId = orderedRunningTaskIds.withIndex().associate { (index, id) -> id to index } val sortedTasks = tasks.sortedWith(compareBy(nullsLast()) { orderFromId[it.key.id] }) return sortedTasks.map { SingleTask(it) } @@ -286,7 +288,7 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re } private fun updateOrderedRunningTaskIds(): MutableList { - val desktopTasksAsList = getOrderedAndWrappedDesktopTasks().flatMap { it.tasks } + val desktopTasksAsList = getOrderedAndWrappedDesktopTasks().map { it.task } val desktopTaskIds = desktopTasksAsList.map { it.key.id } var newOrder = orderedRunningTaskIds @@ -311,42 +313,43 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re val newShownTasks = if (Flags.enableMultiInstanceMenuTaskbar()) { val deduplicatedDesktopTasks = - desktopTasks.distinctBy { Pair(it.task1.key.packageName, it.task1.key.userId) } + desktopTasks.distinctBy { Pair(it.task.key.packageName, it.task.key.userId) } shownTasks .filter { - !it.supportsMultipleTasks() && - it.task1.key.id in deduplicatedDesktopTasks.map { it.task1.key.id } + it is SingleTask && + it.task.key.id in deduplicatedDesktopTasks.map { it.task.key.id } } .toMutableList() .apply { addAll( deduplicatedDesktopTasks.filter { currentTask -> - val currentTaskKey = currentTask.task1.key - currentTaskKey.id !in shownTasks.map { it.task1.key.id } && + val currentTaskKey = currentTask.task.key + currentTaskKey.id !in shownTaskIds && shownHotseatItems.none { hotseatItem -> - hotseatItem.targetPackage == currentTaskKey.packageName && - hotseatItem.user.identifier == currentTaskKey.userId + currentTask.containsPackage( + hotseatItem.targetPackage, + hotseatItem.user.identifier, + ) } } ) } } else { - val desktopTaskIds = desktopTasks.map { it.task1.key.id } + val desktopTaskIds = desktopTasks.map { it.task.key.id } val shownHotseatItemTaskIds = shownHotseatItems.mapNotNull { it as? TaskItemInfo }.map { it.taskId } shownTasks - .filter { !it.supportsMultipleTasks() && it.task1.key.id in desktopTaskIds } + .filter { it is SingleTask && it.task.key.id in desktopTaskIds } .toMutableList() .apply { addAll( desktopTasks.filter { desktopTask -> - desktopTask.task1.key.id !in - shownTasks.map { shownTask -> shownTask.task1.key.id } + desktopTask.task.key.id !in shownTaskIds } ) - removeAll { it.task1.key.id in shownHotseatItemTaskIds } + removeAll { it is SingleTask && it.task.key.id in shownHotseatItemTaskIds } } } @@ -371,21 +374,28 @@ class TaskbarRecentAppsController(context: Context, private val recentsModel: Re groupTasks: List, shownHotseatItems: List, ): List { + // TODO: b/393476333 - Check the behavior of the Taskbar recents section when empty desks + // become supported. return if (Flags.enableMultiInstanceMenuTaskbar()) { groupTasks.filter { groupTask -> - val taskKey = groupTask.task1.key // Keep tasks that are group tasks or unique package name/user combinations - groupTask.hasMultipleTasks() || - shownHotseatItems.none { - it.targetPackage == taskKey.packageName && - it.user.identifier == taskKey.userId - } + when (groupTask) { + is SingleTask -> + shownHotseatItems.none { + groupTask.containsPackage(it.targetPackage, it.user.identifier) + } + + else -> true + } } } else { val hotseatPackages = shownHotseatItems.map { it.targetPackage } groupTasks.filter { groupTask -> - groupTask.hasMultipleTasks() || - !hotseatPackages.contains(groupTask.task1.key.packageName) + when (groupTask) { + is SingleTask -> hotseatPackages.none { groupTask.containsPackage(it) } + + else -> true + } } } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index f29f95d347..e5d642d9cb 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -39,7 +39,7 @@ import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.taskbar.bubbles.BubbleBarController; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.SplitConfigurationOptions; -import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SplitTask; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskContainer; import com.android.quickstep.views.TaskView; @@ -332,7 +332,7 @@ public class TaskbarUIController implements BubbleBarController.BubbleBarLocatio * Launches the given task in split-screen. */ public void launchSplitTasks( - @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { } + @NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) { } /** * Returns the matching view (if any) in the taskbar. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java index e4e97e5a75..c7f33e94e0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java @@ -70,6 +70,7 @@ import com.android.launcher3.util.LauncherBindableItemsContainer; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ActivityContext; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SingleTask; import com.android.quickstep.views.TaskViewType; import com.android.systemui.shared.recents.model.Task; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; @@ -657,9 +658,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar final Set recentTasksSet = new ArraySet<>(recentTasks); for (GroupTask task : recentTasks) { if (mTaskbarOverflowView != null && overflownTasks != null - && overflownTasks.size() < itemsToAddToOverflow) { + && overflownTasks.size() < itemsToAddToOverflow + && task instanceof SingleTask singleTask) { // TODO(b/343289567 and b/316004172): support app pairs and desktop mode. - overflownTasks.add(task.task1); + overflownTasks.add(singleTask.getTask()); if (overflownTasks.size() == itemsToAddToOverflow) { mTaskbarOverflowView.setItems(overflownTasks); } @@ -733,18 +735,22 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar && tagClass.isInstance(getChildAt(mNextViewIndex).getTag()); } - /** Binds the GroupTask to the BubbleTextView to be ready to present to the user. */ + /** Binds the SingleTask to the BubbleTextView to be ready to present to the user. */ public void applyGroupTaskToBubbleTextView(BubbleTextView btv, GroupTask groupTask) { - // TODO(b/343289567): support app pairs. - Task task1 = groupTask.task1; + if (!(groupTask instanceof SingleTask singleTask)) { + // TODO(b/343289567 and b/316004172): support app pairs and desktop mode. + return; + } + + Task task = singleTask.getTask(); // TODO(b/344038728): use FastBitmapDrawable instead of Drawable, to get disabled state // while dragging. - Drawable taskIcon = groupTask.task1.icon; + Drawable taskIcon = task.icon; if (taskIcon != null) { taskIcon = taskIcon.getConstantState().newDrawable().mutate(); } - btv.applyIconAndLabel(taskIcon, task1.titleDescription); - btv.setTag(groupTask); + btv.applyIconAndLabel(taskIcon, task.titleDescription); + btv.setTag(singleTask); } /** diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 0f05887d45..cbc5d3d63b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -92,6 +92,7 @@ import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.util.MultiTranslateDelegate; import com.android.launcher3.util.MultiValueAlpha; import com.android.quickstep.util.GroupTask; +import com.android.quickstep.util.SingleTask; import com.android.systemui.shared.recents.model.Task; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; @@ -739,9 +740,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar return mControllers.taskbarRecentAppsController.getRunningAppState( itemInfo.getTaskId()); } - if (tag instanceof GroupTask groupTask && !groupTask.hasMultipleTasks()) { + if (tag instanceof SingleTask singleTask) { return mControllers.taskbarRecentAppsController.getRunningAppState( - groupTask.task1.key.id); + singleTask.getTask().key.id); } return BubbleTextView.RunningAppState.NOT_RUNNING; } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index f672840fcd..c880aa9412 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -178,10 +178,10 @@ import com.android.quickstep.TaskUtils; import com.android.quickstep.TouchInteractionService.TISBinder; import com.android.quickstep.util.ActiveGestureProtoLogProxy; import com.android.quickstep.util.AsyncClockEventDelegate; -import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LauncherUnfoldAnimationController; import com.android.quickstep.util.QuickstepOnboardingPrefs; import com.android.quickstep.util.SplitSelectStateController; +import com.android.quickstep.util.SplitTask; import com.android.quickstep.util.SplitToWorkspaceController; import com.android.quickstep.util.SplitWithKeyboardShortcutController; import com.android.quickstep.util.TISBindHelper; @@ -1386,33 +1386,23 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, } /** - * Launches the given {@link GroupTask} in splitscreen. + * Launches the given {@link SplitTask} in splitscreen. */ public void launchSplitTasks( - @NonNull GroupTask groupTask, @Nullable RemoteTransition remoteTransition) { - // SplitBounds can be null if coming from Taskbar launch. - final boolean firstTaskIsLeftTopTask = isFirstTaskLeftTopTask(groupTask); - // task2 should never be null when calling this method. Allow a crash to catch invalid calls - Task task1 = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2; - Task task2 = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1; + @NonNull SplitTask splitTask, @Nullable RemoteTransition remoteTransition) { mSplitSelectStateController.launchExistingSplitPair( null /* launchingTaskView */, - task1.key.id, - task2.key.id, + splitTask.getTopLeftTask().key.id, + splitTask.getBottomRightTask().key.id, SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT, /* callback= */ success -> mSplitSelectStateController.resetState(), /* freezeTaskList= */ false, - groupTask.mSplitBounds == null + splitTask.mSplitBounds == null ? SNAP_TO_2_50_50 - : groupTask.mSplitBounds.snapPosition, + : splitTask.mSplitBounds.snapPosition, remoteTransition); } - private static boolean isFirstTaskLeftTopTask(@NonNull GroupTask groupTask) { - return groupTask.mSplitBounds == null - || groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id; - } - /** * Launches two apps as an app pair. */ diff --git a/quickstep/src/com/android/quickstep/util/DesktopTask.kt b/quickstep/src/com/android/quickstep/util/DesktopTask.kt index 5463cf7169..066918571b 100644 --- a/quickstep/src/com/android/quickstep/util/DesktopTask.kt +++ b/quickstep/src/com/android/quickstep/util/DesktopTask.kt @@ -27,8 +27,6 @@ class DesktopTask(override val tasks: List) : override fun containsTask(taskId: Int) = tasks.any { it.key.id == taskId } - override fun hasMultipleTasks() = tasks.size > 1 - override fun supportsMultipleTasks() = true override fun copy() = DesktopTask(tasks) diff --git a/quickstep/src/com/android/quickstep/util/GroupTask.kt b/quickstep/src/com/android/quickstep/util/GroupTask.kt index d5bbcd3d0e..8237d2e3f2 100644 --- a/quickstep/src/com/android/quickstep/util/GroupTask.kt +++ b/quickstep/src/com/android/quickstep/util/GroupTask.kt @@ -50,9 +50,16 @@ constructor( /** * Returns true if a task in this group has a package name that matches the given `packageName`. */ - fun containsPackage(packageName: String) = tasks.any { it.key.packageName == packageName } + fun containsPackage(packageName: String?) = tasks.any { it.key.packageName == packageName } - open fun hasMultipleTasks() = task2 != null + /** + * Returns true if a task in this group has a package name that matches the given `packageName`, + * and its user ID matches the given `userId`. + */ + fun containsPackage(packageName: String?, userId: Int) = + tasks.any { it.key.packageName == packageName && it.key.userId == userId } + + fun isEmpty() = tasks.isEmpty() /** Returns whether this task supports multiple tasks or not. */ open fun supportsMultipleTasks() = taskViewType == TaskViewType.GROUPED @@ -78,6 +85,10 @@ constructor( /** A [Task] container that must contain exactly one task in the recent tasks list. */ class SingleTask(task: Task) : GroupTask(task, task2 = null, mSplitBounds = null, TaskViewType.SINGLE) { + + val task: Task + get() = task1 + override fun copy() = SingleTask(task1) override fun toString() = "type=$taskViewType task=$task1" @@ -96,6 +107,12 @@ class SingleTask(task: Task) : class SplitTask(task1: Task, task2: Task, splitBounds: SplitConfigurationOptions.SplitBounds) : GroupTask(task1, task2, splitBounds, TaskViewType.GROUPED) { + val topLeftTask: Task + get() = if (mSplitBounds!!.leftTopTaskId == task1.key.id) task1!! else task2!! + + val bottomRightTask: Task + get() = if (topLeftTask == task1) task2!! else task1!! + override fun copy() = SplitTask(task1, task2!!, mSplitBounds!!) override fun toString() = "type=$taskViewType task1=$task1 task2=$task2" diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java index 5f8b4d93d0..fd8b356708 100644 --- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java +++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java @@ -71,7 +71,6 @@ import com.android.internal.logging.InstanceId; import com.android.launcher3.R; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.apppairs.AppPairIcon; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.IconProvider; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; @@ -260,7 +259,7 @@ public class SplitSelectStateController { GroupTask groupTask = taskGroups.get(i); if (isInstanceOfAppPair( groupTask, componentKeys.get(0), componentKeys.get(1))) { - lastActiveTasks[0] = groupTask.task1; + lastActiveTasks[0] = ((SplitTask) groupTask).getTopLeftTask(); break; } } @@ -314,11 +313,15 @@ public class SplitSelectStateController { */ public boolean isInstanceOfAppPair(GroupTask groupTask, @NonNull ComponentKey componentKey1, @NonNull ComponentKey componentKey2) { - return ((isInstanceOfComponent(groupTask.task1, componentKey1) - && isInstanceOfComponent(groupTask.task2, componentKey2)) - || - (isInstanceOfComponent(groupTask.task1, componentKey2) - && isInstanceOfComponent(groupTask.task2, componentKey1))); + if (groupTask instanceof SplitTask splitTask) { + return ((isInstanceOfComponent(splitTask.getTopLeftTask(), componentKey1) + && isInstanceOfComponent(splitTask.getBottomRightTask(), componentKey2)) + || + (isInstanceOfComponent(splitTask.getTopLeftTask(), componentKey2) + && isInstanceOfComponent(splitTask.getBottomRightTask(), + componentKey1))); + } + return false; } /** diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index c0b026bd66..c6db576177 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -212,9 +212,11 @@ import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.RecentsAtomicAnimationFactory; import com.android.quickstep.util.RecentsOrientedState; +import com.android.quickstep.util.SingleTask; import com.android.quickstep.util.SplitAnimationController.Companion.SplitAnimInitProps; import com.android.quickstep.util.SplitAnimationTimings; import com.android.quickstep.util.SplitSelectStateController; +import com.android.quickstep.util.SplitTask; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.TaskGridNavHelper; @@ -1968,7 +1970,7 @@ public abstract class RecentsView< GroupTask groupTask = taskGroups.get(i); boolean containsStagedTask = stagedTaskIdToBeRemoved != INVALID_TASK_ID && groupTask.containsTask(stagedTaskIdToBeRemoved); - boolean shouldSkipGroupTask = containsStagedTask && !groupTask.hasMultipleTasks(); + boolean shouldSkipGroupTask = containsStagedTask && groupTask instanceof SingleTask; if ((isSplitSelectionActive() && groupTask.taskViewType == TaskViewType.DESKTOP) || shouldSkipGroupTask) { @@ -1982,25 +1984,27 @@ public abstract class RecentsView< // to be a temporary container for the remaining task. TaskView taskView = getTaskViewFromPool( containsStagedTask ? TaskViewType.SINGLE : groupTask.taskViewType); - if (taskView instanceof GroupedTaskView) { - boolean firstTaskIsLeftTopTask = - groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id; - Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2; - Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1; - ((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState, - mTaskOverlayFactory, groupTask.mSplitBounds); - } else if (taskView instanceof DesktopTaskView) { + if (taskView instanceof GroupedTaskView groupedTaskView) { + var splitTask = (SplitTask) groupTask; + groupedTaskView.bind(splitTask.getTopLeftTask(), + splitTask.getBottomRightTask(), mOrientationState, + mTaskOverlayFactory, splitTask.mSplitBounds); + } else if (taskView instanceof DesktopTaskView desktopTaskView) { // Minimized tasks should not be shown in Overview List nonMinimizedTasks = groupTask.getTasks().stream() .filter(task -> !task.isMinimized) .toList(); - ((DesktopTaskView) taskView).bind(nonMinimizedTasks, mOrientationState, + desktopTaskView.bind(nonMinimizedTasks, mOrientationState, mTaskOverlayFactory); - } else { - Task task = groupTask.task1.key.id == stagedTaskIdToBeRemoved ? groupTask.task2 - : groupTask.task1; + } else if (groupTask instanceof SplitTask splitTask) { + Task task = splitTask.getTopLeftTask().key.id == stagedTaskIdToBeRemoved + ? splitTask.getBottomRightTask() + : splitTask.getTopLeftTask(); taskView.bind(task, mOrientationState, mTaskOverlayFactory); + } else { + taskView.bind(((SingleTask) groupTask).getTask(), mOrientationState, + mTaskOverlayFactory); } addView(taskView); diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt index 0491c07895..e4bdba510f 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/SplitSelectStateControllerTest.kt @@ -102,12 +102,12 @@ class SplitSelectStateControllerTest { fun activeTasks_noMatchingTasks() { val nonMatchingComponent = ComponentKey(ComponentName("no", "match"), primaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName("pomegranate", "juice"), ComponentName("pumpkin", "pie"), ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("hotdog", "juice"), ComponentName("personal", "computer"), ) @@ -143,12 +143,12 @@ class SplitSelectStateControllerTest { val matchingComponent = ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage, matchingClass), ComponentName("pomegranate", "juice"), ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pumpkin", "pie"), ComponentName("personal", "computer"), ) @@ -170,7 +170,7 @@ class SplitSelectStateControllerTest { it[0].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[0], groupTask1.task1) + assertEquals(it[0], groupTask1.topLeftTask) } // Capture callback from recentsModel#getTasks() @@ -196,12 +196,12 @@ class SplitSelectStateControllerTest { val nonPrimaryUserComponent = ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage, matchingClass), ComponentName("pomegranate", "juice"), ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pumpkin", "pie"), ComponentName("personal", "computer"), ) @@ -237,14 +237,14 @@ class SplitSelectStateControllerTest { val nonPrimaryUserComponent = ComponentKey(ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage, matchingClass), nonPrimaryUserHandle, ComponentName("pomegranate", "juice"), nonPrimaryUserHandle, ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pumpkin", "pie"), ComponentName("personal", "computer"), ) @@ -267,7 +267,7 @@ class SplitSelectStateControllerTest { matchingClass, ) assertEquals("userId mismatched", it[0].key.userId, nonPrimaryUserHandle.identifier) - assertEquals(it[0], groupTask1.task1) + assertEquals(it[0], groupTask1.topLeftTask) } // Capture callback from recentsModel#getTasks() @@ -293,12 +293,12 @@ class SplitSelectStateControllerTest { val matchingComponent = ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage, matchingClass), ComponentName("pumpkin", "pie"), ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pomegranate", "juice"), ComponentName(matchingPackage, matchingClass), ) @@ -320,7 +320,7 @@ class SplitSelectStateControllerTest { it[0].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[0], groupTask1.task1) + assertEquals(it[0], groupTask1.topLeftTask) } // Capture callback from recentsModel#getTasks() @@ -348,9 +348,9 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) + generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pomegranate", "juice"), ComponentName(matchingPackage, matchingClass), ) @@ -374,7 +374,7 @@ class SplitSelectStateControllerTest { it[1].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[1], groupTask2.task2) + assertEquals(it[1], groupTask2.bottomRightTask) } // Capture callback from recentsModel#getTasks() @@ -401,9 +401,9 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) + generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pomegranate", "juice"), ComponentName(matchingPackage, matchingClass), ) @@ -426,7 +426,7 @@ class SplitSelectStateControllerTest { it[0].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[0], groupTask2.task2) + assertEquals(it[0], groupTask2.bottomRightTask) assertNull("No tasks should have matched", it[1] /*task*/) } @@ -454,12 +454,12 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage, matchingClass), primaryUserHandle) val groupTask1 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage, matchingClass), ComponentName("pumpkin", "pie"), ) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName("pomegranate", "juice"), ComponentName(matchingPackage, matchingClass), ) @@ -482,7 +482,7 @@ class SplitSelectStateControllerTest { it[0].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[0], groupTask1.task1) + assertEquals(it[0], groupTask1.topLeftTask) assertEquals( "ComponentName package mismatched", it[1].key.baseIntent.component?.packageName, @@ -493,7 +493,7 @@ class SplitSelectStateControllerTest { it[1].key.baseIntent.component?.className, matchingClass, ) - assertEquals(it[1], groupTask2.task2) + assertEquals(it[1], groupTask2.bottomRightTask) } // Capture callback from recentsModel#getTasks() @@ -524,14 +524,14 @@ class SplitSelectStateControllerTest { ComponentKey(ComponentName(matchingPackage2, matchingClass2), primaryUserHandle) val groupTask1 = - generateGroupTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) + generateSplitTask(ComponentName("hotdog", "pie"), ComponentName("pumpkin", "pie")) val groupTask2 = - generateGroupTask( + generateSplitTask( ComponentName(matchingPackage2, matchingClass2), ComponentName(matchingPackage, matchingClass), ) val groupTask3 = - generateGroupTask( + generateSplitTask( ComponentName("hotdog", "pie"), ComponentName(matchingPackage, matchingClass), ) @@ -545,7 +545,7 @@ class SplitSelectStateControllerTest { val taskConsumer = Consumer> { assertEquals("Expected array length 2", 2, it.size) - assertEquals("Found wrong task", it[0], groupTask2.task1) + assertEquals("Found wrong task", it[0], groupTask2.topLeftTask) } // Capture callback from recentsModel#getTasks() @@ -640,11 +640,11 @@ class SplitSelectStateControllerTest { verify(recentsView, times(0)).resetDesktopTaskFromSplitSelectState() } - // Generate GroupTask with default userId. - private fun generateGroupTask( + /** Generates a [SplitTask] with default userId. */ + private fun generateSplitTask( task1ComponentName: ComponentName, task2ComponentName: ComponentName, - ): GroupTask { + ): SplitTask { val task1 = Task() var taskInfo = ActivityManager.RunningTaskInfo() taskInfo.taskId = getUniqueId() @@ -666,20 +666,20 @@ class SplitSelectStateControllerTest { SplitConfigurationOptions.SplitBounds( /* leftTopBounds = */ Rect(), /* rightBottomBounds = */ Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ task1.key.id, + /* rightBottomTaskId = */ task2.key.id, /* snapPosition = */ SNAP_TO_2_50_50, ), ) } - // Generate GroupTask with custom user handles. - private fun generateGroupTask( + /** Generates a [SplitTask] with custom user handles. */ + private fun generateSplitTask( task1ComponentName: ComponentName, userHandle1: UserHandle, task2ComponentName: ComponentName, userHandle2: UserHandle, - ): GroupTask { + ): SplitTask { val task1 = Task() var taskInfo = ActivityManager.RunningTaskInfo() taskInfo.taskId = getUniqueId() @@ -704,8 +704,8 @@ class SplitSelectStateControllerTest { SplitConfigurationOptions.SplitBounds( /* leftTopBounds = */ Rect(), /* rightBottomBounds = */ Rect(), - /* leftTopTaskId = */ -1, - /* rightBottomTaskId = */ -1, + /* leftTopTaskId = */ task1.key.id, + /* rightBottomTaskId = */ task2.key.id, /* snapPosition = */ SNAP_TO_2_50_50, ), )