Exclude opening/move-to-front task from exiting immersive

It's possible for a non-top task to be moved to front and enter
immersive at the same time, so exclude it in move-to-front or opening
WCT from exiting immersive mode, since it doesn't make sense to exit
immersive in those cases.

Flag: com.android.window.flags.enable_fully_immersive_in_desktop
Fix: 374772576
Test: open Candy Crush in desktop, enter immersive, open settings from
notification shade, close settings, put candy crash back in immersive
and verify the app header can't be dragged or double clicked.

Change-Id: I9f614065814dcfbb2bae347ba00c10a53cdae71a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
index 9d4926b..19ffd96 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandler.kt
@@ -137,14 +137,19 @@
      *
      * @param wct that will apply these changes
      * @param displayId of the display that should exit immersive mode
+     * @param excludeTaskId of the task to ignore (not exit) if it is the immersive one
      * @return a function to apply once the transition that will apply these changes is started
      */
     fun exitImmersiveIfApplicable(
         wct: WindowContainerTransaction,
-        displayId: Int
+        displayId: Int,
+        excludeTaskId: Int? = null,
     ): ((IBinder) -> Unit)? {
         if (!Flags.enableFullyImmersiveInDesktop()) return null
         val immersiveTask = desktopRepository.getTaskInFullImmersiveState(displayId) ?: return null
+        if (immersiveTask == excludeTaskId) {
+            return null
+        }
         val taskInfo = shellTaskOrganizer.getRunningTaskInfo(immersiveTask) ?: return null
         logV("Appending immersive exit for task: $immersiveTask in display: $displayId")
         wct.setBounds(taskInfo.token, getExitDestinationBounds(taskInfo))
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 1d17cd6..28e63f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -357,8 +357,11 @@
         // TODO(342378842): Instead of using default display, support multiple displays
         val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
             DEFAULT_DISPLAY, wct, taskId)
-        val runOnTransit = immersiveTransitionHandler
-            .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+            wct = wct,
+            displayId = DEFAULT_DISPLAY,
+            excludeTaskId = taskId,
+        )
         wct.startTask(
             taskId,
             ActivityOptions.makeBasic().apply {
@@ -385,7 +388,11 @@
         }
         logV("moveRunningTaskToDesktop taskId=%d", task.taskId)
         exitSplitIfApplicable(wct, task)
-        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(wct, task.displayId)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+            wct = wct,
+            displayId = task.displayId,
+            excludeTaskId = task.taskId,
+        )
         // Bring other apps to front first
         val taskToMinimize =
             bringDesktopAppsToFrontBeforeShowingNewTask(task.displayId, wct, task.taskId)
@@ -594,8 +601,11 @@
         logV("moveBackgroundTaskToFront taskId=%s", taskId)
         val wct = WindowContainerTransaction()
         // TODO: b/342378842 - Instead of using default display, support multiple displays
-        val runOnTransit = immersiveTransitionHandler
-            .exitImmersiveIfApplicable(wct, DEFAULT_DISPLAY)
+        val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+            wct = wct,
+            displayId = DEFAULT_DISPLAY,
+            excludeTaskId = taskId,
+        )
         wct.startTask(
             taskId,
             ActivityOptions.makeBasic().apply {
@@ -618,7 +628,10 @@
         val wct = WindowContainerTransaction()
         wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
         val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
-            wct, taskInfo.displayId)
+            wct = wct,
+            displayId = taskInfo.displayId,
+            excludeTaskId = taskInfo.taskId,
+        )
         val transition =
             startLaunchTransition(TRANSIT_TO_FRONT, wct, taskInfo.taskId, remoteTransition)
         runOnTransit?.invoke(transition)
@@ -1218,8 +1231,11 @@
             wct.startTask(requestedTaskId, options.toBundle())
             val taskToMinimize = bringDesktopAppsToFrontBeforeShowingNewTask(
                 callingTask.displayId, wct, requestedTaskId)
-            val runOnTransit = immersiveTransitionHandler
-                .exitImmersiveIfApplicable(wct, callingTask.displayId)
+            val runOnTransit = immersiveTransitionHandler.exitImmersiveIfApplicable(
+                wct = wct,
+                displayId = callingTask.displayId,
+                excludeTaskId = requestedTaskId,
+            )
             val transition = transitions.startTransition(TRANSIT_OPEN, wct, null)
             addPendingMinimizeTransition(transition, taskToMinimize)
             runOnTransit?.invoke(transition)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
index ef99b00..5842dfa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopFullImmersiveTransitionHandlerTest.kt
@@ -340,6 +340,31 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
+    fun exitImmersiveIfApplicable_byDisplay_withExcludeTask_doesNotExit() {
+        val task = createFreeformTask()
+        whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
+        val wct = WindowContainerTransaction()
+        val transition = Binder()
+        desktopRepository.setTaskInFullImmersiveState(
+            displayId = DEFAULT_DISPLAY,
+            taskId = task.taskId,
+            immersive = true
+        )
+
+        immersiveHandler.exitImmersiveIfApplicable(
+            wct = wct,
+            displayId = DEFAULT_DISPLAY,
+            excludeTaskId = task.taskId
+        )?.invoke(transition)
+
+        assertThat(immersiveHandler.pendingExternalExitTransitions.any { exit ->
+            exit.transition == transition && exit.displayId == DEFAULT_DISPLAY
+                    && exit.taskId == task.taskId
+        }).isFalse()
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_ENABLE_FULLY_IMMERSIVE_IN_DESKTOP)
     fun exitImmersiveIfApplicable_byTask_inImmersive_changesTaskBounds() {
         val task = createFreeformTask()
         whenever(mockShellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
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
index af51e32..c50d304 100644
--- 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
@@ -3060,12 +3060,13 @@
     whenever(transitions.startTransition(eq(TRANSIT_OPEN), any(), anyOrNull()))
       .thenReturn(transition)
     whenever(mockDesktopFullImmersiveTransitionHandler
-      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId)))
+      .thenReturn(runOnStartTransit)
 
     runOpenInstance(immersiveTask, freeformTask.taskId)
 
     verify(mockDesktopFullImmersiveTransitionHandler)
-      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId))
+      .exitImmersiveIfApplicable(any(), eq(immersiveTask.displayId), eq(freeformTask.taskId))
     runOnStartTransit.assertOnlyInvocation(transition)
   }
 
@@ -3370,12 +3371,13 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mockDesktopFullImmersiveTransitionHandler
-      .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit)
     whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
 
     controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
 
-    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId)
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)
     runOnStartTransit.assertOnlyInvocation(transition)
   }
 
@@ -3386,12 +3388,13 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mockDesktopFullImmersiveTransitionHandler
-      .exitImmersiveIfApplicable(wct, task.displayId)).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)).thenReturn(runOnStartTransit)
     whenever(enterDesktopTransitionHandler.moveToDesktop(wct, UNKNOWN)).thenReturn(transition)
 
     controller.moveTaskToDesktop(taskId = task.taskId, wct = wct, transitionSource = UNKNOWN)
 
-    verify(mockDesktopFullImmersiveTransitionHandler).exitImmersiveIfApplicable(wct, task.displayId)
+    verify(mockDesktopFullImmersiveTransitionHandler)
+      .exitImmersiveIfApplicable(wct, task.displayId, task.taskId)
     runOnStartTransit.assertOnlyInvocation(transition)
   }
 
@@ -3401,13 +3404,14 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mockDesktopFullImmersiveTransitionHandler
-      .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
+      .thenReturn(runOnStartTransit)
     whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
     verify(mockDesktopFullImmersiveTransitionHandler)
-      .exitImmersiveIfApplicable(any(), eq(task.displayId))
+      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))
     runOnStartTransit.assertOnlyInvocation(transition)
   }
 
@@ -3417,13 +3421,14 @@
     val runOnStartTransit = RunOnStartTransitionCallback()
     val transition = Binder()
     whenever(mockDesktopFullImmersiveTransitionHandler
-      .exitImmersiveIfApplicable(any(), eq(task.displayId))).thenReturn(runOnStartTransit)
+      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId)))
+      .thenReturn(runOnStartTransit)
     whenever(transitions.startTransition(any(), any(), anyOrNull())).thenReturn(transition)
 
     controller.moveTaskToFront(task.taskId, remoteTransition = null)
 
     verify(mockDesktopFullImmersiveTransitionHandler)
-      .exitImmersiveIfApplicable(any(), eq(task.displayId))
+      .exitImmersiveIfApplicable(any(), eq(task.displayId), eq(task.taskId))
     runOnStartTransit.assertOnlyInvocation(transition)
   }