diff options
3 files changed, 346 insertions, 349 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 949ee810f8d6..afa2754803f1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -125,7 +125,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private MoveToDesktopAnimator mMoveToDesktopAnimator; private final Rect mDragToDesktopAnimationStartBounds = new Rect(); - private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener; + private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener = + new DesktopModeKeyguardChangeListener(); private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; public DesktopModeWindowDecorViewModel( @@ -157,7 +158,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new, - new DesktopModeKeyguardChangeListener(), rootTaskDisplayAreaOrganizer); } @@ -177,7 +177,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory, - DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { mContext = context; mMainHandler = mainHandler; @@ -194,7 +193,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory; mInputMonitorFactory = inputMonitorFactory; mTransactionFactory = transactionFactory; - mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener; mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; shellInit.addInitCallback(this::onInit, this); 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 deleted file mode 100644 index 3b3b924da4be..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ /dev/null @@ -1,345 +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.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_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -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.app.ActivityManager; -import android.app.WindowConfiguration; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.hardware.display.VirtualDisplay; -import android.hardware.input.InputManager; -import android.os.Handler; -import android.os.IBinder; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -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.RootTaskDisplayAreaOrganizer; -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.DisplayLayout; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.desktopmode.DesktopTasksController; -import com.android.wm.shell.recents.RecentsTransitionHandler; -import com.android.wm.shell.recents.RecentsTransitionStateListener; -import com.android.wm.shell.sysui.ShellController; -import com.android.wm.shell.sysui.ShellInit; -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.Mock; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Supplier; - -/** Tests of {@link DesktopModeWindowDecorViewModel} */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { - - private static final String TAG = "DesktopModeWindowDecorViewModelTests"; - private static final Rect STABLE_INSETS = new Rect(0, 100, 0, 0); - - @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 DisplayLayout mDisplayLayout; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private DesktopTasksController mDesktopTasksController; - @Mock private InputMonitor mInputMonitor; - @Mock private InputManager mInputManager; - @Mock private Transitions mTransitions; - @Mock private DesktopModeWindowDecorViewModel.InputMonitorFactory mMockInputMonitorFactory; - @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory; - @Mock private SurfaceControl.Transaction mTransaction; - @Mock private Display mDisplay; - @Mock private ShellController mShellController; - @Mock private ShellExecutor mShellExecutor; - @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener - mDesktopModeKeyguardChangeListener; - @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - @Mock private RecentsTransitionHandler mRecentsTransitionHandler; - - private final List<InputManager> mMockInputManagers = new ArrayList<>(); - - private ShellInit mShellInit; - private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel; - - @Before - public void setUp() { - mMockInputManagers.add(mInputManager); - mShellInit = new ShellInit(mShellExecutor); - mDesktopModeWindowDecorViewModel = - new DesktopModeWindowDecorViewModel( - mContext, - mMainHandler, - mMainChoreographer, - mShellInit, - mTaskOrganizer, - mDisplayController, - mShellController, - mSyncQueue, - mTransitions, - Optional.of(mDesktopTasksController), - mRecentsTransitionHandler, - mDesktopModeWindowDecorFactory, - mMockInputMonitorFactory, - mTransactionFactory, - mDesktopModeKeyguardChangeListener, - mRootTaskDisplayAreaOrganizer - ); - - doReturn(mDesktopModeWindowDecoration) - .when(mDesktopModeWindowDecorFactory) - .create(any(), any(), any(), any(), any(), any(), any(), any(), any()); - doReturn(mTransaction).when(mTransactionFactory).get(); - doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt()); - doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets(); - doNothing().when(mShellController).addKeyguardChangeListener(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]); - - mDesktopModeWindowDecoration.mDisplay = mDisplay; - doReturn(Display.DEFAULT_DISPLAY).when(mDisplay).getDisplayId(); - - mShellInit.init(); - } - - @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); - 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, - mRootTaskDisplayAreaOrganizer); - 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); - 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, - mRootTaskDisplayAreaOrganizer); - } - - @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); - 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 { - 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(); - } - - @Test - public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception { - doReturn(true).when( - mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded(); - - final int taskId = 1; - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN); - taskInfo.isFocused = true; - 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); - - taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED); - taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED); - mDesktopModeWindowDecorViewModel.onTaskChanging( - taskInfo, surfaceControl, startT, finishT); - - verify(mDesktopModeWindowDecorFactory, never()) - .create(any(), any(), any(), any(), any(), any(), any(), any(), any()); - } - - @Test - public void testRelayoutBlockedDuringRecentsTransition() throws Exception { - final ArgumentCaptor<RecentsTransitionStateListener> recentsCaptor = - ArgumentCaptor.forClass(RecentsTransitionStateListener.class); - verify(mRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture()); - - final IBinder transition = mock(IBinder.class); - final DesktopModeWindowDecoration decoration = mock(DesktopModeWindowDecoration.class); - final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); - final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); - final int taskId = 1; - final SurfaceControl taskSurface = new SurfaceControl(); - final ActivityManager.RunningTaskInfo taskInfo = - createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FREEFORM); - doReturn(decoration).when(mDesktopModeWindowDecorFactory) - .create(any(), any(), any(), eq(taskInfo), eq(taskSurface), any(), any(), any(), - any()); - - // Make sure a window decorations exists first by launching a freeform task. - mDesktopModeWindowDecorViewModel.onTaskOpening(taskInfo, taskSurface, startT, finishT); - // Now call back when as a Recents transition starts. - recentsCaptor.getValue().onTransitionStarted(transition); - - verify(decoration).incrementRelayoutBlock(); - verify(decoration).addTransitionPausingRelayout(transition); - } - - private static ActivityManager.RunningTaskInfo createTaskInfo(int taskId, - int displayId, @WindowConfiguration.WindowingMode 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/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt new file mode 100644 index 000000000000..00d70a75837b --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -0,0 +1,344 @@ +/* + * 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 android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration +import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD +import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.graphics.Rect +import android.hardware.display.DisplayManager +import android.hardware.display.VirtualDisplay +import android.os.Handler +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.Choreographer +import android.view.Display.DEFAULT_DISPLAY +import android.view.InputChannel +import android.view.InputMonitor +import android.view.SurfaceControl +import android.view.SurfaceView +import androidx.core.content.getSystemService +import androidx.test.filters.SmallTest +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.common.DisplayController +import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SyncTransactionQueue +import com.android.wm.shell.desktopmode.DesktopTasksController +import com.android.wm.shell.recents.RecentsTransitionHandler +import com.android.wm.shell.recents.RecentsTransitionStateListener +import com.android.wm.shell.sysui.KeyguardChangeListener +import com.android.wm.shell.sysui.ShellController +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever +import java.util.Optional +import java.util.function.Supplier + +/** Tests of [DesktopModeWindowDecorViewModel] */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class DesktopModeWindowDecorViewModelTests : ShellTestCase() { + @Mock private lateinit var mockDesktopModeWindowDecorFactory: + DesktopModeWindowDecoration.Factory + @Mock private lateinit var mockMainHandler: Handler + @Mock private lateinit var mockMainChoreographer: Choreographer + @Mock private lateinit var mockTaskOrganizer: ShellTaskOrganizer + @Mock private lateinit var mockDisplayController: DisplayController + @Mock private lateinit var mockDisplayLayout: DisplayLayout + @Mock private lateinit var mockSyncQueue: SyncTransactionQueue + @Mock private lateinit var mockDesktopTasksController: DesktopTasksController + @Mock private lateinit var mockInputMonitor: InputMonitor + @Mock private lateinit var mockTransitions: Transitions + @Mock private lateinit var mockInputMonitorFactory: + DesktopModeWindowDecorViewModel.InputMonitorFactory + @Mock private lateinit var mockShellController: ShellController + @Mock private lateinit var mockShellExecutor: ShellExecutor + @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer + @Mock private lateinit var mockRecentsTransitionHandler: RecentsTransitionHandler + + private val transactionFactory = Supplier<SurfaceControl.Transaction> { + SurfaceControl.Transaction() + } + + private lateinit var shellInit: ShellInit + private lateinit var desktopModeWindowDecorViewModel: DesktopModeWindowDecorViewModel + + @Before + fun setUp() { + shellInit = ShellInit(mockShellExecutor) + desktopModeWindowDecorViewModel = DesktopModeWindowDecorViewModel( + mContext, + mockMainHandler, + mockMainChoreographer, + shellInit, + mockTaskOrganizer, + mockDisplayController, + mockShellController, + mockSyncQueue, + mockTransitions, + Optional.of(mockDesktopTasksController), + mockRecentsTransitionHandler, + mockDesktopModeWindowDecorFactory, + mockInputMonitorFactory, + transactionFactory, + mockRootTaskDisplayAreaOrganizer + ) + + whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.stableInsets()).thenReturn(STABLE_INSETS) + whenever(mockInputMonitorFactory.create(any(), any())).thenReturn(mockInputMonitor) + + // InputChannel cannot be mocked because it passes to InputEventReceiver. + val inputChannels = InputChannel.openInputChannelPair(TAG) + inputChannels.first().dispose() + whenever(mockInputMonitor.inputChannel).thenReturn(inputChannels[1]) + + shellInit.init() + } + + @Test + fun testDeleteCaptionOnChangeTransitionWhenNecessary() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val taskSurface = SurfaceControl() + val decoration = setUpMockDecorationForTask(task) + + onTaskOpening(task, taskSurface) + + task.setWindowingMode(WINDOWING_MODE_UNDEFINED) + task.setActivityType(ACTIVITY_TYPE_UNDEFINED) + onTaskChanging(task, taskSurface) + + verify(mockDesktopModeWindowDecorFactory).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + verify(decoration).close() + } + + @Test + fun testCreateCaptionOnChangeTransitionWhenNecessary() { + val task = createTask( + windowingMode = WINDOWING_MODE_UNDEFINED, + activityType = ACTIVITY_TYPE_UNDEFINED + ) + val taskSurface = SurfaceControl() + setUpMockDecorationForTask(task) + + onTaskChanging(task, taskSurface) + verify(mockDesktopModeWindowDecorFactory, never()).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + + task.setWindowingMode(WINDOWING_MODE_FREEFORM) + task.setActivityType(ACTIVITY_TYPE_STANDARD) + onTaskChanging(task, taskSurface) + verify(mockDesktopModeWindowDecorFactory, times(1)).create( + mContext, + mockDisplayController, + mockTaskOrganizer, + task, + taskSurface, + mockMainHandler, + mockMainChoreographer, + mockSyncQueue, + mockRootTaskDisplayAreaOrganizer + ) + } + + @Test + fun testCreateAndDisposeEventReceiver() { + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + setUpMockDecorationForTask(task) + + onTaskOpening(task) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + + verify(mockInputMonitorFactory).create(any(), any()) + verify(mockInputMonitor).dispose() + } + + @Test + fun testEventReceiversOnMultipleDisplays() { + val secondaryDisplay = createVirtualDisplay() ?: return + val secondaryDisplayId = secondaryDisplay.display.displayId + val task = createTask(displayId = DEFAULT_DISPLAY, windowingMode = WINDOWING_MODE_FREEFORM) + val secondTask = createTask( + displayId = secondaryDisplayId, + windowingMode = WINDOWING_MODE_FREEFORM + ) + val thirdTask = createTask( + displayId = secondaryDisplayId, + windowingMode = WINDOWING_MODE_FREEFORM + ) + setUpMockDecorationsForTasks(task, secondTask, thirdTask) + + onTaskOpening(task) + onTaskOpening(secondTask) + onTaskOpening(thirdTask) + desktopModeWindowDecorViewModel.destroyWindowDecoration(thirdTask) + desktopModeWindowDecorViewModel.destroyWindowDecoration(task) + secondaryDisplay.release() + + verify(mockInputMonitorFactory, times(2)).create(any(), any()) + verify(mockInputMonitor, times(1)).dispose() + } + + @Test + fun testCaptionIsNotCreatedWhenKeyguardIsVisible() { + val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) + val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>() + verify(mockShellController).addKeyguardChangeListener(keyguardListenerCaptor.capture()) + + keyguardListenerCaptor.firstValue.onKeyguardVisibilityChanged( + true /* visible */, + true /* occluded */, + false /* animatingDismiss */ + ) + onTaskOpening(task) + + task.setWindowingMode(WINDOWING_MODE_UNDEFINED) + task.setWindowingMode(ACTIVITY_TYPE_UNDEFINED) + onTaskChanging(task) + + verify(mockDesktopModeWindowDecorFactory, never()) + .create(any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + } + + @Test + fun testRelayoutBlockedDuringRecentsTransition() { + val recentsCaptor = argumentCaptor<RecentsTransitionStateListener>() + verify(mockRecentsTransitionHandler).addTransitionStateListener(recentsCaptor.capture()) + + val transition = mock(IBinder::class.java) + val task = createTask(windowingMode = WINDOWING_MODE_FREEFORM) + val decoration = setUpMockDecorationForTask(task) + + // Make sure a window decorations exists first by launching a freeform task. + onTaskOpening(task) + // Now call back when a Recents transition starts. + recentsCaptor.firstValue.onTransitionStarted(transition) + + verify(decoration).incrementRelayoutBlock() + verify(decoration).addTransitionPausingRelayout(transition) + } + + private fun onTaskOpening(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { + desktopModeWindowDecorViewModel.onTaskOpening( + task, + leash, + SurfaceControl.Transaction(), + SurfaceControl.Transaction() + ) + } + + private fun onTaskChanging(task: RunningTaskInfo, leash: SurfaceControl = SurfaceControl()) { + desktopModeWindowDecorViewModel.onTaskChanging( + task, + leash, + SurfaceControl.Transaction(), + SurfaceControl.Transaction() + ) + } + + private fun createTask( + displayId: Int = DEFAULT_DISPLAY, + @WindowConfiguration.WindowingMode windowingMode: Int, + activityType: Int = ACTIVITY_TYPE_STANDARD, + focused: Boolean = true + ): RunningTaskInfo { + return TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setWindowingMode(windowingMode) + .setVisible(true) + .setActivityType(activityType) + .build().apply { + isFocused = focused + } + } + + private fun setUpMockDecorationForTask(task: RunningTaskInfo): DesktopModeWindowDecoration { + val decoration = mock(DesktopModeWindowDecoration::class.java) + whenever(mockDesktopModeWindowDecorFactory.create( + any(), any(), any(), eq(task), any(), any(), any(), any(), any()) + ).thenReturn(decoration) + return decoration + } + + private fun setUpMockDecorationsForTasks(vararg tasks: RunningTaskInfo) { + tasks.forEach { setUpMockDecorationForTask(it) } + } + + private fun createVirtualDisplay(): VirtualDisplay? { + val surfaceView = SurfaceView(mContext) + return mContext.getSystemService<DisplayManager>()?.createVirtualDisplay( + "testEventReceiversOnMultipleDisplays", + /*width=*/ 400, + /*height=*/ 400, + /*densityDpi=*/320, + surfaceView.holder.surface, + DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + ) + } + + private fun RunningTaskInfo.setWindowingMode(@WindowConfiguration.WindowingMode mode: Int) { + configuration.windowConfiguration.windowingMode = mode + } + + private fun RunningTaskInfo.setActivityType(type: Int) { + configuration.windowConfiguration.activityType = type + } + + companion object { + private const val TAG = "DesktopModeWindowDecorViewModelTests" + private val STABLE_INSETS = Rect(0, 100, 0, 0) + } +} |