From 789c3395b6a5093c99bda06581437f909be59a3c Mon Sep 17 00:00:00 2001 From: Jorge Gil Date: Wed, 30 Nov 2022 00:05:50 +0000 Subject: [PATCH 1/2] Sort tasks by Z-order instead of lastActiveTime lastActiveTime is not a reliable way to determine the Z order of freeform tasks because it misses Z order changes of already visible tasks (e.g. when bringing a semi occluded task to the top). This change keeps track of the freeform task's Z-order by observing TaskListener's appear/vanish and focus changes. Bug: 258056163 Test: atest DesktopModeControllerTest DesktopModeTaskRepositoryTest Test: manual: 1. Clear all open tasks and enable desktop mode 2. Launch YT from taskbar 3. Launch Gmail from taskbar 4. Tap on YT to bring to front 5. Launch Photos from taskbar Top->bottom order is Photos, YT, Gmail Change-Id: I6f2751b32f43d7b8ac2f97ce4c06be7ffa4dec11 --- .../desktopmode/DesktopModeController.java | 10 ++++-- .../desktopmode/DesktopModeTaskRepository.kt | 26 +++++++++++++++ .../shell/freeform/FreeformTaskListener.java | 32 +++++++++++++++---- .../DesktopModeControllerTest.java | 16 +++++----- .../DesktopModeTaskRepositoryTest.kt | 26 +++++++++++++++ 5 files changed, 93 insertions(+), 17 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index abc4024bc2903..9707c964c849d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -64,6 +64,7 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.Comparator; +import java.util.List; import java.util.concurrent.Executor; /** @@ -267,9 +268,12 @@ public class DesktopModeController implements RemoteCallable -task.lastActiveTime)); - WindowContainerTransaction wct = new WindowContainerTransaction(); + final List allTasksInZOrder = + mDesktopModeTaskRepository.getFreeformTasksInZOrder(); + // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last + // in the WCT. + taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId))); + final WindowContainerTransaction wct = new WindowContainerTransaction(); for (RunningTaskInfo task : taskInfos) { wct.reorder(task.token, true); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index b7749fc4c3d44..8ec5251bdc818 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -33,6 +33,8 @@ class DesktopModeTaskRepository { */ private val activeTasks = ArraySet() private val visibleTasks = ArraySet() + // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). + private val freeformTasksInZOrder = mutableListOf() private val activeTasksListeners = ArraySet() // Track visible tasks separately because a task may be part of the desktop but not visible. private val visibleTasksListeners = ArrayMap() @@ -107,6 +109,13 @@ class DesktopModeTaskRepository { return ArraySet(activeTasks) } + /** + * Get a list of freeform tasks, ordered from top-bottom (top at index 0). + */ + fun getFreeformTasksInZOrder(): List { + return freeformTasksInZOrder + } + /** * Updates whether a freeform task with this id is visible or not and notifies listeners. */ @@ -126,6 +135,23 @@ class DesktopModeTaskRepository { } } + /** + * Add (or move if it already exists) the task to the top of the ordered list. + */ + fun addOrMoveFreeformTaskToTop(taskId: Int) { + if (freeformTasksInZOrder.contains(taskId)) { + freeformTasksInZOrder.remove(taskId) + } + freeformTasksInZOrder.add(0, taskId) + } + + /** + * Remove the task from the ordered list. + */ + fun removeFreeformTask(taskId: Int) { + freeformTasksInZOrder.remove(taskId) + } + /** * Defines interface for classes that can listen to changes for active tasks in desktop mode. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java index 90b35a5a55e1d..7af4cb69003a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java @@ -38,7 +38,8 @@ import java.util.Optional; * {@link ShellTaskOrganizer.TaskListener} for {@link * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. */ -public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { +public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener, + ShellTaskOrganizer.FocusListener { private static final String TAG = "FreeformTaskListener"; private final ShellTaskOrganizer mShellTaskOrganizer; @@ -67,6 +68,9 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { private void onInit() { mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM); + if (DesktopModeStatus.IS_SUPPORTED) { + mShellTaskOrganizer.addFocusListener(this); + } } @Override @@ -86,13 +90,16 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { t.apply(); } - if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) { + if (DesktopModeStatus.IS_SUPPORTED) { mDesktopModeTaskRepository.ifPresent(repository -> { - if (repository.addActiveTask(taskInfo.taskId)) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, - "Adding active freeform task: #%d", taskInfo.taskId); + repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); + if (taskInfo.isVisible) { + if (repository.addActiveTask(taskInfo.taskId)) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "Adding active freeform task: #%d", taskInfo.taskId); + } + repository.updateVisibleFreeformTasks(taskInfo.taskId, true); } - repository.updateVisibleFreeformTasks(taskInfo.taskId, true); }); } } @@ -105,6 +112,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { if (DesktopModeStatus.IS_SUPPORTED) { mDesktopModeTaskRepository.ifPresent(repository -> { + repository.removeFreeformTask(taskInfo.taskId); if (repository.removeActiveTask(taskInfo.taskId)) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, "Removing active freeform task: #%d", taskInfo.taskId); @@ -139,6 +147,18 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { } } + @Override + public void onFocusTaskChanged(RunningTaskInfo taskInfo) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, + "Freeform Task Focus Changed: #%d focused=%b", + taskInfo.taskId, taskInfo.isFocused); + if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isFocused) { + mDesktopModeTaskRepository.ifPresent(repository -> { + repository.addOrMoveFreeformTaskToTop(taskInfo.taskId); + }); + } + } + @Override public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { b.setParent(findTaskSurface(taskId)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 89bafcb6b2f45..210f09e22efb8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -234,13 +234,13 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testShowDesktopApps() { - // Set up two active tasks on desktop + // Set up two active tasks on desktop, task2 is on top of task1. RunningTaskInfo freeformTask1 = createFreeformTask(); - freeformTask1.lastActiveTime = 100; - RunningTaskInfo freeformTask2 = createFreeformTask(); - freeformTask2.lastActiveTime = 200; mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId); + RunningTaskInfo freeformTask2 = createFreeformTask(); mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( freeformTask1); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( @@ -260,15 +260,15 @@ public class DesktopModeControllerTest extends ShellTestCase { // Check wct has reorder calls assertThat(wct.getHierarchyOps()).hasSize(2); - // Task 2 has activity later, must be first + // Task 1 appeared first, must be first reorder to top. WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(freeformTask2.token.asBinder()); + assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder()); - // Task 1 should be second + // Task 2 appeared last, must be last reorder to top. WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(freeformTask1.token.asBinder()); + assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index aaa5c8a35acb8..1e43a5983821a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -140,6 +140,32 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(3) } + @Test + fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() { + repo.addOrMoveFreeformTaskToTop(5) + repo.addOrMoveFreeformTaskToTop(6) + repo.addOrMoveFreeformTaskToTop(7) + + val tasks = repo.getFreeformTasksInZOrder() + assertThat(tasks.size).isEqualTo(3) + assertThat(tasks[0]).isEqualTo(7) + assertThat(tasks[1]).isEqualTo(6) + assertThat(tasks[2]).isEqualTo(5) + } + + @Test + fun addOrMoveFreeformTaskToTop_alreadyExists_movesToTop() { + repo.addOrMoveFreeformTaskToTop(5) + repo.addOrMoveFreeformTaskToTop(6) + repo.addOrMoveFreeformTaskToTop(7) + + repo.addOrMoveFreeformTaskToTop(6) + + val tasks = repo.getFreeformTasksInZOrder() + assertThat(tasks.size).isEqualTo(3) + assertThat(tasks.first()).isEqualTo(6) + } + class TestListener : DesktopModeTaskRepository.ActiveTasksListener { var activeTaskChangedCalls = 0 override fun onActiveTasksChanged() { From fa5835246451e04dfbd5c32bc2a7c9126e58f1b6 Mon Sep 17 00:00:00 2001 From: Jorge Gil Date: Thu, 1 Dec 2022 18:32:50 +0000 Subject: [PATCH 2/2] Only bring desktop app to front when necessary If the active desktops apps are already in the front, then there's no need to add WCT#reorder operations for every desktop app. Instead, check first whether there is an invisble app, and if there is, reorder all apps so that the Z order is maintained. Bug: 255344915 Bug: 258056163 Test: atest DesktopModeControllerTest Test: manual - in desktop mode, 1. Launch Gmail 2. Launch Photos 3. Observe there is no reorder triggered anymore Change-Id: Ib2cc8d900d1d0ccc8b9e2b49b95f1708531aa398 --- .../desktopmode/DesktopModeController.java | 21 ++++- .../desktopmode/DesktopModeTaskRepository.kt | 7 ++ .../DesktopModeControllerTest.java | 81 ++++++++++++++++--- 3 files changed, 93 insertions(+), 16 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index 9707c964c849d..17ba246910d5d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -259,21 +259,36 @@ public class DesktopModeController implements RemoteCallable activeTasks = mDesktopModeTaskRepository.getActiveTasks(); + final WindowContainerTransaction wct = new WindowContainerTransaction(); + final ArraySet activeTasks = mDesktopModeTaskRepository.getActiveTasks(); ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size()); - ArrayList taskInfos = new ArrayList<>(); + + final List taskInfos = new ArrayList<>(); for (Integer taskId : activeTasks) { RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId); if (taskInfo != null) { taskInfos.add(taskInfo); } } + + if (taskInfos.isEmpty()) { + return wct; + } + + final boolean allActiveTasksAreVisible = taskInfos.stream() + .allMatch(info -> mDesktopModeTaskRepository.isVisibleTask(info.taskId)); + if (allActiveTasksAreVisible) { + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: active tasks are already in front, skipping."); + return wct; + } + ProtoLog.d(WM_SHELL_DESKTOP_MODE, + "bringDesktopAppsToFront: reordering all active tasks to the front"); final List allTasksInZOrder = mDesktopModeTaskRepository.getFreeformTasksInZOrder(); // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last // in the WCT. taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId))); - final WindowContainerTransaction wct = new WindowContainerTransaction(); for (RunningTaskInfo task : taskInfos) { wct.reorder(task.token, true); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 8ec5251bdc818..600ccc17ecaaf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -102,6 +102,13 @@ class DesktopModeTaskRepository { return activeTasks.contains(taskId) } + /** + * Whether a task is visible. + */ + fun isVisibleTask(taskId: Int): Boolean { + return visibleTasks.contains(taskId) + } + /** * Get a set of the active tasks */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index 210f09e22efb8..01584a067cc2a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -50,6 +50,7 @@ import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction.Change; +import android.window.WindowContainerTransaction.HierarchyOp; import androidx.test.filters.SmallTest; @@ -222,25 +223,29 @@ public class DesktopModeControllerTest extends ShellTestCase { // Check that there are hierarchy changes for home task and visible task assertThat(wct.getHierarchyOps()).hasSize(2); // First show home task - WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder()); // Then visible task on top of it - WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); + HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder()); } @Test - public void testShowDesktopApps() { + public void testShowDesktopApps_allAppsInvisible_bringsToFront() { // Set up two active tasks on desktop, task2 is on top of task1. RunningTaskInfo freeformTask1 = createFreeformTask(); mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + freeformTask1.taskId, false /* visible */); RunningTaskInfo freeformTask2 = createFreeformTask(); mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId); mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks( + freeformTask2.taskId, false /* visible */); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( freeformTask1); when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( @@ -248,29 +253,68 @@ public class DesktopModeControllerTest extends ShellTestCase { // Run show desktop apps logic mController.showDesktopApps(); - ArgumentCaptor wctCaptor = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), wctCaptor.capture(), any()); - } else { - verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture()); - } - WindowContainerTransaction wct = wctCaptor.getValue(); + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); // Check wct has reorder calls assertThat(wct.getHierarchyOps()).hasSize(2); // Task 1 appeared first, must be first reorder to top. - WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); + HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder()); // Task 2 appeared last, must be last reorder to top. - WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); + HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder()); } + @Test + public void testShowDesktopApps_appsAlreadyVisible_doesNothing() { + final RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); + final RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); + + mController.showDesktopApps(); + + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); + // No reordering needed. + assertThat(wct.getHierarchyOps()).isEmpty(); + } + + @Test + public void testShowDesktopApps_someAppsInvisible_reordersAll() { + final RunningTaskInfo task1 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task1.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1); + final RunningTaskInfo task2 = createFreeformTask(); + mDesktopModeTaskRepository.addActiveTask(task2.taskId); + mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId); + mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */); + when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2); + + mController.showDesktopApps(); + + final WindowContainerTransaction wct = getBringAppsToFrontTransaction(); + // Both tasks should be reordered to top, even if one was already visible. + assertThat(wct.getHierarchyOps()).hasSize(2); + final HierarchyOp op1 = wct.getHierarchyOps().get(0); + assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder()); + final HierarchyOp op2 = wct.getHierarchyOps().get(1); + assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); + assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder()); + } + @Test public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() { when(DesktopModeStatus.isActive(any())).thenReturn(false); @@ -355,6 +399,17 @@ public class DesktopModeControllerTest extends ShellTestCase { return arg.getValue(); } + private WindowContainerTransaction getBringAppsToFrontTransaction() { + final ArgumentCaptor arg = ArgumentCaptor.forClass( + WindowContainerTransaction.class); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), arg.capture(), any()); + } else { + verify(mShellTaskOrganizer).applyTransaction(arg.capture()); + } + return arg.getValue(); + } + private void assertThatBoundsCleared(Change change) { assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue(); assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();