Loading libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java +43 −19 Original line number Diff line number Diff line Loading @@ -47,20 +47,26 @@ public class GroupedTaskInfo implements Parcelable { public static final int TYPE_FULLSCREEN = 1; public static final int TYPE_SPLIT = 2; public static final int TYPE_FREEFORM = 3; public static final int TYPE_DESK = 3; public static final int TYPE_MIXED = 4; @IntDef(prefix = {"TYPE_"}, value = { TYPE_FULLSCREEN, TYPE_SPLIT, TYPE_FREEFORM, TYPE_DESK, TYPE_MIXED }) public @interface GroupType {} /** * The ID of the desk that this `GroupedTaskInfo` represents (when the type is `TYPE_DESK`). The * value is -1 if this is not a desk. */ private final int mDeskId; /** * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or * TYPE_FREEFORM. * TYPE_DESK. */ @GroupType protected final int mType; Loading @@ -69,7 +75,7 @@ public class GroupedTaskInfo implements Parcelable { * The list of tasks associated with this single recent task info. * TYPE_FULLSCREEN: Contains the stack of tasks associated with a single "task" in overview * TYPE_SPLIT: Contains the two split roots of each side * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode * TYPE_DESK: Contains the set of tasks currently in freeform mode contained in desk. */ @Nullable protected final List<TaskInfo> mTasks; Loading @@ -83,7 +89,7 @@ public class GroupedTaskInfo implements Parcelable { protected final SplitBounds mSplitBounds; /** * Only set for TYPE_FREEFORM. * Only set for TYPE_DESK. * * TODO(b/348332802): move isMinimized inside each Task object instead once we have a * replacement for RecentTaskInfo Loading @@ -103,8 +109,8 @@ public class GroupedTaskInfo implements Parcelable { * Create new for a stack of fullscreen tasks */ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) { return new GroupedTaskInfo(List.of(task), null, TYPE_FULLSCREEN, null /* minimizedFreeformTasks */); return new GroupedTaskInfo(/* deskId = */ -1, List.of(task), null, TYPE_FULLSCREEN, /* minimizedFreeformTaskIds = */ null); } /** Loading @@ -112,18 +118,19 @@ public class GroupedTaskInfo implements Parcelable { */ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1, @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) { return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */); return new GroupedTaskInfo(/* deskId = */ -1, List.of(task1, task2), splitBounds, TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null); } /** * Create new for a group of freeform tasks * Create new for a group of freeform tasks that belong to a single desk. */ public static GroupedTaskInfo forFreeformTasks( public static GroupedTaskInfo forDeskTasks( int deskId, @NonNull List<TaskInfo> tasks, @NonNull Set<Integer> minimizedFreeformTasks) { return new GroupedTaskInfo(tasks, null /* splitBounds */, TYPE_FREEFORM, minimizedFreeformTasks.stream().mapToInt(i -> i).toArray()); @NonNull Set<Integer> minimizedFreeformTaskIds) { return new GroupedTaskInfo(deskId, tasks, /* splitBounds = */ null, TYPE_DESK, minimizedFreeformTaskIds.stream().mapToInt(i -> i).toArray()); } /** Loading @@ -141,10 +148,12 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo( int deskId, @NonNull List<TaskInfo> tasks, @Nullable SplitBounds splitBounds, @GroupType int type, @Nullable int[] minimizedFreeformTaskIds) { mDeskId = deskId; mTasks = tasks; mGroupedTasks = null; mSplitBounds = splitBounds; Loading @@ -154,6 +163,7 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) { mDeskId = -1; mTasks = null; mGroupedTasks = groupedTasks; mSplitBounds = null; Loading @@ -174,6 +184,7 @@ public class GroupedTaskInfo implements Parcelable { } protected GroupedTaskInfo(@NonNull Parcel parcel) { mDeskId = parcel.readInt(); mTasks = new ArrayList(); final int numTasks = parcel.readInt(); for (int i = 0; i < numTasks; i++) { Loading Loading @@ -273,6 +284,16 @@ public class GroupedTaskInfo implements Parcelable { return mSplitBounds; } /** * Returns the ID of the desk represented by `this` if the type is `TYPE_DESK`, or -1 otherwise. */ public int getDeskId() { if (mType == TYPE_MIXED) { throw new IllegalStateException("No desk ID for a mixed task"); } return mDeskId; } /** * Get type of this recents entry. One of {@link GroupType}. * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about Loading @@ -285,7 +306,7 @@ public class GroupedTaskInfo implements Parcelable { } /** * Returns the set of minimized task ids, only valid for TYPE_FREEFORM. * Returns the set of minimized task ids, only valid for TYPE_DESK. */ @Nullable public int[] getMinimizedTaskIds() { Loading @@ -301,7 +322,8 @@ public class GroupedTaskInfo implements Parcelable { return false; } GroupedTaskInfo other = (GroupedTaskInfo) obj; return mType == other.mType return mDeskId == other.mDeskId && mType == other.mType && Objects.equals(mTasks, other.mTasks) && Objects.equals(mGroupedTasks, other.mGroupedTasks) && Objects.equals(mSplitBounds, other.mSplitBounds) Loading @@ -310,7 +332,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public int hashCode() { return Objects.hash(mType, mTasks, mGroupedTasks, mSplitBounds, return Objects.hash(mDeskId, mType, mTasks, mGroupedTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds)); } Loading @@ -322,6 +344,7 @@ public class GroupedTaskInfo implements Parcelable { .map(GroupedTaskInfo::toString) .collect(Collectors.joining(",\n\t", "[\n\t", "\n]"))); } else { taskString.append("Desk ID= ").append(mDeskId).append(", "); taskString.append("Tasks=" + mTasks.stream() .map(taskInfo -> getTaskInfoDumpString(taskInfo)) .collect(Collectors.joining(", ", "[", "]"))); Loading Loading @@ -353,6 +376,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mDeskId); // We don't use the parcel list methods because we want to only write the TaskInfo state // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated final int tasksSize = mTasks != null ? mTasks.size() : 0; Loading @@ -375,7 +399,7 @@ public class GroupedTaskInfo implements Parcelable { return switch (type) { case TYPE_FULLSCREEN -> "FULLSCREEN"; case TYPE_SPLIT -> "SPLIT"; case TYPE_FREEFORM -> "FREEFORM"; case TYPE_DESK -> "DESK"; case TYPE_MIXED -> "MIXED"; default -> "UNKNOWN"; }; Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +3 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,9 @@ class DesktopRepository( fun getDeskIds(displayId: Int): Set<Int> = desktopData.desksSequence(displayId).map { desk -> desk.deskId }.toSet() /** Returns all the ids of all desks in all displays. */ fun getAllDeskIds(): Set<Int> = desktopData.desksSequence().map { desk -> desk.deskId }.toSet() /** Returns the id of the default desk in the given display. */ fun getDefaultDeskId(displayId: Int): Int? = getDefaultDesk(displayId)?.deskId Loading libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +109 −19 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.util.Slog; import android.util.SparseIntArray; import android.window.DesktopExperienceFlags; import android.window.DesktopModeFlags; import android.window.WindowContainerToken; Loading Loading @@ -92,6 +93,11 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackTransitionObserver.TaskStackTransitionObserverListener, UserChangeListener { private static final String TAG = RecentTasksController.class.getSimpleName(); // When the multiple desktops feature is disabled, all freeform tasks are lumped together into // a single `GroupedTaskInfo` whose type is `TYPE_DESK`, and its `mDeskId` doesn't matter, so // we pick the below arbitrary value. private static final int INVALID_DESK_ID = -1; private final Context mContext; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; Loading Loading @@ -128,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, // Temporary vars used in `generateList()` private final Map<Integer, TaskInfo> mTmpRemaining = new HashMap<>(); private final Map<Integer, Desk> mTmpDesks = new HashMap<>(); /** * Creates {@link RecentTasksController}, returns {@code null} if the feature is not Loading Loading @@ -528,6 +535,78 @@ public class RecentTasksController implements TaskStackListenerCallback, return false; } /** * Represents a desk whose ID is `mDeskId` and contains the tasks in `mDeskTasks`. Some of these * tasks are minimized and their IDs are contained in the `mMinimizedDeskTasks` set. */ private static class Desk { final int mDeskId; boolean mHasVisibleTasks = false; final ArrayList<TaskInfo> mDeskTasks = new ArrayList<>(); final Set<Integer> mMinimizedDeskTasks = new HashSet<>(); Desk(int deskId) { mDeskId = deskId; } void addTask(TaskInfo taskInfo, boolean isMinimized, boolean isVisible) { mDeskTasks.add(taskInfo); if (isMinimized) { mMinimizedDeskTasks.add(taskInfo.taskId); } mHasVisibleTasks |= isVisible; } boolean hasTasks() { return !mDeskTasks.isEmpty(); } GroupedTaskInfo createDeskTaskInfo() { return GroupedTaskInfo.forDeskTasks(mDeskId, mDeskTasks, mMinimizedDeskTasks); } } /** * Clears the `mTmpDesks` map, and re-initializes it with the current state of desks from all * displays, without adding any desk tasks. This is a preparation step so that tasks can be * added to these desks in `generateList()`. * * This is needed since with the `ENABLE_MULTIPLE_DESKTOPS_BACKEND` flag, we want to include * desk even if they're empty (i.e. have no tasks). * * @param multipleDesktopsEnabled whether the multiple desktops backend feature is enabled. */ private void initializeDesksMap(boolean multipleDesktopsEnabled) { mTmpDesks.clear(); if (DesktopModeStatus.canEnterDesktopMode(mContext) && mDesktopUserRepositories.isPresent()) { if (multipleDesktopsEnabled) { for (var deskId : mDesktopUserRepositories.get().getCurrent().getAllDeskIds()) { getOrCreateDesk(deskId); } } else { // When the multiple desks feature is disabled, we lump all freeform windows in a // single `GroupedTaskInfo` regardless of their display. The `deskId` in this case // doesn't matter and can be any arbitrary value. getOrCreateDesk(/* deskId = */ INVALID_DESK_ID); } } } /** * Returns the `Desk` whose ID is `deskId` from the `mTmpDesks` map if it exists, or it creates * one and adds it to the map and then returns it. */ private Desk getOrCreateDesk(int deskId) { var desk = mTmpDesks.get(deskId); if (desk == null) { desk = new Desk(deskId); mTmpDesks.put(deskId, desk); } return desk; } /** * Generates a list of GroupedTaskInfos for the given raw list of tasks (either recents or * running tasks). Loading Loading @@ -555,6 +634,14 @@ public class RecentTasksController implements TaskStackListenerCallback, ProtoLog.v(WM_SHELL_TASK_OBSERVER, "RecentTasksController.generateList(%s)", reason); } final boolean multipleDesktopsEnabled = DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue(); initializeDesksMap(multipleDesktopsEnabled); // When the multiple desktops feature is enabled, we include all desks even if they're // empty. final boolean shouldIncludeEmptyDesktops = multipleDesktopsEnabled; // Make a mapping of task id -> task info for the remaining tasks to be processed, this // mapping is used to keep track of split tasks that may exist later in the task list that // should be ignored because they've already been grouped Loading @@ -566,11 +653,7 @@ public class RecentTasksController implements TaskStackListenerCallback, ArrayList<GroupedTaskInfo> groupedTasks = new ArrayList<>(tasks.size()); ArrayList<GroupedTaskInfo> visibleGroupedTasks = new ArrayList<>(); // Phase 1: Extract the desktop and visible fullscreen/split tasks. We make new collections // here as the GroupedTaskInfo can store them without copying ArrayList<TaskInfo> desktopTasks = new ArrayList<>(); Set<Integer> minimizedDesktopTasks = new HashSet<>(); boolean desktopTasksVisible = false; // Phase 1: Extract the desktops and visible fullscreen/split tasks. for (int i = 0; i < tasks.size(); i++) { final TaskInfo taskInfo = tasks.get(i); final int taskId = taskInfo.taskId; Loading Loading @@ -600,11 +683,15 @@ public class RecentTasksController implements TaskStackListenerCallback, taskInfo.positionInParent = new Point(taskInfo.lastNonFullscreenBounds.left, taskInfo.lastNonFullscreenBounds.top); } desktopTasks.add(taskInfo); if (mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId)) { minimizedDesktopTasks.add(taskId); } desktopTasksVisible |= mVisibleTasksMap.containsKey(taskId); // Lump all freeform tasks together as if they were all in a single desk whose ID is // `INVALID_DESK_ID` when the multiple desktops feature is disabled. final int deskId = multipleDesktopsEnabled ? mDesktopUserRepositories.get().getCurrent().getDeskIdForTask(taskId) : INVALID_DESK_ID; final Desk desk = getOrCreateDesk(deskId); desk.addTask(taskInfo, mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId), mVisibleTasksMap.containsKey(taskId)); mTmpRemaining.remove(taskId); continue; } Loading Loading @@ -636,9 +723,10 @@ public class RecentTasksController implements TaskStackListenerCallback, if (enableShellTopTaskTracking()) { // Phase 2: If there were desktop tasks and they are visible, add them to the visible // list as well (the actual order doesn't matter for Overview) if (!desktopTasks.isEmpty() && desktopTasksVisible) { visibleGroupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (desk.mHasVisibleTasks) { visibleGroupedTasks.add(desk.createDeskTaskInfo()); } } if (!visibleGroupedTasks.isEmpty()) { Loading Loading @@ -674,16 +762,18 @@ public class RecentTasksController implements TaskStackListenerCallback, // Phase 5: If there were desktop tasks and they are not visible (ie. weren't added // above), add them to the end of the final list (the actual order doesn't // matter for Overview) if (!desktopTasks.isEmpty() && !desktopTasksVisible) { groupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (!desk.mHasVisibleTasks && (desk.hasTasks() || shouldIncludeEmptyDesktops)) { groupedTasks.add(desk.createDeskTaskInfo()); } } dumpGroupedTasks(groupedTasks, "Phase 5"); } else { // Add the desktop tasks at the end of the list if (!desktopTasks.isEmpty()) { groupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (desk.hasTasks() || shouldIncludeEmptyDesktops) { groupedTasks.add(desk.createDeskTaskInfo()); } } } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt +28 −23 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.shared.GroupedTaskInfo import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT import com.android.wm.shell.shared.split.SplitBounds Loading Loading @@ -87,14 +87,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testFreeformTasks_hasCorrectType() { assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).isBaseType(TYPE_FREEFORM)) fun testDeskTasks_hasCorrectType() { assertThat(deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1)).isBaseType(TYPE_DESK)) .isTrue() } @Test fun testCreateFreeformTasks_hasCorrectNumberOfTasks() { val list = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList fun testCreateDeskTasks_hasCorrectNumberOfTasks() { val list = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList assertThat(list).hasSize(3) assertThat(list[0].taskId).isEqualTo(1) assertThat(list[1].taskId).isEqualTo(2) Loading @@ -102,9 +102,9 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testCreateFreeformTasks_nonExistentMinimizedTaskId_throwsException() { fun testCreateDeskTasks_nonExistentMinimizedTaskId_throwsException() { assertThrows(IllegalArgumentException::class.java) { freeformTasksGroupInfo( deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(1, 4) ) Loading @@ -122,8 +122,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testMixedWithFreeformBase_hasCorrectType() { assertThat(mixedTaskGroupInfoWithFreeformBase().isBaseType(TYPE_FREEFORM)).isTrue() fun testMixedWithDeskBase_hasCorrectType() { assertThat(mixedTaskGroupInfoWithDeskBase().isBaseType(TYPE_DESK)).isTrue() } @Test Loading Loading @@ -190,15 +190,16 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testParcelling_freeformTasks() { val taskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)) fun testParcelling_DeskTasks() { val taskInfo = deskTasksGroupInfo(deskId = 50, freeformTaskIds = arrayOf(1, 2, 3)) val parcel = Parcel.obtain() taskInfo.writeToParcel(parcel, 0) parcel.setDataPosition(0) // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.deskId).isEqualTo(taskInfo.deskId) assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.taskInfoList).hasSize(3) // Only compare task ids val taskIdComparator = Correspondence.transforming<TaskInfo, Int>( Loading @@ -209,8 +210,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testParcelling_freeformTasks_minimizedTasks() { val taskInfo = freeformTasksGroupInfo( fun testParcelling_DeskTasks_minimizedTasks() { val taskInfo = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2)) val parcel = Parcel.obtain() Loading @@ -220,14 +221,14 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray()) } @Test fun testParcelling_mixedTasks() { val taskInfo = GroupedTaskInfo.forMixed(listOf( freeformTasksGroupInfo(freeformTaskIds = arrayOf(4, 5, 6), deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(4, 5, 6), minimizedTaskIds = arrayOf(5)), splitTasksGroupInfo(firstId = 2, secondId = 3), singleTaskGroupInfo(id = 1))) Loading @@ -239,7 +240,7 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.baseGroupedTask.minimizedTaskIds).isEqualTo( arrayOf(5).toIntArray()) for (i in 1..6) { Loading Loading @@ -275,12 +276,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testTaskProperties_freeformTasks() { fun testTaskProperties_DeskTasks() { val task1 = createTaskInfo(id = 1) val task2 = createTaskInfo(id = 2) val taskInfo = GroupedTaskInfo.forFreeformTasks(listOf(task1, task2), setOf()) val taskInfo = GroupedTaskInfo.forDeskTasks( /* deskId = */ 500, listOf(task1, task2), setOf()) assertThat(taskInfo.deskId).isEqualTo(500) assertThat(taskInfo.getTaskById(1)).isEqualTo(task1) assertThat(taskInfo.getTaskById(2)).isEqualTo(task2) assertThat(taskInfo.containsTask(1)).isTrue() Loading Loading @@ -325,11 +328,13 @@ class GroupedTaskInfoTest : ShellTestCase() { return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds) } private fun freeformTasksGroupInfo( private fun deskTasksGroupInfo( deskId: Int, freeformTaskIds: Array<Int>, minimizedTaskIds: Array<Int> = emptyArray() ): GroupedTaskInfo { return GroupedTaskInfo.forFreeformTasks( return GroupedTaskInfo.forDeskTasks( deskId, freeformTaskIds.map { createTaskInfo(it) }.toList(), minimizedTaskIds.toSet()) } Loading @@ -346,9 +351,9 @@ class GroupedTaskInfoTest : ShellTestCase() { singleTaskGroupInfo(id = 1))) } private fun mixedTaskGroupInfoWithFreeformBase(): GroupedTaskInfo { private fun mixedTaskGroupInfoWithDeskBase(): GroupedTaskInfo { return GroupedTaskInfo.forMixed(listOf( freeformTasksGroupInfo(freeformTaskIds = arrayOf(2, 3, 4)), deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(2, 3, 4)), singleTaskGroupInfo(id = 1))) } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +4 −4 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; Loading Loading @@ -349,7 +349,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); Loading Loading @@ -389,7 +389,7 @@ public class RecentTasksControllerTest extends ShellTestCase { // Check that groups have expected types assertTrue(splitGroup.isBaseType(TYPE_SPLIT)); assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup.isBaseType(TYPE_FULLSCREEN)); // Check freeform group entries Loading Loading @@ -460,7 +460,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); Loading Loading
libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java +43 −19 Original line number Diff line number Diff line Loading @@ -47,20 +47,26 @@ public class GroupedTaskInfo implements Parcelable { public static final int TYPE_FULLSCREEN = 1; public static final int TYPE_SPLIT = 2; public static final int TYPE_FREEFORM = 3; public static final int TYPE_DESK = 3; public static final int TYPE_MIXED = 4; @IntDef(prefix = {"TYPE_"}, value = { TYPE_FULLSCREEN, TYPE_SPLIT, TYPE_FREEFORM, TYPE_DESK, TYPE_MIXED }) public @interface GroupType {} /** * The ID of the desk that this `GroupedTaskInfo` represents (when the type is `TYPE_DESK`). The * value is -1 if this is not a desk. */ private final int mDeskId; /** * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or * TYPE_FREEFORM. * TYPE_DESK. */ @GroupType protected final int mType; Loading @@ -69,7 +75,7 @@ public class GroupedTaskInfo implements Parcelable { * The list of tasks associated with this single recent task info. * TYPE_FULLSCREEN: Contains the stack of tasks associated with a single "task" in overview * TYPE_SPLIT: Contains the two split roots of each side * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode * TYPE_DESK: Contains the set of tasks currently in freeform mode contained in desk. */ @Nullable protected final List<TaskInfo> mTasks; Loading @@ -83,7 +89,7 @@ public class GroupedTaskInfo implements Parcelable { protected final SplitBounds mSplitBounds; /** * Only set for TYPE_FREEFORM. * Only set for TYPE_DESK. * * TODO(b/348332802): move isMinimized inside each Task object instead once we have a * replacement for RecentTaskInfo Loading @@ -103,8 +109,8 @@ public class GroupedTaskInfo implements Parcelable { * Create new for a stack of fullscreen tasks */ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) { return new GroupedTaskInfo(List.of(task), null, TYPE_FULLSCREEN, null /* minimizedFreeformTasks */); return new GroupedTaskInfo(/* deskId = */ -1, List.of(task), null, TYPE_FULLSCREEN, /* minimizedFreeformTaskIds = */ null); } /** Loading @@ -112,18 +118,19 @@ public class GroupedTaskInfo implements Parcelable { */ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1, @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) { return new GroupedTaskInfo(List.of(task1, task2), splitBounds, TYPE_SPLIT, null /* minimizedFreeformTasks */); return new GroupedTaskInfo(/* deskId = */ -1, List.of(task1, task2), splitBounds, TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null); } /** * Create new for a group of freeform tasks * Create new for a group of freeform tasks that belong to a single desk. */ public static GroupedTaskInfo forFreeformTasks( public static GroupedTaskInfo forDeskTasks( int deskId, @NonNull List<TaskInfo> tasks, @NonNull Set<Integer> minimizedFreeformTasks) { return new GroupedTaskInfo(tasks, null /* splitBounds */, TYPE_FREEFORM, minimizedFreeformTasks.stream().mapToInt(i -> i).toArray()); @NonNull Set<Integer> minimizedFreeformTaskIds) { return new GroupedTaskInfo(deskId, tasks, /* splitBounds = */ null, TYPE_DESK, minimizedFreeformTaskIds.stream().mapToInt(i -> i).toArray()); } /** Loading @@ -141,10 +148,12 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo( int deskId, @NonNull List<TaskInfo> tasks, @Nullable SplitBounds splitBounds, @GroupType int type, @Nullable int[] minimizedFreeformTaskIds) { mDeskId = deskId; mTasks = tasks; mGroupedTasks = null; mSplitBounds = splitBounds; Loading @@ -154,6 +163,7 @@ public class GroupedTaskInfo implements Parcelable { } private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) { mDeskId = -1; mTasks = null; mGroupedTasks = groupedTasks; mSplitBounds = null; Loading @@ -174,6 +184,7 @@ public class GroupedTaskInfo implements Parcelable { } protected GroupedTaskInfo(@NonNull Parcel parcel) { mDeskId = parcel.readInt(); mTasks = new ArrayList(); final int numTasks = parcel.readInt(); for (int i = 0; i < numTasks; i++) { Loading Loading @@ -273,6 +284,16 @@ public class GroupedTaskInfo implements Parcelable { return mSplitBounds; } /** * Returns the ID of the desk represented by `this` if the type is `TYPE_DESK`, or -1 otherwise. */ public int getDeskId() { if (mType == TYPE_MIXED) { throw new IllegalStateException("No desk ID for a mixed task"); } return mDeskId; } /** * Get type of this recents entry. One of {@link GroupType}. * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about Loading @@ -285,7 +306,7 @@ public class GroupedTaskInfo implements Parcelable { } /** * Returns the set of minimized task ids, only valid for TYPE_FREEFORM. * Returns the set of minimized task ids, only valid for TYPE_DESK. */ @Nullable public int[] getMinimizedTaskIds() { Loading @@ -301,7 +322,8 @@ public class GroupedTaskInfo implements Parcelable { return false; } GroupedTaskInfo other = (GroupedTaskInfo) obj; return mType == other.mType return mDeskId == other.mDeskId && mType == other.mType && Objects.equals(mTasks, other.mTasks) && Objects.equals(mGroupedTasks, other.mGroupedTasks) && Objects.equals(mSplitBounds, other.mSplitBounds) Loading @@ -310,7 +332,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public int hashCode() { return Objects.hash(mType, mTasks, mGroupedTasks, mSplitBounds, return Objects.hash(mDeskId, mType, mTasks, mGroupedTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds)); } Loading @@ -322,6 +344,7 @@ public class GroupedTaskInfo implements Parcelable { .map(GroupedTaskInfo::toString) .collect(Collectors.joining(",\n\t", "[\n\t", "\n]"))); } else { taskString.append("Desk ID= ").append(mDeskId).append(", "); taskString.append("Tasks=" + mTasks.stream() .map(taskInfo -> getTaskInfoDumpString(taskInfo)) .collect(Collectors.joining(", ", "[", "]"))); Loading Loading @@ -353,6 +376,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mDeskId); // We don't use the parcel list methods because we want to only write the TaskInfo state // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated final int tasksSize = mTasks != null ? mTasks.size() : 0; Loading @@ -375,7 +399,7 @@ public class GroupedTaskInfo implements Parcelable { return switch (type) { case TYPE_FULLSCREEN -> "FULLSCREEN"; case TYPE_SPLIT -> "SPLIT"; case TYPE_FREEFORM -> "FREEFORM"; case TYPE_DESK -> "DESK"; case TYPE_MIXED -> "MIXED"; default -> "UNKNOWN"; }; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt +3 −0 Original line number Diff line number Diff line Loading @@ -217,6 +217,9 @@ class DesktopRepository( fun getDeskIds(displayId: Int): Set<Int> = desktopData.desksSequence(displayId).map { desk -> desk.deskId }.toSet() /** Returns all the ids of all desks in all displays. */ fun getAllDeskIds(): Set<Int> = desktopData.desksSequence().map { desk -> desk.deskId }.toSet() /** Returns the id of the default desk in the given display. */ fun getDefaultDeskId(displayId: Int): Int? = getDefaultDesk(displayId)?.deskId Loading
libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +109 −19 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ import android.os.Bundle; import android.os.RemoteException; import android.util.Slog; import android.util.SparseIntArray; import android.window.DesktopExperienceFlags; import android.window.DesktopModeFlags; import android.window.WindowContainerToken; Loading Loading @@ -92,6 +93,11 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackTransitionObserver.TaskStackTransitionObserverListener, UserChangeListener { private static final String TAG = RecentTasksController.class.getSimpleName(); // When the multiple desktops feature is disabled, all freeform tasks are lumped together into // a single `GroupedTaskInfo` whose type is `TYPE_DESK`, and its `mDeskId` doesn't matter, so // we pick the below arbitrary value. private static final int INVALID_DESK_ID = -1; private final Context mContext; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; Loading Loading @@ -128,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, // Temporary vars used in `generateList()` private final Map<Integer, TaskInfo> mTmpRemaining = new HashMap<>(); private final Map<Integer, Desk> mTmpDesks = new HashMap<>(); /** * Creates {@link RecentTasksController}, returns {@code null} if the feature is not Loading Loading @@ -528,6 +535,78 @@ public class RecentTasksController implements TaskStackListenerCallback, return false; } /** * Represents a desk whose ID is `mDeskId` and contains the tasks in `mDeskTasks`. Some of these * tasks are minimized and their IDs are contained in the `mMinimizedDeskTasks` set. */ private static class Desk { final int mDeskId; boolean mHasVisibleTasks = false; final ArrayList<TaskInfo> mDeskTasks = new ArrayList<>(); final Set<Integer> mMinimizedDeskTasks = new HashSet<>(); Desk(int deskId) { mDeskId = deskId; } void addTask(TaskInfo taskInfo, boolean isMinimized, boolean isVisible) { mDeskTasks.add(taskInfo); if (isMinimized) { mMinimizedDeskTasks.add(taskInfo.taskId); } mHasVisibleTasks |= isVisible; } boolean hasTasks() { return !mDeskTasks.isEmpty(); } GroupedTaskInfo createDeskTaskInfo() { return GroupedTaskInfo.forDeskTasks(mDeskId, mDeskTasks, mMinimizedDeskTasks); } } /** * Clears the `mTmpDesks` map, and re-initializes it with the current state of desks from all * displays, without adding any desk tasks. This is a preparation step so that tasks can be * added to these desks in `generateList()`. * * This is needed since with the `ENABLE_MULTIPLE_DESKTOPS_BACKEND` flag, we want to include * desk even if they're empty (i.e. have no tasks). * * @param multipleDesktopsEnabled whether the multiple desktops backend feature is enabled. */ private void initializeDesksMap(boolean multipleDesktopsEnabled) { mTmpDesks.clear(); if (DesktopModeStatus.canEnterDesktopMode(mContext) && mDesktopUserRepositories.isPresent()) { if (multipleDesktopsEnabled) { for (var deskId : mDesktopUserRepositories.get().getCurrent().getAllDeskIds()) { getOrCreateDesk(deskId); } } else { // When the multiple desks feature is disabled, we lump all freeform windows in a // single `GroupedTaskInfo` regardless of their display. The `deskId` in this case // doesn't matter and can be any arbitrary value. getOrCreateDesk(/* deskId = */ INVALID_DESK_ID); } } } /** * Returns the `Desk` whose ID is `deskId` from the `mTmpDesks` map if it exists, or it creates * one and adds it to the map and then returns it. */ private Desk getOrCreateDesk(int deskId) { var desk = mTmpDesks.get(deskId); if (desk == null) { desk = new Desk(deskId); mTmpDesks.put(deskId, desk); } return desk; } /** * Generates a list of GroupedTaskInfos for the given raw list of tasks (either recents or * running tasks). Loading Loading @@ -555,6 +634,14 @@ public class RecentTasksController implements TaskStackListenerCallback, ProtoLog.v(WM_SHELL_TASK_OBSERVER, "RecentTasksController.generateList(%s)", reason); } final boolean multipleDesktopsEnabled = DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue(); initializeDesksMap(multipleDesktopsEnabled); // When the multiple desktops feature is enabled, we include all desks even if they're // empty. final boolean shouldIncludeEmptyDesktops = multipleDesktopsEnabled; // Make a mapping of task id -> task info for the remaining tasks to be processed, this // mapping is used to keep track of split tasks that may exist later in the task list that // should be ignored because they've already been grouped Loading @@ -566,11 +653,7 @@ public class RecentTasksController implements TaskStackListenerCallback, ArrayList<GroupedTaskInfo> groupedTasks = new ArrayList<>(tasks.size()); ArrayList<GroupedTaskInfo> visibleGroupedTasks = new ArrayList<>(); // Phase 1: Extract the desktop and visible fullscreen/split tasks. We make new collections // here as the GroupedTaskInfo can store them without copying ArrayList<TaskInfo> desktopTasks = new ArrayList<>(); Set<Integer> minimizedDesktopTasks = new HashSet<>(); boolean desktopTasksVisible = false; // Phase 1: Extract the desktops and visible fullscreen/split tasks. for (int i = 0; i < tasks.size(); i++) { final TaskInfo taskInfo = tasks.get(i); final int taskId = taskInfo.taskId; Loading Loading @@ -600,11 +683,15 @@ public class RecentTasksController implements TaskStackListenerCallback, taskInfo.positionInParent = new Point(taskInfo.lastNonFullscreenBounds.left, taskInfo.lastNonFullscreenBounds.top); } desktopTasks.add(taskInfo); if (mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId)) { minimizedDesktopTasks.add(taskId); } desktopTasksVisible |= mVisibleTasksMap.containsKey(taskId); // Lump all freeform tasks together as if they were all in a single desk whose ID is // `INVALID_DESK_ID` when the multiple desktops feature is disabled. final int deskId = multipleDesktopsEnabled ? mDesktopUserRepositories.get().getCurrent().getDeskIdForTask(taskId) : INVALID_DESK_ID; final Desk desk = getOrCreateDesk(deskId); desk.addTask(taskInfo, mDesktopUserRepositories.get().getCurrent().isMinimizedTask(taskId), mVisibleTasksMap.containsKey(taskId)); mTmpRemaining.remove(taskId); continue; } Loading Loading @@ -636,9 +723,10 @@ public class RecentTasksController implements TaskStackListenerCallback, if (enableShellTopTaskTracking()) { // Phase 2: If there were desktop tasks and they are visible, add them to the visible // list as well (the actual order doesn't matter for Overview) if (!desktopTasks.isEmpty() && desktopTasksVisible) { visibleGroupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (desk.mHasVisibleTasks) { visibleGroupedTasks.add(desk.createDeskTaskInfo()); } } if (!visibleGroupedTasks.isEmpty()) { Loading Loading @@ -674,16 +762,18 @@ public class RecentTasksController implements TaskStackListenerCallback, // Phase 5: If there were desktop tasks and they are not visible (ie. weren't added // above), add them to the end of the final list (the actual order doesn't // matter for Overview) if (!desktopTasks.isEmpty() && !desktopTasksVisible) { groupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (!desk.mHasVisibleTasks && (desk.hasTasks() || shouldIncludeEmptyDesktops)) { groupedTasks.add(desk.createDeskTaskInfo()); } } dumpGroupedTasks(groupedTasks, "Phase 5"); } else { // Add the desktop tasks at the end of the list if (!desktopTasks.isEmpty()) { groupedTasks.add( GroupedTaskInfo.forFreeformTasks(desktopTasks, minimizedDesktopTasks)); for (var desk : mTmpDesks.values()) { if (desk.hasTasks() || shouldIncludeEmptyDesktops) { groupedTasks.add(desk.createDeskTaskInfo()); } } } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt +28 −23 Original line number Diff line number Diff line Loading @@ -26,7 +26,7 @@ import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.wm.shell.ShellTestCase import com.android.wm.shell.shared.GroupedTaskInfo import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN import com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT import com.android.wm.shell.shared.split.SplitBounds Loading Loading @@ -87,14 +87,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testFreeformTasks_hasCorrectType() { assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).isBaseType(TYPE_FREEFORM)) fun testDeskTasks_hasCorrectType() { assertThat(deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1)).isBaseType(TYPE_DESK)) .isTrue() } @Test fun testCreateFreeformTasks_hasCorrectNumberOfTasks() { val list = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList fun testCreateDeskTasks_hasCorrectNumberOfTasks() { val list = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3)).taskInfoList assertThat(list).hasSize(3) assertThat(list[0].taskId).isEqualTo(1) assertThat(list[1].taskId).isEqualTo(2) Loading @@ -102,9 +102,9 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testCreateFreeformTasks_nonExistentMinimizedTaskId_throwsException() { fun testCreateDeskTasks_nonExistentMinimizedTaskId_throwsException() { assertThrows(IllegalArgumentException::class.java) { freeformTasksGroupInfo( deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(1, 4) ) Loading @@ -122,8 +122,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testMixedWithFreeformBase_hasCorrectType() { assertThat(mixedTaskGroupInfoWithFreeformBase().isBaseType(TYPE_FREEFORM)).isTrue() fun testMixedWithDeskBase_hasCorrectType() { assertThat(mixedTaskGroupInfoWithDeskBase().isBaseType(TYPE_DESK)).isTrue() } @Test Loading Loading @@ -190,15 +190,16 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testParcelling_freeformTasks() { val taskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3)) fun testParcelling_DeskTasks() { val taskInfo = deskTasksGroupInfo(deskId = 50, freeformTaskIds = arrayOf(1, 2, 3)) val parcel = Parcel.obtain() taskInfo.writeToParcel(parcel, 0) parcel.setDataPosition(0) // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.deskId).isEqualTo(taskInfo.deskId) assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.taskInfoList).hasSize(3) // Only compare task ids val taskIdComparator = Correspondence.transforming<TaskInfo, Int>( Loading @@ -209,8 +210,8 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testParcelling_freeformTasks_minimizedTasks() { val taskInfo = freeformTasksGroupInfo( fun testParcelling_DeskTasks_minimizedTasks() { val taskInfo = deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2)) val parcel = Parcel.obtain() Loading @@ -220,14 +221,14 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray()) } @Test fun testParcelling_mixedTasks() { val taskInfo = GroupedTaskInfo.forMixed(listOf( freeformTasksGroupInfo(freeformTaskIds = arrayOf(4, 5, 6), deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(4, 5, 6), minimizedTaskIds = arrayOf(5)), splitTasksGroupInfo(firstId = 2, secondId = 3), singleTaskGroupInfo(id = 1))) Loading @@ -239,7 +240,7 @@ class GroupedTaskInfoTest : ShellTestCase() { // Read the object back from the parcel val taskInfoFromParcel: GroupedTaskInfo = GroupedTaskInfo.CREATOR.createFromParcel(parcel) assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue() assertThat(taskInfoFromParcel.isBaseType(TYPE_DESK)).isTrue() assertThat(taskInfoFromParcel.baseGroupedTask.minimizedTaskIds).isEqualTo( arrayOf(5).toIntArray()) for (i in 1..6) { Loading Loading @@ -275,12 +276,14 @@ class GroupedTaskInfoTest : ShellTestCase() { } @Test fun testTaskProperties_freeformTasks() { fun testTaskProperties_DeskTasks() { val task1 = createTaskInfo(id = 1) val task2 = createTaskInfo(id = 2) val taskInfo = GroupedTaskInfo.forFreeformTasks(listOf(task1, task2), setOf()) val taskInfo = GroupedTaskInfo.forDeskTasks( /* deskId = */ 500, listOf(task1, task2), setOf()) assertThat(taskInfo.deskId).isEqualTo(500) assertThat(taskInfo.getTaskById(1)).isEqualTo(task1) assertThat(taskInfo.getTaskById(2)).isEqualTo(task2) assertThat(taskInfo.containsTask(1)).isTrue() Loading Loading @@ -325,11 +328,13 @@ class GroupedTaskInfoTest : ShellTestCase() { return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds) } private fun freeformTasksGroupInfo( private fun deskTasksGroupInfo( deskId: Int, freeformTaskIds: Array<Int>, minimizedTaskIds: Array<Int> = emptyArray() ): GroupedTaskInfo { return GroupedTaskInfo.forFreeformTasks( return GroupedTaskInfo.forDeskTasks( deskId, freeformTaskIds.map { createTaskInfo(it) }.toList(), minimizedTaskIds.toSet()) } Loading @@ -346,9 +351,9 @@ class GroupedTaskInfoTest : ShellTestCase() { singleTaskGroupInfo(id = 1))) } private fun mixedTaskGroupInfoWithFreeformBase(): GroupedTaskInfo { private fun mixedTaskGroupInfoWithDeskBase(): GroupedTaskInfo { return GroupedTaskInfo.forMixed(listOf( freeformTasksGroupInfo(freeformTaskIds = arrayOf(2, 3, 4)), deskTasksGroupInfo(deskId = 0, freeformTaskIds = arrayOf(2, 3, 4)), singleTaskGroupInfo(id = 1))) } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +4 −4 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_DESK; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN; import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; Loading Loading @@ -349,7 +349,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); Loading Loading @@ -389,7 +389,7 @@ public class RecentTasksControllerTest extends ShellTestCase { // Check that groups have expected types assertTrue(splitGroup.isBaseType(TYPE_SPLIT)); assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup.isBaseType(TYPE_FULLSCREEN)); // Check freeform group entries Loading Loading @@ -460,7 +460,7 @@ public class RecentTasksControllerTest extends ShellTestCase { GroupedTaskInfo freeformGroup = recentTasks.get(2); // Check that groups have expected types assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM)); assertTrue(freeformGroup.isBaseType(TYPE_DESK)); assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN)); assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN)); Loading