summaryrefslogtreecommitdiff
path: root/libs/WindowManager/Shell/tests
diff options
context:
space:
mode:
Diffstat (limited to 'libs/WindowManager/Shell/tests')
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/Android.bp3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml27
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java130
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java136
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java16
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java400
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt408
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt61
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java40
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java248
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java91
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java41
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java47
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java36
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java183
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java45
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java53
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java49
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java260
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt130
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java186
37 files changed, 2126 insertions, 707 deletions
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index f4efc374ecc2..1c28c3d58ccb 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -6,3 +6,4 @@ pablogamito@google.com
lbill@google.com
madym@google.com
hwwang@google.com
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index 1a8b9540cbd0..2ac1dc0c4838 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -69,6 +69,9 @@ android_test {
enabled: false,
},
+ platform_apis: true,
+ certificate: "platform",
+
aaptflags: [
"--extra-packages",
"com.android.wm.shell.tests",
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index 59d9104fb5ba..fac04614d945 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -19,6 +19,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.wm.shell.tests">
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.mock" />
<uses-library android:name="android.test.runner" />
diff --git a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
new file mode 100644
index 000000000000..8949a75d1a15
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <!-- Resources used in WindowDecorationTests -->
+ <dimen name="test_freeform_decor_caption_height">32dp</dimen>
+ <dimen name="test_freeform_decor_caption_width">216dp</dimen>
+ <dimen name="test_window_decor_left_outset">10dp</dimen>
+ <dimen name="test_window_decor_top_outset">20dp</dimen>
+ <dimen name="test_window_decor_right_outset">30dp</dimen>
+ <dimen name="test_window_decor_bottom_outset">40dp</dimen>
+ <dimen name="test_window_decor_shadow_radius">5dp</dimen>
+ <dimen name="test_window_decor_resize_handle">10dp</dimen>
+</resources> \ No newline at end of file
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 7cbace5af48f..081c8ae91bdb 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/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index 98b59126227c..79070b1469be 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -40,6 +40,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import java.util.ArrayList;
+
/**
* Tests for {@link ActivityEmbeddingAnimationRunner}.
*
@@ -62,13 +64,13 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
final TransitionInfo.Change embeddingChange = createChange();
embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
info.addChange(embeddingChange);
- doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any());
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any());
mAnimRunner.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction);
final ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class);
verify(mAnimRunner).createAnimator(eq(info), eq(mStartTransaction), eq(mFinishTransaction),
- finishCallback.capture());
+ finishCallback.capture(), any());
verify(mStartTransaction).apply();
verify(mAnimator).start();
verifyNoMoreInteractions(mFinishTransaction);
@@ -88,7 +90,8 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
info.addChange(embeddingChange);
final Animator animator = mAnimRunner.createAnimator(
info, mStartTransaction, mFinishTransaction,
- () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
+ () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */),
+ new ArrayList());
// The animation should be empty when it is behind starting window.
assertEquals(0, animator.getDuration());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
index 3792e8361284..54a12ab999c5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -56,13 +56,12 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
@Mock
SurfaceControl.Transaction mFinishTransaction;
@Mock
- Transitions.TransitionFinishCallback mFinishCallback;
- @Mock
Animator mAnimator;
ActivityEmbeddingController mController;
ActivityEmbeddingAnimationRunner mAnimRunner;
ActivityEmbeddingAnimationSpec mAnimSpec;
+ Transitions.TransitionFinishCallback mFinishCallback;
@CallSuper
@Before
@@ -75,9 +74,11 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
assertNotNull(mAnimRunner);
mAnimSpec = mAnimRunner.mAnimationSpec;
assertNotNull(mAnimSpec);
+ mFinishCallback = (wct, wctCB) -> {};
spyOn(mController);
spyOn(mAnimRunner);
spyOn(mAnimSpec);
+ spyOn(mFinishCallback);
}
/** Creates a mock {@link TransitionInfo.Change}. */
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index baecf6fe6673..4d98b6ba4f7a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -55,7 +55,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
@Before
public void setup() {
super.setUp();
- doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any());
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 90a377309edd..2e328b0736dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -63,7 +63,9 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
import org.junit.Ignore;
@@ -102,6 +104,9 @@ public class BackAnimationControllerTest extends ShellTestCase {
@Mock
private IBackNaviAnimationController mIBackNaviAnimationController;
+ @Mock
+ private ShellController mShellController;
+
private BackAnimationController mController;
private int mEventTime = 0;
@@ -118,10 +123,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
ANIMATION_ENABLED);
mTestableLooper = TestableLooper.get(this);
mShellInit = spy(new ShellInit(mShellExecutor));
- mController = new BackAnimationController(mShellInit,
+ mController = new BackAnimationController(mShellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
+ mController.setEnableUAnimation(true);
mShellInit.init();
mEventTime = 0;
mShellExecutor.flushAll();
@@ -175,6 +181,12 @@ public class BackAnimationControllerTest extends ShellTestCase {
}
@Test
+ public void instantiateController_addExternalInterface() {
+ verify(mShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_BACK_ANIMATION), any(), any());
+ }
+
+ @Test
@Ignore("b/207481538")
public void crossActivity_screenshotAttachedAndVisible() {
SurfaceControl screenshotSurface = new SurfaceControl();
@@ -234,10 +246,10 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted();
ArgumentCaptor<BackEvent> backEventCaptor = ArgumentCaptor.forClass(BackEvent.class);
- verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(backEventCaptor.capture());
+ verify(mIOnBackInvokedCallback).onBackStarted(backEventCaptor.capture());
assertEquals(animationTarget, backEventCaptor.getValue().getDepartingAnimationTarget());
+ verify(mIOnBackInvokedCallback, atLeastOnce()).onBackProgressed(any(BackEvent.class));
// Check that back invocation is dispatched.
mController.setTriggerBack(true); // Fake trigger back
@@ -250,7 +262,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Toggle the setting off
Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
ShellInit shellInit = new ShellInit(mShellExecutor);
- mController = new BackAnimationController(shellInit,
+ mController = new BackAnimationController(shellInit, mShellController,
mShellExecutor, new Handler(mTestableLooper.getLooper()), mTransaction,
mActivityTaskManager, mContext,
mContentResolver);
@@ -265,11 +277,11 @@ public class BackAnimationControllerTest extends ShellTestCase {
triggerBackGesture();
- verify(appCallback, never()).onBackStarted();
+ verify(appCallback, never()).onBackStarted(any(BackEvent.class));
verify(appCallback, never()).onBackProgressed(backEventCaptor.capture());
verify(appCallback, times(1)).onBackInvoked();
- verify(mIOnBackInvokedCallback, never()).onBackStarted();
+ verify(mIOnBackInvokedCallback, never()).onBackStarted(any(BackEvent.class));
verify(mIOnBackInvokedCallback, never()).onBackProgressed(backEventCaptor.capture());
verify(mIOnBackInvokedCallback, never()).onBackInvoked();
}
@@ -302,7 +314,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted();
+ verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
}
@Test
@@ -321,7 +333,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
doMotionEvent(MotionEvent.ACTION_DOWN, 0);
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted();
+ verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
}
@@ -337,7 +349,7 @@ public class BackAnimationControllerTest extends ShellTestCase {
// Check that back start and progress is dispatched when first move.
doMotionEvent(MotionEvent.ACTION_MOVE, 100);
simulateRemoteAnimationStart(BackNavigationInfo.TYPE_RETURN_TO_HOME, animationTarget);
- verify(mIOnBackInvokedCallback).onBackStarted();
+ verify(mIOnBackInvokedCallback).onBackStarted(any(BackEvent.class));
// Check that back invocation is dispatched.
mController.setTriggerBack(true); // Fake trigger back
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
new file mode 100644
index 000000000000..1e0f9bc6322f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/OWNERS
@@ -0,0 +1,5 @@
+# WM shell sub-module back navigation owners
+# Bug component: 1152663
+shanh@google.com
+arthurhung@google.com
+wilsonshih@google.com
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
new file mode 100644
index 000000000000..3aefc3f03a8a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import static org.junit.Assert.assertEquals;
+
+import android.window.BackEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class TouchTrackerTest {
+ private static final float FAKE_THRESHOLD = 400;
+ private static final float INITIAL_X_LEFT_EDGE = 5;
+ private static final float INITIAL_X_RIGHT_EDGE = FAKE_THRESHOLD - INITIAL_X_LEFT_EDGE;
+ private TouchTracker mTouchTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ mTouchTracker = new TouchTracker();
+ mTouchTracker.setProgressThreshold(FAKE_THRESHOLD);
+ }
+
+ @Test
+ public void generatesProgress_onStart() {
+ mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
+ BackEvent event = mTouchTracker.createStartEvent(null);
+ assertEquals(event.getProgress(), 0f, 0f);
+ }
+
+ @Test
+ public void generatesProgress_leftEdge() {
+ mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
+ float touchX = 10;
+
+ // Pre-commit
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
+
+ // Post-commit
+ touchX += 100;
+ mTouchTracker.setTriggerBack(true);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
+
+ // Cancel
+ touchX -= 10;
+ mTouchTracker.setTriggerBack(false);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Cancel more
+ touchX -= 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Restart
+ touchX += 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Restarted, but pre-commit
+ float restartX = touchX;
+ touchX += 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f);
+
+ // Restarted, post-commit
+ touchX += 10;
+ mTouchTracker.setTriggerBack(true);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
+ }
+
+ @Test
+ public void generatesProgress_rightEdge() {
+ mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT);
+ float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge
+
+ // Pre-commit
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
+
+ // Post-commit
+ touchX -= 100;
+ mTouchTracker.setTriggerBack(true);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
+
+ // Cancel
+ touchX += 10;
+ mTouchTracker.setTriggerBack(false);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Cancel more
+ touchX += 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Restart
+ touchX -= 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), 0, 0f);
+
+ // Restarted, but pre-commit
+ float restartX = touchX;
+ touchX -= 10;
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f);
+
+ // Restarted, post-commit
+ touchX -= 10;
+ mTouchTracker.setTriggerBack(true);
+ mTouchTracker.update(touchX, 0);
+ assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
+ }
+
+ private float getProgress() {
+ return mTouchTracker.createProgressEvent().getProgress();
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 9967e5f47752..262ee72d86fc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -22,6 +22,8 @@ import static android.view.Surface.ROTATION_0;
import static android.view.WindowInsets.Type.ime;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -131,6 +133,15 @@ public class DisplayImeControllerTest extends ShellTestCase {
verify(mT).show(any());
}
+ @Test
+ public void insetsControlChanged_updateImeSourceControl() {
+ mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
+ assertNotNull(mPerDisplay.mImeSourceControl);
+
+ mPerDisplay.insetsControlChanged(new InsetsState(), new InsetsSourceControl[]{});
+ assertNull(mPerDisplay.mImeSourceControl);
+ }
+
private InsetsSourceControl[] insetsSourceControl() {
return new InsetsSourceControl[]{
new InsetsSourceControl(
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 695550dd8fa5..3d779481d361 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.common.split;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static com.google.common.truth.Truth.assertThat;
@@ -91,12 +92,21 @@ public class SplitLayoutTests extends ShellTestCase {
// Verify updateConfiguration returns true if the root bounds changed.
config.windowConfiguration.setBounds(new Rect(0, 0, 2160, 1080));
assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the orientation changed.
+ config.orientation = ORIENTATION_LANDSCAPE;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
+
+ // Verify updateConfiguration returns true if the density changed.
+ config.densityDpi = 123;
+ assertThat(mSplitLayout.updateConfiguration(config)).isTrue();
}
@Test
public void testUpdateDivideBounds() {
mSplitLayout.updateDivideBounds(anyInt());
- verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class));
+ verify(mSplitLayoutHandler).onLayoutSizeChanging(any(SplitLayout.class), anyInt(),
+ anyInt());
}
@Test
@@ -131,7 +141,7 @@ public class SplitLayoutTests extends ShellTestCase {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt());
}
@@ -143,7 +153,7 @@ public class SplitLayoutTests extends ShellTestCase {
DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */,
DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END);
- mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget);
+ mSplitLayout.snapToTarget(mSplitLayout.getDividePosition(), snapTarget);
waitDividerFlingFinished();
verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt());
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 6292130ddec9..2fc0914acbd4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager;
@@ -93,6 +94,7 @@ public class CompatUIControllerTest extends ShellTestCase {
private @Mock Lazy<Transitions> mMockTransitionsLazy;
private @Mock CompatUIWindowManager mMockCompatLayout;
private @Mock LetterboxEduWindowManager mMockLetterboxEduLayout;
+ private @Mock DockStateReader mDockStateReader;
@Captor
ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -113,7 +115,7 @@ public class CompatUIControllerTest extends ShellTestCase {
mShellInit = spy(new ShellInit(mMockExecutor));
mController = new CompatUIController(mContext, mShellInit, mMockShellController,
mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
- mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) {
+ mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader) {
@Override
CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
ShellTaskOrganizer.TaskListener taskListener) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
index 1dee88c43806..a58620dfc6dc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayoutTest.java
@@ -68,11 +68,11 @@ public class LetterboxEduDialogLayoutTest extends ShellTestCase {
@Test
public void testOnFinishInflate() {
- assertEquals(mLayout.getDialogContainer(),
+ assertEquals(mLayout.getDialogContainerView(),
mLayout.findViewById(R.id.letterbox_education_dialog_container));
assertEquals(mLayout.getDialogTitle(),
mLayout.findViewById(R.id.letterbox_education_dialog_title));
- assertEquals(mLayout.getBackgroundDim(), mLayout.getBackground());
+ assertEquals(mLayout.getBackgroundDimDrawable(), mLayout.getBackground());
assertEquals(mLayout.getBackground().getAlpha(), 0);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
index f3a8cf45b7f8..14190f18929c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManagerTest.java
@@ -54,7 +54,9 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.compatui.DialogAnimationController;
import com.android.wm.shell.transition.Transitions;
import org.junit.After;
@@ -97,12 +99,13 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
@Captor
private ArgumentCaptor<Runnable> mRunOnIdleCaptor;
- @Mock private LetterboxEduAnimationController mAnimationController;
+ @Mock private DialogAnimationController<LetterboxEduDialogLayout> mAnimationController;
@Mock private SyncTransactionQueue mSyncTransactionQueue;
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private SurfaceControlViewHost mViewHost;
@Mock private Transitions mTransitions;
@Mock private Runnable mOnDismissCallback;
+ @Mock private DockStateReader mDockStateReader;
private SharedPreferences mSharedPreferences;
@Nullable
@@ -153,6 +156,16 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
}
@Test
+ public void testCreateLayout_eligibleAndDocked_doesNotCreateLayout() {
+ LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
+ true, /* isDocked */ true);
+
+ assertFalse(windowManager.createLayout(/* canShow= */ true));
+
+ assertNull(windowManager.mLayout);
+ }
+
+ @Test
public void testCreateLayout_taskBarEducationIsShowing_doesNotCreateLayout() {
LetterboxEduWindowManager windowManager = createWindowManager(/* eligible= */
true, USER_ID_1, /* isTaskbarEduShowing= */ true);
@@ -354,7 +367,7 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
assertThat(params.width).isEqualTo(expectedWidth);
assertThat(params.height).isEqualTo(expectedHeight);
MarginLayoutParams dialogParams =
- (MarginLayoutParams) layout.getDialogContainer().getLayoutParams();
+ (MarginLayoutParams) layout.getDialogContainerView().getLayoutParams();
int verticalMargin = (int) mContext.getResources().getDimension(
R.dimen.letterbox_education_dialog_margin);
assertThat(dialogParams.topMargin).isEqualTo(verticalMargin + expectedExtraTopMargin);
@@ -382,17 +395,27 @@ public class LetterboxEduWindowManagerTest extends ShellTestCase {
return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */ false);
}
+ private LetterboxEduWindowManager createWindowManager(boolean eligible, boolean isDocked) {
+ return createWindowManager(eligible, USER_ID_1, /* isTaskbarEduShowing= */
+ false, isDocked);
+ }
+
private LetterboxEduWindowManager createWindowManager(boolean eligible,
int userId, boolean isTaskbarEduShowing) {
+ return createWindowManager(eligible, userId, isTaskbarEduShowing, /* isDocked */false);
+ }
+
+ private LetterboxEduWindowManager createWindowManager(boolean eligible,
+ int userId, boolean isTaskbarEduShowing, boolean isDocked) {
+ doReturn(isDocked).when(mDockStateReader).isDocked();
LetterboxEduWindowManager windowManager = new LetterboxEduWindowManager(mContext,
createTaskInfo(eligible, userId), mSyncTransactionQueue, mTaskListener,
createDisplayLayout(), mTransitions, mOnDismissCallback,
- mAnimationController);
+ mAnimationController, mDockStateReader);
spyOn(windowManager);
doReturn(mViewHost).when(windowManager).createSurfaceViewHost();
doReturn(isTaskbarEduShowing).when(windowManager).isTaskbarEduShowing();
-
return windowManager;
}
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 dd23d97d9199..08af3d3eecfe 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
@@ -20,28 +20,38 @@ 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_CLOSE;
+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;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask;
+import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask;
+import static com.android.wm.shell.desktopmode.DesktopTestHelpers.createHomeTask;
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.never;
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;
import android.testing.AndroidTestingRunner;
import android.window.DisplayAreaInfo;
-import android.window.WindowContainerToken;
+import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction.Change;
+import android.window.WindowContainerTransaction.HierarchyOp;
import androidx.test.filters.SmallTest;
@@ -49,9 +59,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
@@ -63,11 +73,16 @@ 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 {
@Mock
+ private ShellController mShellController;
+ @Mock
private ShellTaskOrganizer mShellTaskOrganizer;
@Mock
private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@@ -76,9 +91,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;
@@ -87,23 +100,21 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Before
public void setUp() {
mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
+ when(DesktopModeStatus.isProto1Enabled()).thenReturn(true);
when(DesktopModeStatus.isActive(any())).thenReturn(true);
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
- mExecutor = new TestShellExecutor();
mDesktopModeTaskRepository = new DesktopModeTaskRepository();
- mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
- mRootTaskDisplayAreaOrganizer, mMockTransitions,
- mDesktopModeTaskRepository, mMockHandler, mExecutor);
+ mController = createController();
- when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn(
- new WindowContainerTransaction());
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>());
mShellInit.init();
clearInvocations(mShellTaskOrganizer);
clearInvocations(mRootTaskDisplayAreaOrganizer);
+ clearInvocations(mTransitions);
}
@After
@@ -113,149 +124,294 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Test
public void instantiate_addInitCallback() {
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ verify(mShellInit).addInitCallback(any(), any());
}
@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);
-
- // 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);
+ public void instantiate_flagOff_doNotAddInitCallback() {
+ when(DesktopModeStatus.isProto1Enabled()).thenReturn(false);
+ clearInvocations(mShellInit);
+
+ createController();
+
+ verify(mShellInit, never()).addInitCallback(any(), any());
+ }
+
+ @Test
+ public void testDesktopModeEnabled_rootTdaSetToFreeform() {
+ DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
- // The test
mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 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);
+ }
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
+ @Test
+ public void testDesktopModeDisabled_rootTdaSetToFullscreen() {
+ DisplayAreaInfo displayAreaInfo = createMockDisplayArea();
- // WCT should have 2 changes - clear task wm mode and set display wm mode
- WindowContainerTransaction wct = arg.getValue();
- assertThat(wct.getChanges()).hasSize(2);
+ mController.updateDesktopModeActive(false);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 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);
+ }
- // 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);
+ @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 display windowing mode to freeform
- Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
- assertThat(displayWmModeChange).isNotNull();
- assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
+ mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // 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);
}
@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);
-
- // 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);
+ 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)));
- // The test
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);
+ }
- ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
-
- // 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);
-
- // 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);
-
- // 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();
-
- // 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 testDesktopModeEnabled_homeTaskBehindVisibleTask() {
+ createMockDisplayArea();
+ RunningTaskInfo fullscreenTask1 = createFullscreenTask();
+ fullscreenTask1.isVisible = true;
+ RunningTaskInfo fullscreenTask2 = createFullscreenTask();
+ fullscreenTask2.isVisible = false;
+ RunningTaskInfo homeTask = createHomeTask();
+ when(mShellTaskOrganizer.getRunningTasks(anyInt())).thenReturn(new ArrayList<>(
+ Arrays.asList(fullscreenTask1, fullscreenTask2, homeTask)));
+
+ mController.updateDesktopModeActive(true);
+ WindowContainerTransaction wct = getDesktopModeSwitchTransaction();
+
+ // Check that there are hierarchy changes for home task and visible task
+ assertThat(wct.getHierarchyOps()).hasSize(2);
+ // First show home task
+ 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
+ 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() {
- // 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);
+ 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(
+ freeformTask2);
// Run show desktop apps logic
mController.showDesktopApps();
- ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
- WindowContainerTransaction.class);
- verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
- WindowContainerTransaction wct = wctCaptor.getValue();
+ final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
// Check wct has reorder calls
assertThat(wct.getHierarchyOps()).hasSize(2);
- // Task 2 has activity later, must be first
- WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
+ // Task 1 appeared first, must be first reorder to top.
+ HierarchyOp op1 = wct.getHierarchyOps().get(0);
assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op1.getContainer()).isEqualTo(token2.binder());
+ assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder());
- // Task 1 should be second
- WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0);
+ // Task 2 appeared last, must be last reorder to top.
+ HierarchyOp op2 = wct.getHierarchyOps().get(1);
assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
- assertThat(op2.getContainer()).isEqualTo(token2.binder());
+ assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder());
}
- private static class MockToken {
- private final WindowContainerToken mToken;
- private final IBinder mBinder;
+ @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);
- MockToken() {
- mToken = mock(WindowContainerToken.class);
- mBinder = mock(IBinder.class);
- when(mToken.asBinder()).thenReturn(mBinder);
- }
+ mController.showDesktopApps();
+
+ final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
+ // No reordering needed.
+ assertThat(wct.getHierarchyOps()).isEmpty();
+ }
- WindowContainerToken token() {
- return mToken;
+ @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);
+ WindowContainerTransaction wct = mController.handleRequest(
+ new Binder(),
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ assertThat(wct).isNull();
+ }
+
+ @Test
+ public void testHandleTransitionRequest_unsupportedTransit_returnsNull() {
+ WindowContainerTransaction wct = mController.handleRequest(
+ new Binder(),
+ new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */));
+ assertThat(wct).isNull();
+ }
+
+ @Test
+ public void testHandleTransitionRequest_notFreeform_returnsNull() {
+ RunningTaskInfo trigger = new RunningTaskInfo();
+ trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ WindowContainerTransaction wct = mController.handleRequest(
+ new Binder(),
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
+ assertThat(wct).isNull();
+ }
+
+ @Test
+ public void testHandleTransitionRequest_taskOpen_returnsWct() {
+ RunningTaskInfo trigger = new RunningTaskInfo();
+ trigger.token = new MockToken().token();
+ trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ WindowContainerTransaction wct = mController.handleRequest(
+ mock(IBinder.class),
+ new TransitionRequestInfo(TRANSIT_OPEN, trigger, null /* remote */));
+ assertThat(wct).isNotNull();
+ }
+
+ @Test
+ public void testHandleTransitionRequest_taskToFront_returnsWct() {
+ RunningTaskInfo trigger = new RunningTaskInfo();
+ trigger.token = new MockToken().token();
+ trigger.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ WindowContainerTransaction wct = mController.handleRequest(
+ mock(IBinder.class),
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, trigger, null /* remote */));
+ assertThat(wct).isNotNull();
+ }
+
+ private DesktopModeController createController() {
+ return new DesktopModeController(mContext, mShellInit, mShellController,
+ mShellTaskOrganizer, mRootTaskDisplayAreaOrganizer, mTransitions,
+ mDesktopModeTaskRepository, mMockHandler, new TestShellExecutor());
+ }
+
+ private DisplayAreaInfo createMockDisplayArea() {
+ DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(new MockToken().token(),
+ mContext.getDisplayId(), 0);
+ when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
+ .thenReturn(displayAreaInfo);
+ return displayAreaInfo;
+ }
+
+ 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();
+ }
- IBinder binder() {
- return mBinder;
+ private WindowContainerTransaction getBringAppsToFrontTransaction() {
+ final ArgumentCaptor<WindowContainerTransaction> 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();
}
+
}
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 9b28d11f6a9d..1e43a5983821 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
@@ -19,6 +19,7 @@ package com.android.wm.shell.desktopmode
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestShellExecutor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -38,7 +39,7 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun addActiveTask_listenerNotifiedAndTaskIsActive() {
val listener = TestListener()
- repo.addListener(listener)
+ repo.addActiveTaskListener(listener)
repo.addActiveTask(1)
assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
@@ -48,7 +49,7 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun addActiveTask_sameTaskDoesNotNotify() {
val listener = TestListener()
- repo.addListener(listener)
+ repo.addActiveTaskListener(listener)
repo.addActiveTask(1)
repo.addActiveTask(1)
@@ -58,7 +59,7 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun addActiveTask_multipleTasksAddedNotifiesForEach() {
val listener = TestListener()
- repo.addListener(listener)
+ repo.addActiveTaskListener(listener)
repo.addActiveTask(1)
repo.addActiveTask(2)
@@ -68,7 +69,7 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun removeActiveTask_listenerNotifiedAndTaskNotActive() {
val listener = TestListener()
- repo.addListener(listener)
+ repo.addActiveTaskListener(listener)
repo.addActiveTask(1)
repo.removeActiveTask(1)
@@ -80,7 +81,7 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
@Test
fun removeActiveTask_removeNotExistingTaskDoesNotNotify() {
val listener = TestListener()
- repo.addListener(listener)
+ repo.addActiveTaskListener(listener)
repo.removeActiveTask(99)
assertThat(listener.activeTaskChangedCalls).isEqualTo(0)
}
@@ -90,10 +91,95 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
assertThat(repo.isActiveTask(99)).isFalse()
}
- class TestListener : DesktopModeTaskRepository.Listener {
+ @Test
+ fun addListener_notifiesVisibleFreeformTask() {
+ repo.updateVisibleFreeformTasks(1, true)
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleFreeformTasks).isTrue()
+ assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(1)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_addVisibleTasksNotifiesListener() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ repo.updateVisibleFreeformTasks(1, true)
+ repo.updateVisibleFreeformTasks(2, true)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleFreeformTasks).isTrue()
+ // Equal to 2 because adding the listener notifies the current state
+ assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+ }
+
+ @Test
+ fun updateVisibleFreeformTasks_removeVisibleTasksNotifiesListener() {
+ val listener = TestVisibilityListener()
+ val executor = TestShellExecutor()
+ repo.addVisibleTasksListener(listener, executor)
+ repo.updateVisibleFreeformTasks(1, true)
+ repo.updateVisibleFreeformTasks(2, true)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleFreeformTasks).isTrue()
+ repo.updateVisibleFreeformTasks(1, false)
+ executor.flushAll()
+
+ // Equal to 2 because adding the listener notifies the current state
+ assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(2)
+
+ repo.updateVisibleFreeformTasks(2, false)
+ executor.flushAll()
+
+ assertThat(listener.hasVisibleFreeformTasks).isFalse()
+ 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() {
activeTaskChangedCalls++
}
}
+
+ class TestVisibilityListener : DesktopModeTaskRepository.VisibleTasksListener {
+ var hasVisibleFreeformTasks = false
+ var visibleFreeformTaskChangedCalls = 0
+
+ override fun onVisibilityChanged(hasVisibleTasks: Boolean) {
+ hasVisibleFreeformTasks = hasVisibleTasks
+ visibleFreeformTaskChangedCalls++
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
new file mode 100644
index 000000000000..9a92879bde1f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.os.Binder
+import android.testing.AndroidTestingRunner
+import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_TO_FRONT
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
+import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.dx.mockito.inline.extended.ExtendedMockito.never
+import com.android.dx.mockito.inline.extended.StaticMockitoSession
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
+import com.android.wm.shell.sysui.ShellController
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopTasksControllerTest : ShellTestCase() {
+
+ @Mock lateinit var testExecutor: ShellExecutor
+ @Mock lateinit var shellController: ShellController
+ @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
+ @Mock lateinit var transitions: Transitions
+
+ lateinit var mockitoSession: StaticMockitoSession
+ lateinit var controller: DesktopTasksController
+ lateinit var shellInit: ShellInit
+ lateinit var desktopModeTaskRepository: DesktopModeTaskRepository
+
+ // Mock running tasks are registered here so we can get the list from mock shell task organizer
+ private val runningTasks = mutableListOf<RunningTaskInfo>()
+
+ @Before
+ fun setUp() {
+ mockitoSession = mockitoSession().mockStatic(DesktopModeStatus::class.java).startMocking()
+ whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(true)
+
+ shellInit = Mockito.spy(ShellInit(testExecutor))
+ desktopModeTaskRepository = DesktopModeTaskRepository()
+
+ whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
+ whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
+
+ controller = createController()
+
+ shellInit.init()
+ }
+
+ private fun createController(): DesktopTasksController {
+ return DesktopTasksController(
+ context,
+ shellInit,
+ shellController,
+ shellTaskOrganizer,
+ transitions,
+ desktopModeTaskRepository,
+ TestShellExecutor()
+ )
+ }
+
+ @After
+ fun tearDown() {
+ mockitoSession.finishMocking()
+
+ runningTasks.clear()
+ }
+
+ @Test
+ fun instantiate_addInitCallback() {
+ verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
+ }
+
+ @Test
+ fun instantiate_flagOff_doNotAddInitCallback() {
+ whenever(DesktopModeStatus.isProto2Enabled()).thenReturn(false)
+ clearInvocations(shellInit)
+
+ createController()
+
+ verify(shellInit, never()).addInitCallback(any(), any<DesktopTasksController>())
+ }
+
+ @Test
+ fun showDesktopApps_allAppsInvisible_bringsToFront() {
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps()
+
+ val wct = getLatestWct()
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ fun showDesktopApps_appsAlreadyVisible_doesNothing() {
+ setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskVisible(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps()
+
+ verifyWCTNotExecuted()
+ }
+
+ @Test
+ fun showDesktopApps_someAppsInvisible_reordersAll() {
+ val homeTask = setUpHomeTask()
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps()
+
+ val wct = getLatestWct()
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: home, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
+ @Test
+ fun showDesktopApps_noActiveTasks_reorderHomeToTop() {
+ val homeTask = setUpHomeTask()
+
+ controller.showDesktopApps()
+
+ val wct = getLatestWct()
+ assertThat(wct.hierarchyOps).hasSize(1)
+ wct.assertReorderAt(index = 0, homeTask)
+ }
+
+ @Test
+ fun moveToDesktop() {
+ val task = setUpFullscreenTask()
+ controller.moveToDesktop(task)
+ val wct = getLatestWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun moveToDesktop_nonExistentTask_doesNothing() {
+ controller.moveToDesktop(999)
+ verifyWCTNotExecuted()
+ }
+
+ @Test
+ fun moveToFullscreen() {
+ val task = setUpFreeformTask()
+ controller.moveToFullscreen(task)
+ val wct = getLatestWct()
+ assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
+ fun moveToFullscreen_nonExistentTask_doesNothing() {
+ controller.moveToFullscreen(999)
+ verifyWCTNotExecuted()
+ }
+
+ @Test
+ fun getTaskWindowingMode() {
+ val fullscreenTask = setUpFullscreenTask()
+ val freeformTask = setUpFreeformTask()
+
+ assertThat(controller.getTaskWindowingMode(fullscreenTask.taskId))
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ assertThat(controller.getTaskWindowingMode(freeformTask.taskId))
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask = setUpFreeformTask()
+ markTaskVisible(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+
+ val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
+ assertThat(result?.changes?.get(fullscreenTask.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask = setUpFreeformTask()
+ markTaskHidden(freeformTask)
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val fullscreenTask = createFullscreenTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
+ }
+
+ @Test
+ fun handleRequest_freeformTask_freeformVisible_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask1 = setUpFreeformTask()
+ markTaskVisible(freeformTask1)
+
+ val freeformTask2 = createFreeformTask()
+ assertThat(controller.handleRequest(Binder(), createTransition(freeformTask2))).isNull()
+ }
+
+ @Test
+ fun handleRequest_freeformTask_freeformNotVisible_returnSwitchToFullscreenWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val freeformTask1 = setUpFreeformTask()
+ markTaskHidden(freeformTask1)
+
+ val freeformTask2 = createFreeformTask()
+ val result =
+ controller.handleRequest(
+ Binder(),
+ createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
+ )
+ assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
+ fun handleRequest_freeformTask_noOtherTasks_returnSwitchToFullscreenWCT() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val task = createFreeformTask()
+ val result = controller.handleRequest(Binder(), createTransition(task))
+ assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FULLSCREEN)
+ }
+
+ @Test
+ fun handleRequest_notOpenOrToFrontTransition_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val task =
+ TestRunningTaskInfoBuilder()
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build()
+ val transition = createTransition(task = task, type = WindowManager.TRANSIT_CLOSE)
+ val result = controller.handleRequest(Binder(), transition)
+ assertThat(result).isNull()
+ }
+
+ @Test
+ fun handleRequest_noTriggerTask_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
+ }
+
+ @Test
+ fun handleRequest_triggerTaskNotStandard_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+ val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
+ assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+ }
+
+ @Test
+ fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
+ assumeTrue(ENABLE_SHELL_TRANSITIONS)
+
+ val task =
+ TestRunningTaskInfoBuilder()
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
+ .build()
+ assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
+ }
+
+ private fun setUpFreeformTask(): RunningTaskInfo {
+ val task = createFreeformTask()
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ desktopModeTaskRepository.addActiveTask(task.taskId)
+ desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun setUpHomeTask(): RunningTaskInfo {
+ val task = createHomeTask()
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun setUpFullscreenTask(): RunningTaskInfo {
+ val task = createFullscreenTask()
+ whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+ runningTasks.add(task)
+ return task
+ }
+
+ private fun markTaskVisible(task: RunningTaskInfo) {
+ desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = true)
+ }
+
+ private fun markTaskHidden(task: RunningTaskInfo) {
+ desktopModeTaskRepository.updateVisibleFreeformTasks(task.taskId, visible = false)
+ }
+
+ private fun getLatestWct(): WindowContainerTransaction {
+ val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(transitions).startTransition(anyInt(), arg.capture(), isNull())
+ } else {
+ verify(shellTaskOrganizer).applyTransaction(arg.capture())
+ }
+ return arg.value
+ }
+
+ private fun verifyWCTNotExecuted() {
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(transitions, never()).startTransition(anyInt(), any(), isNull())
+ } else {
+ verify(shellTaskOrganizer, never()).applyTransaction(any())
+ }
+ }
+
+ private fun createTransition(
+ task: RunningTaskInfo?,
+ @WindowManager.TransitionType type: Int = TRANSIT_OPEN
+ ): TransitionRequestInfo {
+ return TransitionRequestInfo(type, task, null /* remoteTransition */)
+ }
+}
+
+private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {
+ assertWithMessage("WCT does not have a hierarchy operation at index $index")
+ .that(hierarchyOps.size)
+ .isGreaterThan(index)
+ val op = hierarchyOps[index]
+ assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER)
+ assertThat(op.container).isEqualTo(task.token.asBinder())
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
new file mode 100644
index 000000000000..dc91d756842e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+
+class DesktopTestHelpers {
+ companion object {
+ /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */
+ @JvmStatic
+ fun createFreeformTask(): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM)
+ .setLastActiveTime(100)
+ .build()
+ }
+
+ /** Create a task that has windowing mode set to [WINDOWING_MODE_FULLSCREEN] */
+ @JvmStatic
+ fun createFullscreenTask(): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build()
+ }
+
+ /** Create a new home task */
+ @JvmStatic
+ fun createHomeTask(): RunningTaskInfo {
+ return TestRunningTaskInfoBuilder()
+ .setToken(MockToken().token())
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setLastActiveTime(100)
+ .build()
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java
new file mode 100644
index 000000000000..09d474d1f97c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.IBinder;
+import android.window.WindowContainerToken;
+
+/**
+ * {@link WindowContainerToken} wrapper that supports a mock binder
+ */
+class MockToken {
+ private final WindowContainerToken mToken;
+
+ MockToken() {
+ mToken = mock(WindowContainerToken.class);
+ IBinder binder = mock(IBinder.class);
+ when(mToken.asBinder()).thenReturn(binder);
+ }
+
+ WindowContainerToken token() {
+ return mToken;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
deleted file mode 100644
index a88c83779f25..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.floating;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.wm.shell.floating.FloatingTasksController.SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Insets;
-import android.graphics.Rect;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TaskViewTransitions;
-import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.floating.views.FloatingTaskLayer;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * Tests for the floating tasks controller.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class FloatingTasksControllerTest extends ShellTestCase {
- // Some behavior in the controller constructor is dependent on this so we can only
- // validate if it's working for the real value for those things.
- private static final boolean FLOATING_TASKS_ACTUALLY_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
-
- @Mock private ShellInit mShellInit;
- @Mock private ShellController mShellController;
- @Mock private WindowManager mWindowManager;
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Captor private ArgumentCaptor<FloatingTaskLayer> mFloatingTaskLayerCaptor;
-
- private FloatingTasksController mController;
-
- @Before
- public void setUp() throws RemoteException {
- MockitoAnnotations.initMocks(this);
-
- WindowMetrics windowMetrics = mock(WindowMetrics.class);
- WindowInsets windowInsets = mock(WindowInsets.class);
- Insets insets = Insets.of(0, 0, 0, 0);
- when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
- when(windowMetrics.getWindowInsets()).thenReturn(windowInsets);
- when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
- when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(insets);
-
- // For the purposes of this test, just run everything synchronously
- ShellExecutor shellExecutor = new TestShellExecutor();
- when(mTaskOrganizer.getExecutor()).thenReturn(shellExecutor);
- }
-
- @After
- public void tearDown() {
- if (mController != null) {
- mController.removeTask();
- mController = null;
- }
- }
-
- private void setUpTabletConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
- mController.setConfig(config);
- }
-
- private void setUpPhoneConfig() {
- Configuration config = mock(Configuration.class);
- config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET - 1;
- mController.setConfig(config);
- }
-
- private void createController() {
- mController = new FloatingTasksController(mContext,
- mShellInit,
- mShellController,
- mock(ShellCommandHandler.class),
- Optional.empty(),
- mWindowManager,
- mTaskOrganizer,
- mock(TaskViewTransitions.class),
- mock(ShellExecutor.class),
- mock(ShellExecutor.class),
- mock(SyncTransactionQueue.class));
- spyOn(mController);
- }
-
- //
- // Shell specific
- //
- @Test
- public void instantiateController_addInitCallback() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
-
- verify(mShellInit, times(1)).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void instantiateController_doesntAddInitCallback() {
- if (!FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
-
- verify(mShellInit, never()).addInitCallback(any(), any());
- }
- }
-
- @Test
- public void onInit_registerConfigChangeListener() {
- if (FLOATING_TASKS_ACTUALLY_ENABLED) {
- createController();
- setUpTabletConfig();
- mController.onInit();
-
- verify(mShellController, times(1)).addConfigurationChangeListener(any());
- }
- }
-
- //
- // Tests for floating layer, which is only available for tablets.
- //
-
- @Test
- public void testIsFloatingLayerAvailable_true() {
- createController();
- setUpTabletConfig();
- assertThat(mController.isFloatingLayerAvailable()).isTrue();
- }
-
- @Test
- public void testIsFloatingLayerAvailable_false() {
- createController();
- setUpPhoneConfig();
- assertThat(mController.isFloatingLayerAvailable()).isFalse();
- }
-
- //
- // Tests for floating tasks being enabled, guarded by sysprop flag.
- //
-
- @Test
- public void testIsFloatingTasksEnabled_true() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isTrue();
- }
-
- @Test
- public void testIsFloatingTasksEnabled_false() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
- assertThat(mController.isFloatingTasksEnabled()).isFalse();
- }
-
- //
- // Tests for behavior depending on flags
- //
-
- @Test
- public void testShowTaskIntent_enabled() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
- }
-
- @Test
- public void testShowTaskIntent_notEnabled() {
- createController();
- mController.setFloatingTasksEnabled(false);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager, never()).addView(any(), any());
- }
-
- @Test
- public void testRemoveTask() {
- createController();
- mController.setFloatingTasksEnabled(true);
- setUpTabletConfig();
-
- mController.showTask(mock(Intent.class));
- verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
-
- mController.removeTask();
- verify(mWindowManager).removeView(mFloatingTaskLayerCaptor.capture());
- assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(0);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 0fd5cb081ea9..48415d47304c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -17,17 +17,10 @@
package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_MAXIMIZE;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE;
-
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
@@ -44,9 +37,9 @@ import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
import org.junit.Test;
@@ -65,9 +58,7 @@ public class FreeformTaskTransitionObserverTest {
@Mock
private Transitions mTransitions;
@Mock
- private FullscreenTaskListener<?> mFullscreenTaskListener;
- @Mock
- private FreeformTaskListener<?> mFreeformTaskListener;
+ private WindowDecorViewModel mWindowDecorViewModel;
private FreeformTaskTransitionObserver mTransitionObserver;
@@ -82,7 +73,7 @@ public class FreeformTaskTransitionObserverTest {
doReturn(pm).when(context).getPackageManager();
mTransitionObserver = new FreeformTaskTransitionObserver(
- context, mShellInit, mTransitions, mFullscreenTaskListener, mFreeformTaskListener);
+ context, mShellInit, mTransitions, mWindowDecorViewModel);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
Runnable.class);
@@ -112,11 +103,12 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mFreeformTaskListener).createWindowDecoration(change, startT, finishT);
+ verify(mWindowDecorViewModel).onTaskOpening(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Test
- public void testObtainsWindowDecorOnCloseTransition_freeform() {
+ public void testPreparesWindowDecorOnCloseTransition_freeform() {
final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
@@ -128,7 +120,8 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
+ verify(mWindowDecorViewModel).onTaskClosing(
+ change.getTaskInfo(), startT, finishT);
}
@Test
@@ -138,17 +131,13 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
info.addChange(change);
- final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
-
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(windowDecor, never()).close();
+ verify(mWindowDecorViewModel, never()).destroyWindowDecoration(change.getTaskInfo());
}
@Test
@@ -159,8 +148,6 @@ public class FreeformTaskTransitionObserverTest {
info.addChange(change);
final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -169,7 +156,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionStarting(transition);
mTransitionObserver.onTransitionFinished(transition, false);
- verify(windowDecor).close();
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change.getTaskInfo());
}
@Test
@@ -192,10 +179,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2);
- final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
- doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change2.getTaskInfo()), any(), any());
-
final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -204,7 +187,7 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionFinished(transition1, false);
- verify(windowDecor2).close();
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
}
@Test
@@ -215,10 +198,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0);
info1.addChange(change1);
- final AutoCloseable windowDecor1 = mock(AutoCloseable.class);
- doReturn(windowDecor1).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change1.getTaskInfo()), any(), any());
-
final IBinder transition1 = mock(IBinder.class);
final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
@@ -231,10 +210,6 @@ public class FreeformTaskTransitionObserverTest {
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2);
- final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
- doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change2.getTaskInfo()), any(), any());
-
final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -243,48 +218,8 @@ public class FreeformTaskTransitionObserverTest {
mTransitionObserver.onTransitionFinished(transition1, false);
- verify(windowDecor1).close();
- verify(windowDecor2).close();
- }
-
- @Test
- public void testTransfersWindowDecorOnMaximize() {
- final TransitionInfo.Change change =
- createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN);
- final TransitionInfo info = new TransitionInfo(TRANSIT_MAXIMIZE, 0);
- info.addChange(change);
-
- final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
-
- final IBinder transition = mock(IBinder.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
- mTransitionObserver.onTransitionStarting(transition);
-
- verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
- verify(mFullscreenTaskListener).adoptWindowDecoration(
- eq(change), same(startT), same(finishT), any());
- }
-
- @Test
- public void testTransfersWindowDecorOnRestoreFromMaximize() {
- final TransitionInfo.Change change =
- createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FREEFORM);
- final TransitionInfo info = new TransitionInfo(TRANSIT_RESTORE_FROM_MAXIMIZE, 0);
- info.addChange(change);
-
- final IBinder transition = mock(IBinder.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
- mTransitionObserver.onTransitionStarting(transition);
-
- verify(mFullscreenTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
- verify(mFreeformTaskListener).adoptWindowDecoration(
- eq(change), same(startT), same(finishT), any());
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change1.getTaskInfo());
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
}
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
index cf8297eec061..8ad3d2a72617 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java
@@ -51,6 +51,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
import org.junit.Test;
@@ -176,6 +177,12 @@ public class OneHandedControllerTest extends OneHandedTestCase {
}
@Test
+ public void testControllerRegisteresExternalInterface() {
+ verify(mMockShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_ONE_HANDED), any(), any());
+ }
+
+ @Test
public void testDefaultShouldNotInOneHanded() {
// Assert default transition state is STATE_NONE
assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 1e08f1e55797..35c09a121a1c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -18,8 +18,11 @@ package com.android.wm.shell.pip.phone;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -29,11 +32,15 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static java.lang.Integer.MAX_VALUE;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -58,9 +65,11 @@ import com.android.wm.shell.pip.PipSnapAlgorithm;
import com.android.wm.shell.pip.PipTaskOrganizer;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip.PipTransitionState;
+import com.android.wm.shell.recents.IRecentTasksListener;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
import org.junit.Test;
@@ -133,12 +142,12 @@ public class PipControllerTest extends ShellTestCase {
@Test
public void instantiatePipController_addInitCallback() {
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ verify(mShellInit, times(1)).addInitCallback(any(), eq(mPipController));
}
@Test
public void instantiateController_registerDumpCallback() {
- verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), any());
+ verify(mMockShellCommandHandler, times(1)).addDumpCallback(any(), eq(mPipController));
}
@Test
@@ -152,6 +161,12 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
+ public void instantiatePipController_registerExternalInterface() {
+ verify(mShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_PIP), any(), eq(mPipController));
+ }
+
+ @Test
public void instantiatePipController_registerUserChangeListener() {
verify(mShellController, times(1)).addUserChangeListener(any());
}
@@ -177,6 +192,24 @@ public class PipControllerTest extends ShellTestCase {
}
@Test
+ public void testInvalidateExternalInterface_unregistersListener() {
+ mPipController.setPinnedStackAnimationListener(new PipController.PipAnimationListener() {
+ @Override
+ public void onPipAnimationStarted() {}
+ @Override
+ public void onPipResourceDimensionsChanged(int cornerRadius, int shadowRadius) {}
+ @Override
+ public void onExpandPip() {}
+ });
+ assertTrue(mPipController.hasPinnedStackAnimationListener());
+ // Create initial interface
+ mShellController.createExternalInterfaces(new Bundle());
+ // Recreate the interface to trigger invalidation of the previous instance
+ mShellController.createExternalInterfaces(new Bundle());
+ assertFalse(mPipController.hasPinnedStackAnimationListener());
+ }
+
+ @Test
public void createPip_notSupported_returnsNull() {
Context spyContext = spy(mContext);
PackageManager mockPackageManager = mock(PackageManager.class);
@@ -244,6 +277,10 @@ public class PipControllerTest extends ShellTestCase {
final int displayId = 1;
final Rect bounds = new Rect(0, 0, 10, 10);
when(mMockPipBoundsAlgorithm.getDefaultBounds()).thenReturn(bounds);
+ when(mMockPipBoundsState.getBounds()).thenReturn(bounds);
+ when(mMockPipBoundsState.getMinSize()).thenReturn(new Point(1, 1));
+ when(mMockPipBoundsState.getMaxSize()).thenReturn(new Point(MAX_VALUE, MAX_VALUE));
+ when(mMockPipBoundsState.getBounds()).thenReturn(bounds);
when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
when(mMockPipBoundsState.getDisplayLayout()).thenReturn(mMockDisplayLayout1);
when(mMockDisplayController.getDisplayLayout(displayId)).thenReturn(mMockDisplayLayout2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index dba037db72eb..3bd2ae76ebfd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip.phone;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class PipResizeGestureHandlerTest extends ShellTestCase {
+ private static final float DEFAULT_SNAP_FRACTION = 2.0f;
private static final int STEP_SIZE = 40;
private final MotionEvent.PointerProperties[] mPp = new MotionEvent.PointerProperties[2];
@@ -196,6 +198,51 @@ public class PipResizeGestureHandlerTest extends ShellTestCase {
< mPipBoundsState.getBounds().width());
}
+ @Test
+ public void testUserResizeTo() {
+ // resizing the bounds to normal bounds at first
+ mPipResizeGestureHandler.userResizeTo(mPipBoundsState.getNormalBounds(),
+ DEFAULT_SNAP_FRACTION);
+
+ assertPipBoundsUserResizedTo(mPipBoundsState.getNormalBounds());
+
+ verify(mPipTaskOrganizer, times(1))
+ .scheduleUserResizePip(any(), any(), any());
+
+ verify(mPipTaskOrganizer, times(1))
+ .scheduleFinishResizePip(any(), any());
+
+ // bounds with max size
+ final Rect maxBounds = new Rect(0, 0, mPipBoundsState.getMaxSize().x,
+ mPipBoundsState.getMaxSize().y);
+
+ // resizing the bounds to maximum bounds the second time
+ mPipResizeGestureHandler.userResizeTo(maxBounds, DEFAULT_SNAP_FRACTION);
+
+ assertPipBoundsUserResizedTo(maxBounds);
+
+ // another call to scheduleUserResizePip() and scheduleFinishResizePip() makes
+ // the total number of invocations 2 for each method
+ verify(mPipTaskOrganizer, times(2))
+ .scheduleUserResizePip(any(), any(), any());
+
+ verify(mPipTaskOrganizer, times(2))
+ .scheduleFinishResizePip(any(), any());
+ }
+
+ private void assertPipBoundsUserResizedTo(Rect bounds) {
+ // check user-resized bounds
+ assertEquals(mPipResizeGestureHandler.getUserResizeBounds().width(), bounds.width());
+ assertEquals(mPipResizeGestureHandler.getUserResizeBounds().height(), bounds.height());
+
+ // check if the bounds are the same
+ assertEquals(mPipBoundsState.getBounds().width(), bounds.width());
+ assertEquals(mPipBoundsState.getBounds().height(), bounds.height());
+
+ // a flag should be set to indicate pip has been resized by the user
+ assertTrue(mPipBoundsState.hasUserResizedPip());
+ }
+
private MotionEvent obtainMotionEvent(int action, int topLeft, int bottomRight) {
final MotionEvent.PointerCoords[] pc = new MotionEvent.PointerCoords[2];
for (int i = 0; i < 2; i++) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index b8aaaa76e3c7..82392ad9a3eb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -23,11 +23,13 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -44,6 +46,7 @@ import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
+import android.os.Bundle;
import android.view.SurfaceControl;
import androidx.test.filters.SmallTest;
@@ -57,7 +60,9 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
import com.android.wm.shell.util.SplitBounds;
@@ -92,7 +97,9 @@ public class RecentTasksControllerTest extends ShellTestCase {
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
+ private RecentTasksController mRecentTasksControllerReal;
private ShellInit mShellInit;
+ private ShellController mShellController;
private TestShellExecutor mMainExecutor;
@Before
@@ -100,9 +107,12 @@ public class RecentTasksControllerTest extends ShellTestCase {
mMainExecutor = new TestShellExecutor();
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
- mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
- Optional.of(mDesktopModeTaskRepository), mMainExecutor));
+ mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mMainExecutor));
+ mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
+ mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
+ Optional.of(mDesktopModeTaskRepository), mMainExecutor);
+ mRecentTasksController = spy(mRecentTasksControllerReal);
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
@@ -121,6 +131,26 @@ public class RecentTasksControllerTest extends ShellTestCase {
}
@Test
+ public void instantiateController_addExternalInterface() {
+ verify(mShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS), any(), any());
+ }
+
+ @Test
+ public void testInvalidateExternalInterface_unregistersListener() {
+ // Note: We have to use the real instance of the controller here since that is the instance
+ // that is passed to ShellController internally, and the instance that the listener will be
+ // unregistered from
+ mRecentTasksControllerReal.registerRecentTasksListener(new IRecentTasksListener.Default());
+ assertTrue(mRecentTasksControllerReal.hasRecentTasksListener());
+ // Create initial interface
+ mShellController.createExternalInterfaces(new Bundle());
+ // Recreate the interface to trigger invalidation of the previous instance
+ mShellController.createExternalInterfaces(new Bundle());
+ assertFalse(mRecentTasksControllerReal.hasRecentTasksListener());
+ }
+
+ @Test
public void testAddRemoveSplitNotifyChange() {
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 5a68361c595c..ea3af9d96aa4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -16,18 +16,27 @@
package com.android.wm.shell.splitscreen;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -35,10 +44,14 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -58,16 +71,17 @@ import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
/**
* Tests for {@link SplitScreenController}
*/
@@ -76,7 +90,6 @@ import java.util.Optional;
public class SplitScreenControllerTests extends ShellTestCase {
@Mock ShellInit mShellInit;
- @Mock ShellController mShellController;
@Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellTaskOrganizer mTaskOrganizer;
@Mock SyncTransactionQueue mSyncQueue;
@@ -89,26 +102,33 @@ public class SplitScreenControllerTests extends ShellTestCase {
@Mock Transitions mTransitions;
@Mock TransactionPool mTransactionPool;
@Mock IconProvider mIconProvider;
- @Mock Optional<RecentTasksController> mRecentTasks;
+ @Mock StageCoordinator mStageCoordinator;
+ @Mock RecentTasksController mRecentTasks;
+ @Captor ArgumentCaptor<Intent> mIntentCaptor;
+ private ShellController mShellController;
private SplitScreenController mSplitScreenController;
@Before
public void setup() {
+ assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
MockitoAnnotations.initMocks(this);
+ mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mMainExecutor));
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mMainExecutor));
+ mIconProvider, mRecentTasks, mMainExecutor, mStageCoordinator));
}
@Test
public void instantiateController_addInitCallback() {
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ verify(mShellInit, times(1)).addInitCallback(any(), isA(SplitScreenController.class));
}
@Test
+ @UiThreadTest
public void instantiateController_registerDumpCallback() {
doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -117,6 +137,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
+ @UiThreadTest
public void instantiateController_registerCommandCallback() {
doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -125,6 +146,7 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
+ @UiThreadTest
public void testControllerRegistersKeyguardChangeListener() {
doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
@@ -133,58 +155,123 @@ public class SplitScreenControllerTests extends ShellTestCase {
}
@Test
- public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
- doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
- doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
+ @UiThreadTest
+ public void instantiateController_addExternalInterface() {
+ doReturn(mMainExecutor).when(mTaskOrganizer).getExecutor();
+ when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout());
+ mSplitScreenController.onInit();
+ verify(mShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN), any(), any());
+ }
+
+ @Test
+ public void testInvalidateExternalInterface_unregistersListener() {
+ mSplitScreenController.onInit();
+ mSplitScreenController.registerSplitScreenListener(
+ new SplitScreen.SplitScreenListener() {});
+ verify(mStageCoordinator).registerSplitScreenListener(any());
+ // Create initial interface
+ mShellController.createExternalInterfaces(new Bundle());
+ // Recreate the interface to trigger invalidation of the previous instance
+ mShellController.createExternalInterfaces(new Bundle());
+ verify(mStageCoordinator).unregisterSplitScreenListener(any());
+ }
+
+ @Test
+ public void testStartIntent_appendsNoUserActionFlag() {
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ assertEquals(FLAG_ACTIVITY_NO_USER_ACTION,
+ mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION);
+ }
+
+ @Test
+ public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component to the top running task
+ ActivityManager.RunningTaskInfo topRunningTask =
+ createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
+ doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
- // Verify launching the same activity returns true.
+ verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
+ eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
+ assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK,
+ mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
+
+ @Test
+ public void startIntent_multiInstancesSupported_startTaskInBackgroundBeforeSplitActivated() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
Intent startIntent = createStartIntent("startActivity");
- ActivityManager.RunningTaskInfo focusTaskInfo =
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component to the top running task
+ ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching different activity returns false.
- Intent diffIntent = createStartIntent("diffActivity");
- focusTaskInfo =
- createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
- doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
+ doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
+ // Put the same component into a task in the background
+ ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
+ doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
+ isNull());
}
@Test
- public void testShouldAddMultipleTaskFlag_inSplitScreen() {
+ public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
+ doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
+ doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
+ Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into another side of the split
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
+ ActivityManager.RunningTaskInfo sameTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
+ doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
+ SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ // Put the same component into a task in the background
+ doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
+ .findTaskInBackground(any());
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
+ isNull());
+ }
+
+ @Test
+ public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
+ doReturn(false).when(mSplitScreenController).supportMultiInstancesSplit(any());
Intent startIntent = createStartIntent("startActivity");
+ PendingIntent pendingIntent =
+ PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
+ // Put the same component into another side of the split
+ doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
ActivityManager.RunningTaskInfo sameTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
- Intent diffIntent = createStartIntent("diffActivity");
- ActivityManager.RunningTaskInfo differentTaskInfo =
- createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
-
- // Verify launching the same activity return false.
- doReturn(sameTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching the same activity as adjacent returns true.
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- doReturn(sameTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
- assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
-
- // Verify launching different activity from adjacent returns false.
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
- doReturn(differentTaskInfo).when(mSplitScreenController)
- .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
- assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
- startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+ doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
+ SPLIT_POSITION_BOTTOM_OR_RIGHT);
+
+ mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
+
+ verify(mStageCoordinator).switchSplitPosition(anyString());
}
private Intent createStartIntent(String activityName) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index ea0033ba4bbb..652f9b38c88f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -181,7 +181,7 @@ public class SplitTransitionTests extends ShellTestCase {
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(testRemote), mStageCoordinator, null);
+ new RemoteTransition(testRemote), mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -421,7 +421,7 @@ public class SplitTransitionTests extends ShellTestCase {
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null);
+ new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 835087007b30..65e1ea881b26 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -50,6 +50,7 @@ import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -113,6 +114,7 @@ public class StageCoordinatorTests extends ShellTestCase {
private StageCoordinator mStageCoordinator;
@Before
+ @UiThreadTest
public void setup() {
MockitoAnnotations.initMocks(this);
mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
@@ -153,7 +155,7 @@ public class StageCoordinatorTests extends ShellTestCase {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
// Verify move to undefined stage while split screen not activated moves task to side stage.
- when(mMainStage.isActive()).thenReturn(false);
+ when(mStageCoordinator.isSplitScreenVisible()).thenReturn(false);
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
new WindowContainerTransaction());
@@ -161,7 +163,7 @@ public class StageCoordinatorTests extends ShellTestCase {
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
// Verify move to undefined stage after split screen activated moves task based on position.
- when(mMainStage.isActive()).thenReturn(true);
+ when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
new WindowContainerTransaction());
@@ -260,7 +262,7 @@ public class StageCoordinatorTests extends ShellTestCase {
@Test
public void testResolveStartStage_afterSplitActivated_retrievesStagePosition() {
- when(mMainStage.isActive()).thenReturn(true);
+ when(mStageCoordinator.isSplitScreenVisible()).thenReturn(true);
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
@@ -318,4 +320,16 @@ public class StageCoordinatorTests extends ShellTestCase {
assertTrue(options.getBoolean(
KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION));
}
+
+ @Test
+ public void testExitSplitScreenAfterFolded() {
+ when(mMainStage.isActive()).thenReturn(true);
+ when(mMainStage.isFocused()).thenReturn(true);
+ when(mMainStage.getTopVisibleChildTaskId()).thenReturn(INVALID_TASK_ID);
+
+ mStageCoordinator.onFoldedStateChanged(true);
+
+ verify(mStageCoordinator).onSplitScreenExit();
+ verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false));
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
index 35515e3bb6e8..10dec9ef12f9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
@@ -16,27 +16,36 @@
package com.android.wm.shell.startingsurface;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.display.DisplayManager;
+import android.os.Bundle;
import android.view.Display;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.util.function.TriConsumer;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
import org.junit.Test;
@@ -56,25 +65,51 @@ public class StartingWindowControllerTests extends ShellTestCase {
private @Mock Context mContext;
private @Mock DisplayManager mDisplayManager;
- private @Mock ShellInit mShellInit;
+ private @Mock ShellCommandHandler mShellCommandHandler;
private @Mock ShellTaskOrganizer mTaskOrganizer;
private @Mock ShellExecutor mMainExecutor;
private @Mock StartingWindowTypeAlgorithm mTypeAlgorithm;
private @Mock IconProvider mIconProvider;
private @Mock TransactionPool mTransactionPool;
private StartingWindowController mController;
+ private ShellInit mShellInit;
+ private ShellController mShellController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt());
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
- mController = new StartingWindowController(mContext, mShellInit, mTaskOrganizer,
- mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool);
+ mShellInit = spy(new ShellInit(mMainExecutor));
+ mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+ mMainExecutor));
+ mController = new StartingWindowController(mContext, mShellInit, mShellController,
+ mTaskOrganizer, mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool);
+ mShellInit.init();
}
@Test
- public void instantiate_addInitCallback() {
- verify(mShellInit, times(1)).addInitCallback(any(), any());
+ public void instantiateController_addInitCallback() {
+ verify(mShellInit, times(1)).addInitCallback(any(), isA(StartingWindowController.class));
+ }
+
+ @Test
+ public void instantiateController_addExternalInterface() {
+ verify(mShellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW), any(), any());
+ }
+
+ @Test
+ public void testInvalidateExternalInterface_unregistersListener() {
+ mController.setStartingWindowListener(new TriConsumer<Integer, Integer, Integer>() {
+ @Override
+ public void accept(Integer integer, Integer integer2, Integer integer3) {}
+ });
+ assertTrue(mController.hasStartingWindowListener());
+ // Create initial interface
+ mShellController.createExternalInterfaces(new Bundle());
+ // Recreate the interface to trigger invalidation of the previous instance
+ mShellController.createExternalInterfaces(new Bundle());
+ assertFalse(mController.hasStartingWindowListener());
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index d6ddba9e927d..8d92d0864338 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -16,12 +16,16 @@
package com.android.wm.shell.sysui;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,6 +34,8 @@ import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.After;
@@ -49,16 +55,16 @@ import java.util.Locale;
public class ShellControllerTest extends ShellTestCase {
private static final int TEST_USER_ID = 100;
+ private static final String EXTRA_TEST_BINDER = "test_binder";
@Mock
private ShellInit mShellInit;
@Mock
private ShellCommandHandler mShellCommandHandler;
@Mock
- private ShellExecutor mExecutor;
- @Mock
private Context mTestUserContext;
+ private TestShellExecutor mExecutor;
private ShellController mController;
private TestConfigurationChangeListener mConfigChangeListener;
private TestKeyguardChangeListener mKeyguardChangeListener;
@@ -71,6 +77,7 @@ public class ShellControllerTest extends ShellTestCase {
mKeyguardChangeListener = new TestKeyguardChangeListener();
mConfigChangeListener = new TestConfigurationChangeListener();
mUserChangeListener = new TestUserChangeListener();
+ mExecutor = new TestShellExecutor();
mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
mController.onConfigurationChanged(getConfigurationCopy());
}
@@ -81,6 +88,48 @@ public class ShellControllerTest extends ShellTestCase {
}
@Test
+ public void testAddExternalInterface_ensureCallback() {
+ Binder callback = new Binder();
+ ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() {
+ @Override
+ public void invalidate() {
+ // Do nothing
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return callback;
+ }
+ };
+ mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this);
+
+ Bundle b = new Bundle();
+ mController.asShell().createExternalInterfaces(b);
+ mExecutor.flushAll();
+ assertTrue(b.getIBinder(EXTRA_TEST_BINDER) == callback);
+ }
+
+ @Test
+ public void testAddExternalInterface_disallowDuplicateKeys() {
+ Binder callback = new Binder();
+ ExternalInterfaceBinder wrapper = new ExternalInterfaceBinder() {
+ @Override
+ public void invalidate() {
+ // Do nothing
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return callback;
+ }
+ };
+ mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this);
+ assertThrows(IllegalArgumentException.class, () -> {
+ mController.addExternalInterface(EXTRA_TEST_BINDER, () -> wrapper, this);
+ });
+ }
+
+ @Test
public void testAddUserChangeListener_ensureCallback() {
mController.addUserChangeListener(mUserChangeListener);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index c6492bee040e..595c3b4880df 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -45,7 +45,6 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.clearInvocations;
@@ -67,10 +66,12 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
+import android.window.IWindowContainerToken;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowOrganizer;
@@ -86,7 +87,9 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.sysui.ShellSharedConstants;
import org.junit.Before;
import org.junit.Test;
@@ -117,18 +120,31 @@ public class ShellTransitionTests extends ShellTestCase {
@Before
public void setUp() {
doAnswer(invocation -> invocation.getArguments()[1])
- .when(mOrganizer).startTransition(anyInt(), any(), any());
+ .when(mOrganizer).startTransition(any(), any());
}
@Test
public void instantiate_addInitCallback() {
ShellInit shellInit = mock(ShellInit.class);
- final Transitions t = new Transitions(mContext, shellInit, mOrganizer, mTransactionPool,
- createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor);
+ final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
+ mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
+ mMainHandler, mAnimExecutor);
verify(shellInit, times(1)).addInitCallback(any(), eq(t));
}
@Test
+ public void instantiateController_addExternalInterface() {
+ ShellInit shellInit = new ShellInit(mMainExecutor);
+ ShellController shellController = mock(ShellController.class);
+ final Transitions t = new Transitions(mContext, shellInit, shellController,
+ mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
+ mMainHandler, mAnimExecutor);
+ shellInit.init();
+ verify(shellController, times(1)).addExternalInterface(
+ eq(ShellSharedConstants.KEY_EXTRA_SHELL_SHELL_TRANSITIONS), any(), any());
+ }
+
+ @Test
public void testBasicTransitionFlow() {
Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
@@ -136,7 +152,7 @@ public class ShellTransitionTests extends ShellTestCase {
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
+ verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
@@ -188,7 +204,7 @@ public class ShellTransitionTests extends ShellTestCase {
// Make a request that will be rejected by the testhandler.
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), isNull());
+ verify(mOrganizer, times(1)).startTransition(eq(transitToken), isNull());
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
@@ -199,10 +215,12 @@ public class ShellTransitionTests extends ShellTestCase {
// Make a request that will be handled by testhandler but not animated by it.
RunningTaskInfo mwTaskInfo =
createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ // Make the wct non-empty.
+ handlerWCT.setFocusable(new WindowContainerToken(mock(IWindowContainerToken.class)), true);
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */));
verify(mOrganizer, times(1)).startTransition(
- eq(TRANSIT_OPEN), eq(transitToken), eq(handlerWCT));
+ eq(transitToken), eq(handlerWCT));
transitions.onTransitionReady(transitToken, open, mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class));
assertEquals(1, mDefaultHandler.activeCount());
@@ -217,8 +235,8 @@ public class ShellTransitionTests extends ShellTestCase {
transitions.addHandler(topHandler);
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */));
- verify(mOrganizer, times(1)).startTransition(
- eq(TRANSIT_CHANGE), eq(transitToken), eq(handlerWCT));
+ verify(mOrganizer, times(2)).startTransition(
+ eq(transitToken), eq(handlerWCT));
TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE)
.addChange(TRANSIT_CHANGE).build();
transitions.onTransitionReady(transitToken, change, mock(SurfaceControl.Transaction.class),
@@ -256,7 +274,7 @@ public class ShellTransitionTests extends ShellTestCase {
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */,
new RemoteTransition(testRemote)));
- verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
+ verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
@@ -406,7 +424,7 @@ public class ShellTransitionTests extends ShellTestCase {
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- verify(mOrganizer, times(1)).startTransition(eq(TRANSIT_OPEN), eq(transitToken), any());
+ verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
transitions.onTransitionReady(transitToken, info, mock(SurfaceControl.Transaction.class),
@@ -918,7 +936,7 @@ public class ShellTransitionTests extends ShellTestCase {
TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
RunningTaskInfo taskInfo) {
final TransitionInfo.Change change =
- new TransitionInfo.Change(null /* token */, null /* leash */);
+ new TransitionInfo.Change(null /* token */, createMockSurface(true));
change.setMode(mode);
change.setTaskInfo(taskInfo);
mInfo.addChange(change);
@@ -943,7 +961,7 @@ public class ShellTransitionTests extends ShellTestCase {
final TransitionInfo.Change mChange;
ChangeBuilder(@WindowManager.TransitionType int mode) {
- mChange = new TransitionInfo.Change(null /* token */, null /* leash */);
+ mChange = new TransitionInfo.Change(null /* token */, createMockSurface(true));
mChange.setMode(mode);
}
@@ -1060,8 +1078,9 @@ public class ShellTransitionTests extends ShellTestCase {
private Transitions createTestTransitions() {
ShellInit shellInit = new ShellInit(mMainExecutor);
- final Transitions t = new Transitions(mContext, shellInit, mOrganizer, mTransactionPool,
- createTestDisplayController(), mMainExecutor, mMainHandler, mAnimExecutor);
+ final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
+ mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
+ mMainHandler, mAnimExecutor);
shellInit.init();
return t;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
index 81eefe25704e..8196c5ab08e4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldAnimationControllerTest.java
@@ -224,16 +224,18 @@ public class UnfoldAnimationControllerTest extends ShellTestCase {
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
+ mUnfoldAnimationController.onStateChangeStarted();
mUnfoldAnimationController.onTaskVanished(taskInfo);
+ mUnfoldAnimationController.onStateChangeFinished();
assertThat(mTaskAnimator1.mResetTasks).contains(taskInfo.taskId);
}
@Test
- public void testApplicablePinnedTaskDisappeared_doesNotResetSurface() {
- mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 2);
+ public void testApplicableTaskDisappeared_noStateChange_doesNotResetSurface() {
+ mTaskAnimator1.setTaskMatcher((info) -> info.getWindowingMode() == 0);
RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
- .setWindowingMode(2).build();
+ .setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
@@ -249,7 +251,9 @@ public class UnfoldAnimationControllerTest extends ShellTestCase {
.setWindowingMode(0).build();
mUnfoldAnimationController.onTaskAppeared(taskInfo, mLeash);
+ mUnfoldAnimationController.onStateChangeStarted();
mUnfoldAnimationController.onTaskVanished(taskInfo);
+ mUnfoldAnimationController.onStateChangeFinished();
assertThat(mTaskAnimator1.mResetTasks).doesNotContain(taskInfo.taskId);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
new file mode 100644
index 000000000000..355072116cb1
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+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.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.hardware.input.InputManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+import android.view.Display;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.SurfaceControl;
+import android.view.SurfaceView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopTasksController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/** Tests of {@link DesktopModeWindowDecorViewModel} */
+@SmallTest
+public class DesktopModeWindowDecorViewModelTests extends ShellTestCase {
+
+ private static final String TAG = "DesktopModeWindowDecorViewModelTests";
+
+ @Mock private DesktopModeWindowDecoration mDesktopModeWindowDecoration;
+ @Mock private DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
+
+ @Mock private Handler mMainHandler;
+ @Mock private Choreographer mMainChoreographer;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Mock private DisplayController mDisplayController;
+ @Mock private SyncTransactionQueue mSyncQueue;
+ @Mock private DesktopModeController mDesktopModeController;
+ @Mock private DesktopTasksController mDesktopTasksController;
+ @Mock private InputMonitor mInputMonitor;
+ @Mock private InputManager mInputManager;
+
+ @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory;
+ private final List<InputManager> mMockInputManagers = new ArrayList<>();
+
+ private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
+
+ @Before
+ public void setUp() {
+ mMockInputManagers.add(mInputManager);
+
+ mDesktopModeWindowDecorViewModel =
+ new DesktopModeWindowDecorViewModel(
+ mContext,
+ mMainHandler,
+ mMainChoreographer,
+ mTaskOrganizer,
+ mDisplayController,
+ mSyncQueue,
+ Optional.of(mDesktopModeController),
+ Optional.of(mDesktopTasksController),
+ mDesktopModeWindowDecorFactory,
+ mMockInputMonitorFactory
+ );
+
+ doReturn(mDesktopModeWindowDecoration)
+ .when(mDesktopModeWindowDecorFactory)
+ .create(any(), any(), any(), any(), any(), any(), any(), any());
+
+ when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
+ // InputChannel cannot be mocked because it passes to InputEventReceiver.
+ final InputChannel[] inputChannels = InputChannel.openInputChannelPair(TAG);
+ inputChannels[0].dispose();
+ when(mInputMonitor.getInputChannel()).thenReturn(inputChannels[1]);
+ }
+
+ @Test
+ public void testDeleteCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ runOnMainThread(() -> {
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+
+ mDesktopModeWindowDecorViewModel.onTaskOpening(
+ taskInfo, surfaceControl, startT, finishT);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+ mDesktopModeWindowDecorViewModel.onTaskChanging(
+ taskInfo, surfaceControl, startT, finishT);
+ });
+ verify(mDesktopModeWindowDecorFactory)
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ verify(mDesktopModeWindowDecoration).close();
+ }
+
+ @Test
+ public void testCreateCaptionOnChangeTransitionWhenNecessary() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_UNDEFINED);
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ runOnMainThread(() -> {
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+
+ mDesktopModeWindowDecorViewModel.onTaskChanging(
+ taskInfo, surfaceControl, startT, finishT);
+
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+
+ mDesktopModeWindowDecorViewModel.onTaskChanging(
+ taskInfo, surfaceControl, startT, finishT);
+ });
+ verify(mDesktopModeWindowDecorFactory, times(1))
+ .create(
+ mContext,
+ mDisplayController,
+ mTaskOrganizer,
+ taskInfo,
+ surfaceControl,
+ mMainHandler,
+ mMainChoreographer,
+ mSyncQueue);
+ }
+
+ @Test
+ public void testCreateAndDisposeEventReceiver() throws Exception {
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
+ taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_STANDARD);
+ runOnMainThread(() -> {
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+
+ mDesktopModeWindowDecorViewModel.onTaskOpening(
+ taskInfo, surfaceControl, startT, finishT);
+
+ mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
+ });
+ verify(mMockInputMonitorFactory).create(any(), any());
+ verify(mInputMonitor).dispose();
+ }
+
+ @Test
+ public void testEventReceiversOnMultipleDisplays() throws Exception {
+ runOnMainThread(() -> {
+ SurfaceView surfaceView = new SurfaceView(mContext);
+ final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
+ final VirtualDisplay secondaryDisplay = mDm.createVirtualDisplay(
+ "testEventReceiversOnMultipleDisplays", /*width=*/ 400, /*height=*/ 400,
+ /*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
+ DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
+ try {
+ int secondaryDisplayId = secondaryDisplay.getDisplay().getDisplayId();
+
+ final int taskId = 1;
+ final ActivityManager.RunningTaskInfo taskInfo =
+ createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM);
+ final ActivityManager.RunningTaskInfo secondTaskInfo =
+ createTaskInfo(taskId + 1, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
+ final ActivityManager.RunningTaskInfo thirdTaskInfo =
+ createTaskInfo(taskId + 2, secondaryDisplayId, WINDOWING_MODE_FREEFORM);
+
+ SurfaceControl surfaceControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+ final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+
+ mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, surfaceControl, startT,
+ finishT);
+ mDesktopModeWindowDecorViewModel.onTaskOpening(secondTaskInfo, surfaceControl,
+ startT, finishT);
+ mDesktopModeWindowDecorViewModel.onTaskOpening(thirdTaskInfo, surfaceControl,
+ startT, finishT);
+ mDesktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTaskInfo);
+ mDesktopModeWindowDecorViewModel.destroyWindowDecoration(taskInfo);
+ } finally {
+ secondaryDisplay.release();
+ }
+ });
+ verify(mMockInputMonitorFactory, times(2)).create(any(), any());
+ verify(mInputMonitor, times(1)).dispose();
+ }
+
+ private void runOnMainThread(Runnable r) throws Exception {
+ final Handler mainHandler = new Handler(Looper.getMainLooper());
+ final CountDownLatch latch = new CountDownLatch(1);
+ mainHandler.post(() -> {
+ r.run();
+ latch.countDown();
+ });
+ latch.await(1, TimeUnit.SECONDS);
+ }
+
+ private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId,
+ int displayId, int windowingMode) {
+ ActivityManager.RunningTaskInfo taskInfo =
+ new TestRunningTaskInfoBuilder()
+ .setDisplayId(displayId)
+ .setVisible(true)
+ .build();
+ taskInfo.taskId = taskId;
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ return taskInfo;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
new file mode 100644
index 000000000000..ac10ddb0116a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/TaskPositionerTest.kt
@@ -0,0 +1,130 @@
+package com.android.wm.shell.windowdecor
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.window.WindowContainerToken
+import android.window.WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_RIGHT
+import com.android.wm.shell.windowdecor.TaskPositioner.CTRL_TYPE_UNDEFINED
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.argThat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+/**
+ * Tests for [TaskPositioner].
+ *
+ * Build/Install/Run:
+ * atest WMShellUnitTests:TaskPositionerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class TaskPositionerTest : ShellTestCase() {
+
+ @Mock
+ private lateinit var mockShellTaskOrganizer: ShellTaskOrganizer
+ @Mock
+ private lateinit var mockWindowDecoration: WindowDecoration<*>
+ @Mock
+ private lateinit var mockDragStartListener: TaskPositioner.DragStartListener
+
+ @Mock
+ private lateinit var taskToken: WindowContainerToken
+ @Mock
+ private lateinit var taskBinder: IBinder
+
+ private lateinit var taskPositioner: TaskPositioner
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ taskPositioner = TaskPositioner(
+ mockShellTaskOrganizer,
+ mockWindowDecoration,
+ mockDragStartListener
+ )
+ `when`(taskToken.asBinder()).thenReturn(taskBinder)
+ mockWindowDecoration.mTaskInfo = ActivityManager.RunningTaskInfo().apply {
+ taskId = TASK_ID
+ token = taskToken
+ configuration.windowConfiguration.bounds = STARTING_BOUNDS
+ }
+ }
+
+ @Test
+ fun testDragResize_move_skipsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_UNDEFINED, // Move
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Move the task 10px to the right.
+ val newX = STARTING_BOUNDS.left.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer, never()).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ }
+
+ @Test
+ fun testDragResize_resize_setsDragResizingFlag() {
+ taskPositioner.onDragResizeStart(
+ CTRL_TYPE_RIGHT, // Resize right
+ STARTING_BOUNDS.left.toFloat(),
+ STARTING_BOUNDS.top.toFloat()
+ )
+
+ // Resize the task by 10px to the right.
+ val newX = STARTING_BOUNDS.right.toFloat() + 10
+ val newY = STARTING_BOUNDS.top.toFloat()
+ taskPositioner.onDragResizeMove(
+ newX,
+ newY
+ )
+
+ taskPositioner.onDragResizeEnd(newX, newY)
+
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ change.dragResizing
+ }
+ })
+ verify(mockShellTaskOrganizer).applyTransaction(argThat { wct ->
+ return@argThat wct.changes.any { (token, change) ->
+ token == taskBinder &&
+ ((change.changeMask and CHANGE_DRAG_RESIZING) != 0) &&
+ !change.dragResizing
+ }
+ })
+ }
+
+ companion object {
+ private const val TASK_ID = 5
+ private val STARTING_BOUNDS = Rect(0, 0, 100, 100)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index ab6ac949d4a3..ec4f17fd072b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify;
import android.app.ActivityManager;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
@@ -47,14 +48,17 @@ import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.WindowManager.LayoutParams;
+import android.window.TaskConstants;
import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.tests.R;
import org.junit.Before;
import org.junit.Test;
@@ -62,6 +66,7 @@ import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
@@ -76,12 +81,9 @@ import java.util.function.Supplier;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class WindowDecorationTests extends ShellTestCase {
- private static final int CAPTION_HEIGHT_DP = 32;
- private static final int SHADOW_RADIUS_DP = 5;
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
- private final Rect mOutsetsDp = new Rect();
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
@@ -103,11 +105,20 @@ public class WindowDecorationTests extends ShellTestCase {
private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>();
private SurfaceControl.Transaction mMockSurfaceControlStartT;
private SurfaceControl.Transaction mMockSurfaceControlFinishT;
+ private SurfaceControl.Transaction mMockSurfaceControlAddWindowT;
+ private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
@Before
public void setUp() {
mMockSurfaceControlStartT = createMockSurfaceControlTransaction();
mMockSurfaceControlFinishT = createMockSurfaceControlTransaction();
+ mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction();
+
+ mRelayoutParams.mLayoutResId = 0;
+ mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
+ // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
+ mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
.create(any(), any(), any());
@@ -146,7 +157,11 @@ public class WindowDecorationTests extends ShellTestCase {
// Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
// 64px.
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
- mOutsetsDp.set(10, 20, 30, 40);
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
final SurfaceControl taskSurface = mock(SurfaceControl.class);
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
@@ -196,8 +211,11 @@ public class WindowDecorationTests extends ShellTestCase {
// Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
// 64px.
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
- mOutsetsDp.set(10, 20, 30, 40);
-
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
final SurfaceControl taskSurface = mock(SurfaceControl.class);
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
@@ -215,20 +233,22 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockSurfaceControlStartT)
.setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
- verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+ verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface,
+ TaskConstants.TASK_CHILD_LAYER_TASK_BACKGROUND);
verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
verify(captionContainerSurfaceBuilder).setContainerLayer();
verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
- verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 432, 64);
verify(mMockSurfaceControlStartT).show(captionContainerSurface);
verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any());
+
verify(mMockSurfaceControlViewHost)
.setView(same(mMockView),
argThat(lp -> lp.height == 64
- && lp.width == 300
+ && lp.width == 432
&& (lp.flags & LayoutParams.FLAG_NOT_FOCUSABLE) != 0));
if (ViewRootImpl.CAPTION_ON_SHELL) {
verify(mMockView).setTaskFocusState(true);
@@ -247,7 +267,6 @@ public class WindowDecorationTests extends ShellTestCase {
assertEquals(380, mRelayoutResult.mWidth);
assertEquals(220, mRelayoutResult.mHeight);
- assertEquals(2, mRelayoutResult.mDensity, 0.f);
}
@Test
@@ -286,7 +305,11 @@ public class WindowDecorationTests extends ShellTestCase {
// Density is 2. Outsets are (20, 40, 60, 80) px. Shadow radius is 10px. Caption height is
// 64px.
taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
- mOutsetsDp.set(10, 20, 30, 40);
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
final SurfaceControl taskSurface = mock(SurfaceControl.class);
final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
@@ -355,9 +378,127 @@ public class WindowDecorationTests extends ShellTestCase {
verify(mMockSurfaceControlViewHost).setView(same(mMockView), any());
}
+ @Test
+ public void testAddWindow() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+ windowDecor.relayout(taskInfo);
+
+ final SurfaceControl additionalWindowSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder additionalWindowSurfaceBuilder =
+ createMockSurfaceControlBuilder(additionalWindowSurface);
+ mMockSurfaceControlBuilders.add(additionalWindowSurfaceBuilder);
+
+ WindowDecoration.AdditionalWindow additionalWindow = windowDecor.addTestWindow();
+
+ verify(additionalWindowSurfaceBuilder).setContainerLayer();
+ verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface);
+ verify(additionalWindowSurfaceBuilder).build();
+ verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 20, 40);
+ verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 432, 64);
+ verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
+ verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
+ .create(any(), eq(defaultDisplay), any());
+ assertThat(additionalWindow.mWindowViewHost).isNotNull();
+
+ additionalWindow.releaseView();
+
+ assertThat(additionalWindow.mWindowViewHost).isNull();
+ assertThat(additionalWindow.mWindowSurface).isNull();
+ }
+
+ @Test
+ public void testLayoutResultCalculation_fullWidthCaption() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
+ windowDecor.relayout(taskInfo);
+
+ verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+ verify(captionContainerSurfaceBuilder).setContainerLayer();
+ verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+ // Width of the captionContainerSurface should match the width of TASK_BOUNDS
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+ verify(mMockSurfaceControlStartT).show(captionContainerSurface);
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
- return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer,
+ return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
+ mMockDisplayController, mMockShellTaskOrganizer,
taskInfo, testSurface,
new MockObjectSupplier<>(mMockSurfaceControlBuilders,
() -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))),
@@ -409,9 +550,24 @@ public class WindowDecorationTests extends ShellTestCase {
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
- relayout(null /* taskInfo */, 0 /* layoutResId */, mMockView, CAPTION_HEIGHT_DP,
- mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlStartT,
- mMockSurfaceControlFinishT, mMockWindowContainerTransaction, mRelayoutResult);
+ relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
+ mMockWindowContainerTransaction, mMockView, mRelayoutResult);
+ }
+
+ private WindowDecoration.AdditionalWindow addTestWindow() {
+ final Resources resources = mDecorWindowContext.getResources();
+ int x = mRelayoutParams.mCaptionX;
+ int y = mRelayoutParams.mCaptionY;
+ int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
+ int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
+ String name = "Test Window";
+ WindowDecoration.AdditionalWindow additionalWindow =
+ addWindow(R.layout.desktop_mode_decor_handle_menu, name,
+ mMockSurfaceControlAddWindowT,
+ x - mRelayoutResult.mDecorContainerOffsetX,
+ y - mRelayoutResult.mDecorContainerOffsetY,
+ width, height);
+ return additionalWindow;
}
}
}