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
This commit is contained in:
Ats Jenk
2022-11-09 15:27:20 -08:00
parent dcb12923fc
commit fc55874607
4 changed files with 202 additions and 298 deletions

View File

@@ -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<RunningTaskInfo> getRunningTasks(int displayId) {
ArrayList<RunningTaskInfo> 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;

View File

@@ -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<DesktopModeControll
int displayId = mContext.getDisplayId();
ArrayList<RunningTaskInfo> 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<DesktopModeControll
}
}
private void prepareWindowingModeChange(WindowContainerTransaction wct,
int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer
.getDisplayAreaInfo(displayId);
private WindowContainerTransaction clearBoundsForStandardTasks(
ArrayList<RunningTaskInfo> 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<RunningTaskInfo> 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);

View File

@@ -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;
}
}
}

View File

@@ -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<WindowContainerTransaction> 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<WindowContainerTransaction> 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<WindowContainerTransaction> 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<WindowContainerTransaction> 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;
}
}
}