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

Commit 77609844 authored by Merissa Mitchell's avatar Merissa Mitchell
Browse files

[PiP on Desktop] Always expand PiP to fullscreen if mid-recents.

This is a workaround for an issue where expanding PiP while in Recents
during Desktop session will lead to the device getting stuck in a bad
state. This is a temporary fix by forcing PiP to expand to fullscreen
always, if we are expanding while in Recents in a Desktop session.

Bug: 409201669
Test: atest PipDesktopStateTest
Flag: com.android.window.flags.enable_desktop_windowing_pip

Change-Id: Ibcdc47054aea52092341e55e2ee435543db58d5d
parent 42954a53
Loading
Loading
Loading
Loading
+41 −2
Original line number Original line Diff line number Diff line
@@ -16,22 +16,47 @@
package com.android.wm.shell.common.pip
package com.android.wm.shell.common.pip


import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.window.DesktopExperienceFlags
import android.window.DesktopExperienceFlags
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.Flags
import com.android.wm.shell.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.recents.RecentsTransitionStateListener.RecentsTransitionState
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
import java.util.Optional
import java.util.Optional


/** Helper class for PiP on Desktop Mode. */
/** Helper class for PiP on Desktop Mode. */
class PipDesktopState(
class PipDesktopState(
    private val pipDisplayLayoutState: PipDisplayLayoutState,
    private val pipDisplayLayoutState: PipDisplayLayoutState,
    recentsTransitionHandler: RecentsTransitionHandler,
    private val desktopUserRepositoriesOptional: Optional<DesktopUserRepositories>,
    private val desktopUserRepositoriesOptional: Optional<DesktopUserRepositories>,
    private val dragToDesktopTransitionHandlerOptional: Optional<DragToDesktopTransitionHandler>,
    private val dragToDesktopTransitionHandlerOptional: Optional<DragToDesktopTransitionHandler>,
    val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
    val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
) {
) {
    @RecentsTransitionState
    private var recentsTransitionState = TRANSITION_STATE_NOT_RUNNING

    init {
        recentsTransitionHandler.addTransitionStateListener(
            object : RecentsTransitionStateListener {
                override fun onTransitionStateChanged(@RecentsTransitionState state: Int) {
                    logV(
                        "Recents transition state changed: %s",
                        RecentsTransitionStateListener.stateToString(state),
                    )
                    recentsTransitionState = state
                }
            }
        )
    }

    /**
    /**
     * Returns whether PiP in Desktop Windowing is enabled by checking the following:
     * Returns whether PiP in Desktop Windowing is enabled by checking the following:
     * - PiP in Desktop Windowing flag is enabled
     * - PiP in Desktop Windowing flag is enabled
@@ -83,14 +108,20 @@ class PipDesktopState(
    }
    }


    /** Returns the windowing mode to restore to when resizing out of PIP direction. */
    /** Returns the windowing mode to restore to when resizing out of PIP direction. */
    // TODO(b/403345629): Update this for Multi-Desktop.
    fun getOutPipWindowingMode(): Int {
    fun getOutPipWindowingMode(): Int {
        val isInDesktop = isPipInDesktopMode()
        // Temporary workaround for b/409201669: Always expand to fullscreen if we're exiting PiP
        // in the middle of Recents animation from Desktop session.
        if (RecentsTransitionStateListener.isAnimating(recentsTransitionState) && isInDesktop) {
            return WINDOWING_MODE_FULLSCREEN
        }

        // If we are exiting PiP while the device is in Desktop mode, the task should expand to
        // If we are exiting PiP while the device is in Desktop mode, the task should expand to
        // freeform windowing mode.
        // freeform windowing mode.
        // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will
        // 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.
        //    resolve the windowing mode to the display's windowing mode.
        // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM.
        // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM.
        if (isPipInDesktopMode()) {
        if (isInDesktop) {
            return if (isDisplayInFreeform()) {
            return if (isDisplayInFreeform()) {
                WINDOWING_MODE_UNDEFINED
                WINDOWING_MODE_UNDEFINED
            } else {
            } else {
@@ -108,4 +139,12 @@ class PipDesktopState(


    /** Returns the DisplayLayout associated with the display where PiP window is in. */
    /** Returns the DisplayLayout associated with the display where PiP window is in. */
    fun getCurrentDisplayLayout(): DisplayLayout = pipDisplayLayoutState.displayLayout
    fun getCurrentDisplayLayout(): DisplayLayout = pipDisplayLayoutState.displayLayout

    private fun logV(msg: String, vararg arguments: Any?) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: $msg", TAG, *arguments)
    }

    companion object {
        private const val TAG = "PipDesktopState"
    }
}
}
+5 −2
Original line number Original line Diff line number Diff line
@@ -61,6 +61,7 @@ import com.android.wm.shell.pip2.phone.PipTouchHandler;
import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.pip2.phone.PipTransition;
import com.android.wm.shell.pip2.phone.PipTransitionState;
import com.android.wm.shell.pip2.phone.PipTransitionState;
import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.pip2.phone.PipUiStateChangeController;
import com.android.wm.shell.recents.RecentsTransitionHandler;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.shared.desktopmode.DesktopState;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -277,12 +278,14 @@ public abstract class Pip2Module {
    @Provides
    @Provides
    static PipDesktopState providePipDesktopState(
    static PipDesktopState providePipDesktopState(
            PipDisplayLayoutState pipDisplayLayoutState,
            PipDisplayLayoutState pipDisplayLayoutState,
            RecentsTransitionHandler recentsTransitionHandler,
            Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
            Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
            Optional<DragToDesktopTransitionHandler> dragToDesktopTransitionHandlerOptional,
            Optional<DragToDesktopTransitionHandler> dragToDesktopTransitionHandlerOptional,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer
    ) {
    ) {
        return new PipDesktopState(pipDisplayLayoutState, desktopUserRepositoriesOptional,
        return new PipDesktopState(pipDisplayLayoutState, recentsTransitionHandler,
                dragToDesktopTransitionHandlerOptional, rootTaskDisplayAreaOrganizer);
                desktopUserRepositoriesOptional, dragToDesktopTransitionHandlerOptional,
                rootTaskDisplayAreaOrganizer);
    }
    }


    @WMSingleton
    @WMSingleton
+22 −0
Original line number Original line Diff line number Diff line
@@ -34,12 +34,18 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DesktopUserRepositories
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_ANIMATING
import com.android.wm.shell.recents.RecentsTransitionStateListener.TRANSITION_STATE_NOT_RUNNING
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import java.util.Optional
import org.junit.Before
import org.junit.Before
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.mockito.kotlin.whenever


/**
/**
@@ -51,12 +57,14 @@ import org.mockito.kotlin.whenever
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
class PipDesktopStateTest : ShellTestCase() {
class PipDesktopStateTest : ShellTestCase() {
    private val mockPipDisplayLayoutState = mock<PipDisplayLayoutState>()
    private val mockPipDisplayLayoutState = mock<PipDisplayLayoutState>()
    private val mockRecentsTransitionHandler = mock<RecentsTransitionHandler>()
    private val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
    private val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
    private val mockDesktopRepository = mock<DesktopRepository>()
    private val mockDesktopRepository = mock<DesktopRepository>()
    private val mockDragToDesktopTransitionHandler = mock<DragToDesktopTransitionHandler>()
    private val mockDragToDesktopTransitionHandler = mock<DragToDesktopTransitionHandler>()
    private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
    private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
    private val mockTaskInfo = mock<ActivityManager.RunningTaskInfo>()
    private val mockTaskInfo = mock<ActivityManager.RunningTaskInfo>()
    private lateinit var defaultTda: DisplayAreaInfo
    private lateinit var defaultTda: DisplayAreaInfo
    private lateinit var recentsTransitionStateListener: RecentsTransitionStateListener
    private lateinit var pipDesktopState: PipDesktopState
    private lateinit var pipDesktopState: PipDesktopState


    @Before
    @Before
@@ -73,10 +81,16 @@ class PipDesktopStateTest : ShellTestCase() {
        pipDesktopState =
        pipDesktopState =
            PipDesktopState(
            PipDesktopState(
                mockPipDisplayLayoutState,
                mockPipDisplayLayoutState,
                mockRecentsTransitionHandler,
                Optional.of(mockDesktopUserRepositories),
                Optional.of(mockDesktopUserRepositories),
                Optional.of(mockDragToDesktopTransitionHandler),
                Optional.of(mockDragToDesktopTransitionHandler),
                mockRootTaskDisplayAreaOrganizer
                mockRootTaskDisplayAreaOrganizer
            )
            )

        val captor = argumentCaptor<RecentsTransitionStateListener>()
        verify(mockRecentsTransitionHandler).addTransitionStateListener(captor.capture())
        recentsTransitionStateListener = captor.firstValue
        recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_NOT_RUNNING)
    }
    }


    @Test
    @Test
@@ -140,6 +154,14 @@ class PipDesktopStateTest : ShellTestCase() {
        assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED)
        assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED)
    }
    }


    @Test
    fun outPipWindowingMode_midRecents_inDesktop_returnsFullscreen() {
        whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true)
        recentsTransitionStateListener.onTransitionStateChanged(TRANSITION_STATE_ANIMATING)

        assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN)
    }

    @Test
    @Test
    fun isDragToDesktopInProgress_inProgress_returnsTrue() {
    fun isDragToDesktopInProgress_inProgress_returnsTrue() {
        whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(true)
        whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(true)