Convert DesktopModeWindowDecorViewModelTests to kt

Bug: None
Test: atest DesktopModeWindowDecorViewModelTests
Change-Id: Ic37afd362fef68bfd393a494057d45e1e4fe71e1
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 949ee81..afa2754 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 @@
 
     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 @@
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
                 SurfaceControl.Transaction::new,
-                new DesktopModeKeyguardChangeListener(),
                 rootTaskDisplayAreaOrganizer);
     }
 
@@ -177,7 +177,6 @@
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
             Supplier<SurfaceControl.Transaction> transactionFactory,
-            DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
         mContext = context;
         mMainHandler = mainHandler;
@@ -194,7 +193,6 @@
         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 3b3b924..0000000
--- 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 0000000..00d70a7
--- /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)
+    }
+}