From fc55874607cc7657b087f1eb47821b42d6d5d55b Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Wed, 9 Nov 2022 15:27:20 -0800 Subject: [PATCH] Move desktop mode logic out of ShellTaskOrganizer Expose list of running tasks per display from ShellTaskOrganizer. This allows us to move desktop mode related logic out from ShellTaskOrganizer and to DesktopModeController. Bug: 258282673 Test: atest DesktopModeControllerTest ShellTaskOrganizerTests Change-Id: I179c69c185153a07de3602627ed18734420abda2 --- .../android/wm/shell/ShellTaskOrganizer.java | 70 ++--- .../desktopmode/DesktopModeController.java | 52 +++- .../wm/shell/ShellTaskOrganizerTests.java | 130 --------- .../DesktopModeControllerTest.java | 248 +++++++++++------- 4 files changed, 202 insertions(+), 298 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 43679364b4434..e58e785850fac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -16,14 +16,12 @@ package com.android.wm.shell; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; @@ -48,7 +46,6 @@ import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; -import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -567,6 +564,22 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } } + /** + * Return list of {@link RunningTaskInfo}s for the given display. + * + * @return filtered list of tasks or empty list + */ + public ArrayList getRunningTasks(int displayId) { + ArrayList result = new ArrayList<>(); + for (int i = 0; i < mTasks.size(); i++) { + RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); + if (taskInfo.displayId == displayId) { + result.add(taskInfo); + } + } + return result; + } + /** Gets running task by taskId. Returns {@code null} if no such task observed. */ @Nullable public RunningTaskInfo getRunningTaskInfo(int taskId) { @@ -693,57 +706,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements taskListener.reparentChildSurfaceToTask(taskId, sc, t); } - /** - * Create a {@link WindowContainerTransaction} to clear task bounds. - * - * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to - * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. - * - * @param displayId display id for tasks that will have bounds cleared - * @return {@link WindowContainerTransaction} with pending operations to clear bounds - */ - public WindowContainerTransaction prepareClearBoundsForStandardTasks(int displayId) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks: displayId=%d", displayId); - WindowContainerTransaction wct = new WindowContainerTransaction(); - for (int i = 0; i < mTasks.size(); i++) { - RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); - if ((taskInfo.displayId == displayId) && (taskInfo.getActivityType() - == WindowConfiguration.ACTIVITY_TYPE_STANDARD)) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s", - taskInfo.token, taskInfo); - wct.setBounds(taskInfo.token, null); - } - } - return wct; - } - - /** - * Create a {@link WindowContainerTransaction} to clear task level freeform setting. - * - * Only affects tasks that have {@link RunningTaskInfo#getActivityType()} set to - * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}. - * - * @param displayId display id for tasks that will have windowing mode reset to {@link - * WindowConfiguration#WINDOWING_MODE_UNDEFINED} - * @return {@link WindowContainerTransaction} with pending operations to clear windowing mode - */ - public WindowContainerTransaction prepareClearFreeformForStandardTasks(int displayId) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks: displayId=%d", displayId); - WindowContainerTransaction wct = new WindowContainerTransaction(); - for (int i = 0; i < mTasks.size(); i++) { - RunningTaskInfo taskInfo = mTasks.valueAt(i).getTaskInfo(); - if (taskInfo.displayId == displayId - && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) { - ProtoLog.d(WM_SHELL_DESKTOP_MODE, - "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token, - taskInfo); - wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); - } - } - return wct; - } - private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info, int event) { ActivityInfo topActivityInfo = info.getTaskInfo().topActivityInfo; 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 34ff6d814c8d3..f0255a335d5e4 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 @@ -16,8 +16,10 @@ package com.android.wm.shell.desktopmode; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -151,21 +153,20 @@ public class DesktopModeController implements RemoteCallable runningTasks = mShellTaskOrganizer.getRunningTasks(displayId); + WindowContainerTransaction wct = new WindowContainerTransaction(); - // Reset freeform windowing mode that is set per task level (tasks should inherit - // container value) - wct.merge(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(displayId), - true /* transfer */); + // Reset freeform windowing mode that is set per task level so tasks inherit it + clearFreeformForStandardTasks(runningTasks, wct); int targetWindowingMode; if (active) { targetWindowingMode = WINDOWING_MODE_FREEFORM; } else { targetWindowingMode = WINDOWING_MODE_FULLSCREEN; // Clear any resized bounds - wct.merge(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(displayId), - true /* transfer */); + clearBoundsForStandardTasks(runningTasks, wct); } - prepareWindowingModeChange(wct, displayId, targetWindowingMode); + setDisplayAreaWindowingMode(displayId, targetWindowingMode, wct); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mTransitions.startTransition(TRANSIT_CHANGE, wct, null); } else { @@ -173,17 +174,44 @@ public class DesktopModeController implements RemoteCallable runningTasks, WindowContainerTransaction wct) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearBoundsForTasks"); + for (RunningTaskInfo taskInfo : runningTasks) { + if (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "clearing bounds for token=%s taskInfo=%s", + taskInfo.token, taskInfo); + wct.setBounds(taskInfo.token, null); + } + } + return wct; + } + + private void clearFreeformForStandardTasks(ArrayList runningTasks, + WindowContainerTransaction wct) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "prepareClearFreeformForTasks"); + for (RunningTaskInfo taskInfo : runningTasks) { + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM + && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) { + ProtoLog.v(WM_SHELL_DESKTOP_MODE, + "clearing windowing mode for token=%s taskInfo=%s", taskInfo.token, + taskInfo); + wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); + } + } + } + + private void setDisplayAreaWindowingMode(int displayId, + @WindowConfiguration.WindowingMode int windowingMode, WindowContainerTransaction wct) { + DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + displayId); if (displayAreaInfo == null) { ProtoLog.e(WM_SHELL_DESKTOP_MODE, "unable to update windowing mode for display %d display not found", displayId); return; } - ProtoLog.d(WM_SHELL_DESKTOP_MODE, + ProtoLog.v(WM_SHELL_DESKTOP_MODE, "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId, displayAreaInfo.configuration.windowConfiguration.getWindowingMode(), windowingMode); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 7cbace5af48f7..081c8ae91bdb1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -16,13 +16,9 @@ package com.android.wm.shell; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; @@ -34,8 +30,6 @@ import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIO import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; @@ -44,11 +38,9 @@ import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; -import android.app.WindowConfiguration; import android.content.LocusId; import android.content.pm.ParceledListSlice; import android.os.Binder; @@ -61,8 +53,6 @@ import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; -import android.window.WindowContainerTransaction.Change; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -638,130 +628,10 @@ public class ShellTaskOrganizerTests extends ShellTestCase { verify(mTaskOrganizerController).restartTaskTopActivityProcessIfVisible(task1.token); } - @Test - public void testPrepareClearBoundsForStandardTasks() { - MockToken token1 = new MockToken(); - RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1); - mOrganizer.onTaskAppeared(task1, null); - - MockToken token2 = new MockToken(); - RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2); - mOrganizer.onTaskAppeared(task2, null); - - MockToken otherDisplayToken = new MockToken(); - RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_UNDEFINED, - otherDisplayToken); - otherDisplayTask.displayId = 2; - mOrganizer.onTaskAppeared(otherDisplayTask, null); - - WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1); - - assertEquals(wct.getChanges().size(), 2); - Change boundsChange1 = wct.getChanges().get(token1.binder()); - assertNotNull(boundsChange1); - assertNotEquals( - (boundsChange1.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0); - assertTrue(boundsChange1.getConfiguration().windowConfiguration.getBounds().isEmpty()); - - Change boundsChange2 = wct.getChanges().get(token2.binder()); - assertNotNull(boundsChange2); - assertNotEquals( - (boundsChange2.getWindowSetMask() & WindowConfiguration.WINDOW_CONFIG_BOUNDS), 0); - assertTrue(boundsChange2.getConfiguration().windowConfiguration.getBounds().isEmpty()); - } - - @Test - public void testPrepareClearBoundsForStandardTasks_onlyClearActivityTypeStandard() { - MockToken token1 = new MockToken(); - RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_UNDEFINED, token1); - mOrganizer.onTaskAppeared(task1, null); - - MockToken token2 = new MockToken(); - RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_UNDEFINED, token2); - task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME); - mOrganizer.onTaskAppeared(task2, null); - - WindowContainerTransaction wct = mOrganizer.prepareClearBoundsForStandardTasks(1); - - // Only clear bounds for task1 - assertEquals(1, wct.getChanges().size()); - assertNotNull(wct.getChanges().get(token1.binder())); - } - - @Test - public void testPrepareClearFreeformForStandardTasks() { - MockToken token1 = new MockToken(); - RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1); - mOrganizer.onTaskAppeared(task1, null); - - MockToken token2 = new MockToken(); - RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW, token2); - mOrganizer.onTaskAppeared(task2, null); - - MockToken otherDisplayToken = new MockToken(); - RunningTaskInfo otherDisplayTask = createTaskInfo(3, WINDOWING_MODE_FREEFORM, - otherDisplayToken); - otherDisplayTask.displayId = 2; - mOrganizer.onTaskAppeared(otherDisplayTask, null); - - WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1); - - // Only task with freeform windowing mode and the right display should be updated - assertEquals(wct.getChanges().size(), 1); - Change wmModeChange1 = wct.getChanges().get(token1.binder()); - assertNotNull(wmModeChange1); - assertEquals(wmModeChange1.getWindowingMode(), WINDOWING_MODE_UNDEFINED); - } - - @Test - public void testPrepareClearFreeformForStandardTasks_onlyClearActivityTypeStandard() { - MockToken token1 = new MockToken(); - RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FREEFORM, token1); - mOrganizer.onTaskAppeared(task1, null); - - MockToken token2 = new MockToken(); - RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_FREEFORM, token2); - task2.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME); - mOrganizer.onTaskAppeared(task2, null); - - WindowContainerTransaction wct = mOrganizer.prepareClearFreeformForStandardTasks(1); - - // Only clear freeform for task1 - assertEquals(1, wct.getChanges().size()); - assertNotNull(wct.getChanges().get(token1.binder())); - } - private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); return taskInfo; } - - private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, MockToken token) { - RunningTaskInfo taskInfo = createTaskInfo(taskId, windowingMode); - taskInfo.displayId = 1; - taskInfo.token = token.token(); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD); - return taskInfo; - } - - private static class MockToken { - private final WindowContainerToken mToken; - private final IBinder mBinder; - - MockToken() { - mToken = mock(WindowContainerToken.class); - mBinder = mock(IBinder.class); - when(mToken.asBinder()).thenReturn(mBinder); - } - - WindowContainerToken token() { - return mToken; - } - - IBinder binder() { - return mBinder; - } - } } 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 79b520c734c84..528b26ec2362d 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 @@ -16,10 +16,13 @@ package com.android.wm.shell.desktopmode; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; +import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; @@ -30,13 +33,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -68,6 +72,9 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; +import java.util.ArrayList; +import java.util.Arrays; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DesktopModeControllerTest extends ShellTestCase { @@ -83,9 +90,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Mock private Handler mMockHandler; @Mock - private Transitions mMockTransitions; - private TestShellExecutor mExecutor; - + private Transitions mTransitions; private DesktopModeController mController; private DesktopModeTaskRepository mDesktopModeTaskRepository; private ShellInit mShellInit; @@ -97,20 +102,19 @@ public class DesktopModeControllerTest extends ShellTestCase { when(DesktopModeStatus.isActive(any())).thenReturn(true); mShellInit = Mockito.spy(new ShellInit(mTestExecutor)); - mExecutor = new TestShellExecutor(); mDesktopModeTaskRepository = new DesktopModeTaskRepository(); mController = new DesktopModeController(mContext, mShellInit, mShellController, - mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mMockTransitions, - mDesktopModeTaskRepository, mMockHandler, mExecutor); + mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions, + mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor()); - when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn( - new WindowContainerTransaction()); + when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>()); mShellInit.init(); clearInvocations(mShellTaskOrganizer); clearInvocations(mRootTaskDisplayAreaOrganizer); + clearInvocations(mTransitions); } @After @@ -124,113 +128,106 @@ public class DesktopModeControllerTest extends ShellTestCase { } @Test - public void testDesktopModeEnabled_taskWmClearedDisplaySetToFreeform() { - // Create a fake WCT to simulate setting task windowing mode to undefined - WindowContainerTransaction taskWct = new WindowContainerTransaction(); - MockToken taskMockToken = new MockToken(); - taskWct.setWindowingMode(taskMockToken.token(), WINDOWING_MODE_UNDEFINED); - when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks( - mContext.getDisplayId())).thenReturn(taskWct); + public void testDesktopModeEnabled_rootTdaSetToFreeform() { + DisplayAreaInfo displayAreaInfo = createMockDisplayArea(); - // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly - MockToken displayMockToken = new MockToken(); - DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken, - mContext.getDisplayId(), 0); - when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) - .thenReturn(displayAreaInfo); - - // The test mController.updateDesktopModeActive(true); + WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - ArgumentCaptor arg = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture()); - - // WCT should have 2 changes - clear task wm mode and set display wm mode - WindowContainerTransaction wct = arg.getValue(); - assertThat(wct.getChanges()).hasSize(2); - - // Verify executed WCT has a change for setting task windowing mode to undefined - Change taskWmModeChange = wct.getChanges().get(taskMockToken.binder()); - assertThat(taskWmModeChange).isNotNull(); - assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); - - // Verify executed WCT has a change for setting display windowing mode to freeform - Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder()); - assertThat(displayWmModeChange).isNotNull(); - assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM); + // 1 change: Root TDA windowing mode + assertThat(wct.getChanges().size()).isEqualTo(1); + // Verify WCT has a change for setting windowing mode to freeform + Change change = wct.getChanges().get(displayAreaInfo.token.asBinder()); + assertThat(change).isNotNull(); + assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM); } @Test - public void testDesktopModeDisabled_taskWmAndBoundsClearedDisplaySetToFullscreen() { - // Create a fake WCT to simulate setting task windowing mode to undefined - WindowContainerTransaction taskWmWct = new WindowContainerTransaction(); - MockToken taskWmMockToken = new MockToken(); - taskWmWct.setWindowingMode(taskWmMockToken.token(), WINDOWING_MODE_UNDEFINED); - when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks( - mContext.getDisplayId())).thenReturn(taskWmWct); + public void testDesktopModeDisabled_rootTdaSetToFullscreen() { + DisplayAreaInfo displayAreaInfo = createMockDisplayArea(); - // Create a fake WCT to simulate clearing task bounds - WindowContainerTransaction taskBoundsWct = new WindowContainerTransaction(); - MockToken taskBoundsMockToken = new MockToken(); - taskBoundsWct.setBounds(taskBoundsMockToken.token(), null); - when(mShellTaskOrganizer.prepareClearBoundsForStandardTasks( - mContext.getDisplayId())).thenReturn(taskBoundsWct); - - // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly - MockToken displayMockToken = new MockToken(); - DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken, - mContext.getDisplayId(), 0); - when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) - .thenReturn(displayAreaInfo); - - // The test mController.updateDesktopModeActive(false); + WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - ArgumentCaptor arg = ArgumentCaptor.forClass( - WindowContainerTransaction.class); - verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture()); + // 1 change: Root TDA windowing mode + assertThat(wct.getChanges().size()).isEqualTo(1); + // Verify WCT has a change for setting windowing mode to fullscreen + Change change = wct.getChanges().get(displayAreaInfo.token.asBinder()); + assertThat(change).isNotNull(); + assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); + } - // WCT should have 3 changes - clear task wm mode and bounds and set display wm mode - WindowContainerTransaction wct = arg.getValue(); - assertThat(wct.getChanges()).hasSize(3); + @Test + public void testDesktopModeEnabled_windowingModeCleared() { + createMockDisplayArea(); + RunningTaskInfo freeformTask = createFreeformTask(); + RunningTaskInfo fullscreenTask = createFullscreenTask(); + RunningTaskInfo homeTask = createHomeTask(); + when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>( + Arrays.asList(freeformTask, fullscreenTask, homeTask))); - // Verify executed WCT has a change for setting task windowing mode to undefined - Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder()); - assertThat(taskWmMode).isNotNull(); - assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); + mController.updateDesktopModeActive(true); + WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); - // Verify executed WCT has a change for clearing task bounds - Change bounds = wct.getChanges().get(taskBoundsMockToken.binder()); - assertThat(bounds).isNotNull(); - assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0); - assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue(); + // 2 changes: Root TDA windowing mode and 1 task + assertThat(wct.getChanges().size()).isEqualTo(2); + // No changes for tasks that are not standard or freeform + assertThat(wct.getChanges().get(fullscreenTask.token.asBinder())).isNull(); + assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull(); + // Standard freeform task has windowing mode cleared + Change change = wct.getChanges().get(freeformTask.token.asBinder()); + assertThat(change).isNotNull(); + assertThat(change.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); + } - // Verify executed WCT has a change for setting display windowing mode to fullscreen - Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder()); - assertThat(displayWmModeChange).isNotNull(); - assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); + @Test + public void testDesktopModeDisabled_windowingModeAndBoundsCleared() { + createMockDisplayArea(); + RunningTaskInfo freeformTask = createFreeformTask(); + RunningTaskInfo fullscreenTask = createFullscreenTask(); + RunningTaskInfo homeTask = createHomeTask(); + when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>( + Arrays.asList(freeformTask, fullscreenTask, homeTask))); + + mController.updateDesktopModeActive(false); + WindowContainerTransaction wct = getDesktopModeSwitchTransaction(); + + // 3 changes: Root TDA windowing mode and 2 tasks + assertThat(wct.getChanges().size()).isEqualTo(3); + // No changes to home task + assertThat(wct.getChanges().get(homeTask.token.asBinder())).isNull(); + // Standard tasks have bounds cleared + assertThatBoundsCleared(wct.getChanges().get(freeformTask.token.asBinder())); + assertThatBoundsCleared(wct.getChanges().get(fullscreenTask.token.asBinder())); + // Freeform standard tasks have windowing mode cleared + assertThat(wct.getChanges().get( + freeformTask.token.asBinder()).getWindowingMode()).isEqualTo( + WINDOWING_MODE_UNDEFINED); } @Test public void testShowDesktopApps() { // Set up two active tasks on desktop - mDesktopModeTaskRepository.addActiveTask(1); - mDesktopModeTaskRepository.addActiveTask(2); - MockToken token1 = new MockToken(); - MockToken token2 = new MockToken(); - ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken( - token1.token()).setLastActiveTime(100).build(); - ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken( - token2.token()).setLastActiveTime(200).build(); - when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1); - when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2); + RunningTaskInfo freeformTask1 = createFreeformTask(); + freeformTask1.lastActiveTime = 100; + RunningTaskInfo freeformTask2 = createFreeformTask(); + freeformTask2.lastActiveTime = 200; + mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId); + mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId); + when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn( + freeformTask1); + when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn( + freeformTask2); // Run show desktop apps logic mController.showDesktopApps(); ArgumentCaptor wctCaptor = ArgumentCaptor.forClass( WindowContainerTransaction.class); - verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture()); + 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(); // Check wct has reorder calls @@ -239,12 +236,12 @@ public class DesktopModeControllerTest extends ShellTestCase { // Task 2 has activity later, must be first WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0); assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op1.getContainer()).isEqualTo(token2.binder()); + assertThat(op1.getContainer()).isEqualTo(freeformTask2.token.asBinder()); // Task 1 should be second - WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0); + WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1); assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER); - assertThat(op2.getContainer()).isEqualTo(token2.binder()); + assertThat(op2.getContainer()).isEqualTo(freeformTask1.token.asBinder()); } @Test @@ -266,7 +263,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_notFreeform_returnsNull() { - ActivityManager.RunningTaskInfo trigger = new ActivityManager.RunningTaskInfo(); + RunningTaskInfo trigger = new RunningTaskInfo(); trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); WindowContainerTransaction wct = mController.handleRequest( new Binder(), @@ -276,7 +273,7 @@ public class DesktopModeControllerTest extends ShellTestCase { @Test public void testHandleTransitionRequest_returnsWct() { - ActivityManager.RunningTaskInfo trigger = new ActivityManager.RunningTaskInfo(); + RunningTaskInfo trigger = new RunningTaskInfo(); trigger.token = new MockToken().mToken; trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); WindowContainerTransaction wct = mController.handleRequest( @@ -285,6 +282,57 @@ public class DesktopModeControllerTest extends ShellTestCase { assertThat(wct).isNotNull(); } + private DisplayAreaInfo createMockDisplayArea() { + DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().mToken, + mContext.getDisplayId(), 0); + when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId())) + .thenReturn(displayAreaInfo); + return displayAreaInfo; + } + + private RunningTaskInfo createFreeformTask() { + return new TestRunningTaskInfoBuilder() + .setToken(new MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FREEFORM) + .setLastActiveTime(100) + .build(); + } + + private RunningTaskInfo createFullscreenTask() { + return new TestRunningTaskInfoBuilder() + .setToken(new MockToken().token()) + .setActivityType(ACTIVITY_TYPE_STANDARD) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build(); + } + + private RunningTaskInfo createHomeTask() { + return new TestRunningTaskInfoBuilder() + .setToken(new MockToken().token()) + .setActivityType(ACTIVITY_TYPE_HOME) + .setWindowingMode(WINDOWING_MODE_FULLSCREEN) + .setLastActiveTime(100) + .build(); + } + + private WindowContainerTransaction getDesktopModeSwitchTransaction() { + ArgumentCaptor arg = ArgumentCaptor.forClass( + WindowContainerTransaction.class); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + verify(mTransitions).startTransition(eq(TRANSIT_CHANGE), arg.capture(), any()); + } else { + verify(mRootTaskDisplayAreaOrganizer).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(); + } + private static class MockToken { private final WindowContainerToken mToken; private final IBinder mBinder; @@ -298,9 +346,5 @@ public class DesktopModeControllerTest extends ShellTestCase { WindowContainerToken token() { return mToken; } - - IBinder binder() { - return mBinder; - } } }