Merge "Break tiling on entering pip mode." into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index c3d15df..9c55f0e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -17,6 +17,7 @@
 package com.android.wm.shell.windowdecor.tiling
 
 import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
 import android.content.Context
 import android.content.res.Configuration
 import android.content.res.Resources
@@ -28,9 +29,11 @@
 import android.view.SurfaceControl.Transaction
 import android.view.WindowManager.TRANSIT_CHANGE
 import android.view.WindowManager.TRANSIT_OPEN
+import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerTransaction
 import com.android.internal.annotations.VisibleForTesting
@@ -422,6 +425,8 @@
             change.taskInfo?.let {
                 if (it.isFullscreen || isMinimized(change.mode, info.type)) {
                     removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
+                } else if (isEnteringPip(change, info.type)) {
+                    removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
                 }
             }
         }
@@ -434,6 +439,27 @@
                 infoType == TRANSIT_OPEN))
     }
 
+    private fun isEnteringPip(change: Change, transitType: Int): Boolean {
+        if (change.taskInfo != null && change.taskInfo?.windowingMode == WINDOWING_MODE_PINNED) {
+            // - TRANSIT_PIP: type (from RootWindowContainer)
+            // - TRANSIT_OPEN (from apps that enter PiP instantly on opening, mostly from
+            // CTS/Flicker tests).
+            // - TRANSIT_TO_FRONT, though uncommon with triggering PiP, should semantically also
+            // be allowed to animate if the task in question is pinned already - see b/308054074.
+            // - TRANSIT_CHANGE: This can happen if the request to enter PIP happens when we are
+            // collecting for another transition, such as TRANSIT_CHANGE (display rotation).
+            if (
+                transitType == TRANSIT_PIP ||
+                    transitType == TRANSIT_OPEN ||
+                    transitType == TRANSIT_TO_FRONT ||
+                    transitType == TRANSIT_CHANGE
+            ) {
+                return true
+            }
+        }
+        return false
+    }
+
     class AppResizingHelper(
         val taskInfo: RunningTaskInfo,
         val desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -550,12 +576,16 @@
         taskVanished: Boolean = false,
         shouldDelayUpdate: Boolean = false,
     ) {
+        val taskRepository = desktopUserRepositories.current
         if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
             removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
             leftTaskResizingHelper = null
-            rightTaskResizingHelper
-                ?.desktopModeWindowDecoration
-                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            val taskId = rightTaskResizingHelper?.taskInfo?.taskId
+            if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+                rightTaskResizingHelper
+                    ?.desktopModeWindowDecoration
+                    ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            }
             tearDownTiling()
             return
         }
@@ -563,9 +593,12 @@
         if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
             removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
             rightTaskResizingHelper = null
-            leftTaskResizingHelper
-                ?.desktopModeWindowDecoration
-                ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            val taskId = leftTaskResizingHelper?.taskInfo?.taskId
+            if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+                leftTaskResizingHelper
+                    ?.desktopModeWindowDecoration
+                    ?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
+            }
             tearDownTiling()
         }
     }
@@ -600,7 +633,6 @@
 
     fun onOverviewAnimationStateChange(isRunning: Boolean) {
         if (!isTilingManagerInitialised) return
-
         if (isRunning) {
             desktopTilingDividerWindowManager?.hideDividerBar()
         } else if (allTiledTasksVisible()) {
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
index c40a04c..b511fc3 100644
--- 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
@@ -22,6 +22,7 @@
 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_PINNED
 import android.content.ComponentName
 import android.graphics.Rect
 import android.view.Display.DEFAULT_DISPLAY
@@ -45,6 +46,17 @@
             .apply { bounds?.let { setBounds(it) } }
             .build()
 
+    fun createPinnedTask(displayId: Int = DEFAULT_DISPLAY, bounds: Rect? = null): RunningTaskInfo =
+        TestRunningTaskInfoBuilder()
+            .setDisplayId(displayId)
+            .setParentTaskId(displayId)
+            .setToken(MockToken().token())
+            .setActivityType(ACTIVITY_TYPE_STANDARD)
+            .setWindowingMode(WINDOWING_MODE_PINNED)
+            .setLastActiveTime(100)
+            .apply { bounds?.let { setBounds(it) } }
+            .build()
+
     fun createFullscreenTaskBuilder(displayId: Int = DEFAULT_DISPLAY): TestRunningTaskInfoBuilder =
         TestRunningTaskInfoBuilder()
             .setDisplayId(displayId)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index bc8faed..e4424f3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -16,6 +16,7 @@
 package com.android.wm.shell.windowdecor.tiling
 
 import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
 import android.content.res.Resources
 import android.graphics.Rect
@@ -24,8 +25,10 @@
 import android.view.MotionEvent
 import android.view.SurfaceControl
 import android.view.WindowManager.TRANSIT_CHANGE
+import android.view.WindowManager.TRANSIT_PIP
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
 import android.window.WindowContainerTransaction
 import androidx.test.filters.SmallTest
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -40,6 +43,7 @@
 import com.android.wm.shell.desktopmode.DesktopRepository
 import com.android.wm.shell.desktopmode.DesktopTasksController
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
+import com.android.wm.shell.desktopmode.DesktopTestHelpers.createPinnedTask
 import com.android.wm.shell.desktopmode.DesktopUserRepositories
 import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator
 import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler
@@ -552,6 +556,37 @@
     }
 
     @Test
+    fun taskTiled_shouldBeRemoved_whenEnteringPip() {
+        val task1 = createPipTask()
+        val stableBounds = STABLE_BOUNDS_MOCK
+        whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+            (i.arguments.first() as Rect).set(stableBounds)
+        }
+        whenever(context.resources).thenReturn(resources)
+        whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+        whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+        whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+        tilingDecoration.onAppTiled(
+            task1,
+            desktopWindowDecoration,
+            DesktopTasksController.SnapPosition.LEFT,
+            BOUNDS,
+        )
+        tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+        val changeInfo = createPipChangeTransition(task1)
+        tilingDecoration.onTransitionReady(
+            transition = mock(),
+            info = changeInfo,
+            startTransaction = mock(),
+            finishTransaction = mock(),
+        )
+
+        assertThat(tilingDecoration.leftTaskResizingHelper).isNull()
+        verify(tiledTaskHelper, times(1)).dispose()
+    }
+
+    @Test
     fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
         val task1 = createVisibleTask()
         val task2 = createVisibleTask()
@@ -652,6 +687,23 @@
             whenever(userRepositories.current.isVisibleTask(eq(it.taskId))).thenReturn(true)
         }
 
+    private fun createPipTask() =
+        createPinnedTask().also {
+            whenever(userRepositories.current.isVisibleTask(eq(it.taskId))).thenReturn(true)
+        }
+
+    private fun createPipChangeTransition(task: RunningTaskInfo?, type: Int = TRANSIT_PIP) =
+        TransitionInfo(type, /* flags= */ 0).apply {
+            addChange(
+                Change(mock(), mock()).apply {
+                    mode = TRANSIT_PIP
+                    parent = null
+                    taskInfo = task
+                    flags = flags
+                }
+            )
+        }
+
     companion object {
         private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
         private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)