Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt +6 −4 Original line number Diff line number Diff line Loading @@ -119,11 +119,13 @@ class PipDesktopState( // If we are exiting PiP while the device is in Desktop mode, the task should expand to // freeform windowing mode. // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will // resolve the windowing mode to the display's windowing mode. // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM. // 1) If the display windowing mode is freeform or if the ENABLE_MULTIPLE_DESKTOPS_BACKEND // flag is true, set windowing mode to UNDEFINED so it will resolve the windowing mode to // the display or root desk's windowing mode (which is always FREEFORM). // 2) Otherwise, set windowing mode to FREEFORM. if (isInDesktop) { return if (isDisplayInFreeform()) { return if (isDisplayInFreeform() || DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { WINDOWING_MODE_UNDEFINED } else { WINDOWING_MODE_FREEFORM Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt +64 −18 Original line number Diff line number Diff line Loading @@ -83,6 +83,46 @@ class DesktopPipTransitionController( } } /** * This is called by [PipScheduler#getExitPipViaExpandTransaction] before starting a PiP * transition. If the ENABLE_MULTIPLE_DESKTOPS_BACKEND flag is enabled and the PiP task is going * to freeform windowing mode, we need to reparent the task to the root desk. * * @param wct WindowContainerTransaction that will apply these changes * @param taskId of the task that is exiting PiP */ fun maybeReparentTaskToDesk(wct: WindowContainerTransaction, taskId: Int) { if ( !pipDesktopState.isDesktopWindowingPipEnabled() || !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ) { return } val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId) if (runningTaskInfo == null) { logW("maybeReparentTaskToDesk: Failed to find RunningTaskInfo for taskId=%d", taskId) return } val desktopRepository = desktopUserRepositories.getProfile(runningTaskInfo.userId) val displayId = runningTaskInfo.displayId if (!desktopRepository.isAnyDeskActive(displayId)) { logD("maybeReparentTaskToDesk: PiP transition is not in Desktop session") return } val deskId = getDeskId(desktopRepository, displayId) if (deskId == INVALID_DESK_ID) return logD( "maybeReparentTaskToDesk: addMoveToDeskTaskChanges, taskId=%d deskId=%d", runningTaskInfo.taskId, deskId, ) desktopTasksController.addMoveToDeskTaskChanges(wct, runningTaskInfo, deskId) } /** * This is called by [PipTransition#handleRequest] when a request for entering PiP is received. * Loading @@ -102,7 +142,7 @@ class DesktopPipTransitionController( // Early return if the transition is a synthetic transition that is not backed by a true // system transition. if (transition == DesktopTasksController.SYNTHETIC_TRANSITION) { logD("handlePipTransitionIfInDesktop: SYNTHETIC_TRANSITION, not a true transition") logD("handlePipTransition: SYNTHETIC_TRANSITION, not a true transition") return } Loading @@ -110,25 +150,12 @@ class DesktopPipTransitionController( val displayId = taskInfo.displayId val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) if (!desktopRepository.isAnyDeskActive(displayId)) { logD("handlePipTransitionIfInDesktop: PiP transition is not in Desktop session") logD("handlePipTransition: PiP transition is not in Desktop session") return } val deskId = desktopRepository.getActiveDeskId(displayId) ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { logW( "handlePipTransitionIfInDesktop: " + "Active desk not found for display id %d", displayId, ) return } else { checkNotNull(desktopRepository.getDefaultDeskId(displayId)) { "$TAG: handlePipTransitionIfInDesktop: " + "Expected a default desk to exist in display with id $displayId" } } val deskId = getDeskId(desktopRepository, displayId) if (deskId == INVALID_DESK_ID) return val isLastTask = desktopRepository.isOnlyVisibleNonClosingTaskInDesk( Loading @@ -137,10 +164,16 @@ class DesktopPipTransitionController( displayId = displayId, ) if (!isLastTask) { logD("handlePipTransitionIfInDesktop: PiP task is not last visible task in Desk") logD("handlePipTransition: PiP task is not last visible task in Desk") return } logD( "handlePipTransition: performDesktopExitCleanUp, taskId=%d deskId=%d displayId=%d", taskInfo.taskId, deskId, displayId, ) val desktopExitRunnable = desktopTasksController.performDesktopExitCleanUp( wct = wct, Loading @@ -151,6 +184,18 @@ class DesktopPipTransitionController( desktopExitRunnable?.invoke(transition) } private fun getDeskId(repository: DesktopRepository, displayId: Int): Int = repository.getActiveDeskId(displayId) ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { logW("getDeskId: Active desk not found for display id %d", displayId) INVALID_DESK_ID } else { checkNotNull(repository.getDefaultDeskId(displayId)) { "$TAG: getDeskId: " + "Expected a default desk to exist in display with id $displayId" } } private fun logW(msg: String, vararg arguments: Any?) { ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } Loading @@ -161,5 +206,6 @@ class DesktopPipTransitionController( private companion object { private const val TAG = "DesktopPipTransitionController" private const val INVALID_DESK_ID = -1 } } libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +6 −0 Original line number Diff line number Diff line Loading @@ -1178,6 +1178,12 @@ class DesktopTasksController( DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue && (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false) && isPipAllowedInAppOps(taskInfo) logD( "minimizeTask isMinimizingToPip=%b isAutoEnterEnabled=%b isPipAllowedInAppOps=%b", isMinimizingToPip, (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false), isPipAllowedInAppOps(taskInfo), ) // If task is going to PiP, start a PiP transition instead of a minimize transition if (isMinimizingToPip) { val requestInfo = Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +8 −4 Original line number Diff line number Diff line Loading @@ -128,10 +128,14 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange wct.setBounds(pipTaskToken, null); wct.setWindowingMode(pipTaskToken, mPipDesktopState.getOutPipWindowingMode()); // In multi-activity case, windowing mode change will reparent to original host task, so we // have to update the parent windowing mode to what is expected. mDesktopPipTransitionController.ifPresent(c -> c.maybeUpdateParentInWct(wct, mPipTransitionState.getPipTaskInfo().lastParentTaskIdBeforePip)); mDesktopPipTransitionController.ifPresent(c -> { // In multi-activity case, windowing mode change will reparent to original host task, so // we have to update the parent windowing mode to what is expected. c.maybeUpdateParentInWct(wct, mPipTransitionState.getPipTaskInfo().lastParentTaskIdBeforePip); // In multi-desks case, we have to reparent the task to the root desk. c.maybeReparentTaskToDesk(wct, mPipTransitionState.getPipTaskInfo().taskId); }); return wct; } Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.app.ActivityManager import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper Loading @@ -28,6 +29,7 @@ import androidx.test.filters.SmallTest import com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP import com.android.window.flags.Flags.FLAG_ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopRepository Loading Loading @@ -126,6 +128,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.isPipInDesktopMode()).isFalse() } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading @@ -134,6 +137,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading @@ -142,6 +146,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() { setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN) Loading @@ -149,6 +154,15 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_multiDesktopsEnabled_returnsUndefined() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_midRecents_inDesktop_returnsFullscreen() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt +6 −4 Original line number Diff line number Diff line Loading @@ -119,11 +119,13 @@ class PipDesktopState( // If we are exiting PiP while the device is in Desktop mode, the task should expand to // freeform windowing mode. // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will // resolve the windowing mode to the display's windowing mode. // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM. // 1) If the display windowing mode is freeform or if the ENABLE_MULTIPLE_DESKTOPS_BACKEND // flag is true, set windowing mode to UNDEFINED so it will resolve the windowing mode to // the display or root desk's windowing mode (which is always FREEFORM). // 2) Otherwise, set windowing mode to FREEFORM. if (isInDesktop) { return if (isDisplayInFreeform()) { return if (isDisplayInFreeform() || DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { WINDOWING_MODE_UNDEFINED } else { WINDOWING_MODE_FREEFORM Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopPipTransitionController.kt +64 −18 Original line number Diff line number Diff line Loading @@ -83,6 +83,46 @@ class DesktopPipTransitionController( } } /** * This is called by [PipScheduler#getExitPipViaExpandTransaction] before starting a PiP * transition. If the ENABLE_MULTIPLE_DESKTOPS_BACKEND flag is enabled and the PiP task is going * to freeform windowing mode, we need to reparent the task to the root desk. * * @param wct WindowContainerTransaction that will apply these changes * @param taskId of the task that is exiting PiP */ fun maybeReparentTaskToDesk(wct: WindowContainerTransaction, taskId: Int) { if ( !pipDesktopState.isDesktopWindowingPipEnabled() || !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue ) { return } val runningTaskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId) if (runningTaskInfo == null) { logW("maybeReparentTaskToDesk: Failed to find RunningTaskInfo for taskId=%d", taskId) return } val desktopRepository = desktopUserRepositories.getProfile(runningTaskInfo.userId) val displayId = runningTaskInfo.displayId if (!desktopRepository.isAnyDeskActive(displayId)) { logD("maybeReparentTaskToDesk: PiP transition is not in Desktop session") return } val deskId = getDeskId(desktopRepository, displayId) if (deskId == INVALID_DESK_ID) return logD( "maybeReparentTaskToDesk: addMoveToDeskTaskChanges, taskId=%d deskId=%d", runningTaskInfo.taskId, deskId, ) desktopTasksController.addMoveToDeskTaskChanges(wct, runningTaskInfo, deskId) } /** * This is called by [PipTransition#handleRequest] when a request for entering PiP is received. * Loading @@ -102,7 +142,7 @@ class DesktopPipTransitionController( // Early return if the transition is a synthetic transition that is not backed by a true // system transition. if (transition == DesktopTasksController.SYNTHETIC_TRANSITION) { logD("handlePipTransitionIfInDesktop: SYNTHETIC_TRANSITION, not a true transition") logD("handlePipTransition: SYNTHETIC_TRANSITION, not a true transition") return } Loading @@ -110,25 +150,12 @@ class DesktopPipTransitionController( val displayId = taskInfo.displayId val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId) if (!desktopRepository.isAnyDeskActive(displayId)) { logD("handlePipTransitionIfInDesktop: PiP transition is not in Desktop session") logD("handlePipTransition: PiP transition is not in Desktop session") return } val deskId = desktopRepository.getActiveDeskId(displayId) ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { logW( "handlePipTransitionIfInDesktop: " + "Active desk not found for display id %d", displayId, ) return } else { checkNotNull(desktopRepository.getDefaultDeskId(displayId)) { "$TAG: handlePipTransitionIfInDesktop: " + "Expected a default desk to exist in display with id $displayId" } } val deskId = getDeskId(desktopRepository, displayId) if (deskId == INVALID_DESK_ID) return val isLastTask = desktopRepository.isOnlyVisibleNonClosingTaskInDesk( Loading @@ -137,10 +164,16 @@ class DesktopPipTransitionController( displayId = displayId, ) if (!isLastTask) { logD("handlePipTransitionIfInDesktop: PiP task is not last visible task in Desk") logD("handlePipTransition: PiP task is not last visible task in Desk") return } logD( "handlePipTransition: performDesktopExitCleanUp, taskId=%d deskId=%d displayId=%d", taskInfo.taskId, deskId, displayId, ) val desktopExitRunnable = desktopTasksController.performDesktopExitCleanUp( wct = wct, Loading @@ -151,6 +184,18 @@ class DesktopPipTransitionController( desktopExitRunnable?.invoke(transition) } private fun getDeskId(repository: DesktopRepository, displayId: Int): Int = repository.getActiveDeskId(displayId) ?: if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { logW("getDeskId: Active desk not found for display id %d", displayId) INVALID_DESK_ID } else { checkNotNull(repository.getDefaultDeskId(displayId)) { "$TAG: getDeskId: " + "Expected a default desk to exist in display with id $displayId" } } private fun logW(msg: String, vararg arguments: Any?) { ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } Loading @@ -161,5 +206,6 @@ class DesktopPipTransitionController( private companion object { private const val TAG = "DesktopPipTransitionController" private const val INVALID_DESK_ID = -1 } }
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +6 −0 Original line number Diff line number Diff line Loading @@ -1178,6 +1178,12 @@ class DesktopTasksController( DesktopExperienceFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue && (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false) && isPipAllowedInAppOps(taskInfo) logD( "minimizeTask isMinimizingToPip=%b isAutoEnterEnabled=%b isPipAllowedInAppOps=%b", isMinimizingToPip, (taskInfo.pictureInPictureParams?.isAutoEnterEnabled ?: false), isPipAllowedInAppOps(taskInfo), ) // If task is going to PiP, start a PiP transition instead of a minimize transition if (isMinimizingToPip) { val requestInfo = Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +8 −4 Original line number Diff line number Diff line Loading @@ -128,10 +128,14 @@ public class PipScheduler implements PipTransitionState.PipTransitionStateChange wct.setBounds(pipTaskToken, null); wct.setWindowingMode(pipTaskToken, mPipDesktopState.getOutPipWindowingMode()); // In multi-activity case, windowing mode change will reparent to original host task, so we // have to update the parent windowing mode to what is expected. mDesktopPipTransitionController.ifPresent(c -> c.maybeUpdateParentInWct(wct, mPipTransitionState.getPipTaskInfo().lastParentTaskIdBeforePip)); mDesktopPipTransitionController.ifPresent(c -> { // In multi-activity case, windowing mode change will reparent to original host task, so // we have to update the parent windowing mode to what is expected. c.maybeUpdateParentInWct(wct, mPipTransitionState.getPipTaskInfo().lastParentTaskIdBeforePip); // In multi-desks case, we have to reparent the task to the root desk. c.maybeReparentTaskToDesk(wct, mPipTransitionState.getPipTaskInfo().taskId); }); return wct; } Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ import android.app.ActivityManager import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper Loading @@ -28,6 +29,7 @@ import androidx.test.filters.SmallTest import com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP import com.android.window.flags.Flags.FLAG_ENABLE_DRAGGING_PIP_ACROSS_DISPLAYS import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.DesktopRepository Loading Loading @@ -126,6 +128,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.isPipInDesktopMode()).isFalse() } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading @@ -134,6 +137,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading @@ -142,6 +146,7 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() { setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN) Loading @@ -149,6 +154,15 @@ class PipDesktopStateTest : ShellTestCase() { assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_exitToDesktop_multiDesktopsEnabled_returnsUndefined() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) } @DisableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) @Test fun outPipWindowingMode_midRecents_inDesktop_returnsFullscreen() { whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) Loading