Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c11f2f7 authored by Merissa Mitchell's avatar Merissa Mitchell
Browse files

[PiP on Desktop] Reparent expanding PiP to root desk for multi-desks.

Also update windowing mode to UNDEFINED if PiP is expanding to Desktop
task and multi-desks flag is enabled.

Bug: 416355546
Bug: 403345629
Test: atest PipDesktopStateTest DesktopPipTransitionControllerTest
Test: Manual - expand PiP while in Desktop, bring another task to front
and verify that when tapping on previously PiP-ed task on app handle, it
is brought to front
Flag: com.android.window.flags.enable_desktop_windowing_pip

Change-Id: I4425e7239c87a386a21b237358f7fecd71c25a8a
parent 519eb794
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -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
+64 −18
Original line number Diff line number Diff line
@@ -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.
     *
@@ -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
        }

@@ -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(
@@ -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,
@@ -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)
    }
@@ -161,5 +206,6 @@ class DesktopPipTransitionController(

    private companion object {
        private const val TAG = "DesktopPipTransitionController"
        private const val INVALID_DESK_ID = -1
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -1177,6 +1177,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 =
+8 −4
Original line number Diff line number Diff line
@@ -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;
    }
+14 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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)
@@ -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)
@@ -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)
@@ -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