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

Commit 86442c15 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Keep resolved windowing mode of existing tasks" into main

parents 5a5db5d8 5b4a1c20
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1210,7 +1210,8 @@ public abstract class WMShellModule {
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            IWindowManager windowManager,
            Optional<DesktopUserRepositories> desktopUserRepositories,
            Optional<DesktopTasksController> desktopTasksController
            Optional<DesktopTasksController> desktopTasksController,
            ShellTaskOrganizer shellTaskOrganizer
    ) {
        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
            return Optional.empty();
@@ -1224,7 +1225,8 @@ public abstract class WMShellModule {
                        rootTaskDisplayAreaOrganizer,
                        windowManager,
                        desktopUserRepositories.get(),
                        desktopTasksController.get()));
                        desktopTasksController.get(),
                        shellTaskOrganizer));
    }

    @WMSingleton
+27 −1
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package com.android.wm.shell.desktopmode

import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.windowingModeToString
import android.content.Context
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
@@ -27,6 +30,7 @@ import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
@@ -45,6 +49,7 @@ class DesktopDisplayEventHandler(
    private val windowManager: IWindowManager,
    private val desktopUserRepositories: DesktopUserRepositories,
    private val desktopTasksController: DesktopTasksController,
    private val shellTaskOrganizer: ShellTaskOrganizer,
) : OnDisplaysChangedListener, OnDeskRemovedListener {

    private val desktopRepository: DesktopRepository
@@ -124,13 +129,34 @@ class DesktopDisplayEventHandler(
            }
        val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)
        requireNotNull(tdaInfo) { "DisplayAreaInfo of DEFAULT_DISPLAY must be non-null." }
        if (tdaInfo.configuration.windowConfiguration.windowingMode == targetDisplayWindowingMode) {
        val currentDisplayWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
        if (currentDisplayWindowingMode == targetDisplayWindowingMode) {
            // Already in the target mode.
            return
        }

        logV(
            "As an external display is connected, changing default display's windowing mode from" +
                " ${windowingModeToString(currentDisplayWindowingMode)}" +
                " to ${windowingModeToString(targetDisplayWindowingMode)}"
        )

        val wct = WindowContainerTransaction()
        wct.setWindowingMode(tdaInfo.token, targetDisplayWindowingMode)
        shellTaskOrganizer
            .getRunningTasks(DEFAULT_DISPLAY)
            .filter { it.activityType == ACTIVITY_TYPE_STANDARD }
            .forEach {
                // TODO: b/391965153 - Reconsider the logic under multi-desk window hierarchy
                when (it.windowingMode) {
                    currentDisplayWindowingMode -> {
                        wct.setWindowingMode(it.token, currentDisplayWindowingMode)
                    }
                    targetDisplayWindowingMode -> {
                        wct.setWindowingMode(it.token, WINDOWING_MODE_UNDEFINED)
                    }
                }
            }
        transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
    }

+76 −16
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.ContentResolver
import android.os.Binder
import android.platform.test.annotations.EnableFlags
@@ -37,7 +39,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags
import com.android.wm.shell.MockToken
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
@@ -81,12 +85,20 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
    @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
    @Mock private lateinit var mockDesktopRepository: DesktopRepository
    @Mock private lateinit var mockDesktopTasksController: DesktopTasksController
    @Mock private lateinit var shellTaskOrganizer: ShellTaskOrganizer

    private lateinit var mockitoSession: StaticMockitoSession
    private lateinit var shellInit: ShellInit
    private lateinit var handler: DesktopDisplayEventHandler

    private val onDisplaysChangedListenerCaptor = argumentCaptor<OnDisplaysChangedListener>()
    private val runningTasks = mutableListOf<RunningTaskInfo>()
    private val externalDisplayId = 100
    private val freeformTask =
        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FREEFORM).build()
    private val fullscreenTask =
        TestRunningTaskInfoBuilder().setWindowingMode(WINDOWING_MODE_FULLSCREEN).build()
    private val defaultTDA = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)

    @Before
    fun setUp() {
@@ -99,8 +111,8 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
        shellInit = spy(ShellInit(testExecutor))
        whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
        val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0)
        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
        whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
            .thenReturn(defaultTDA)
        handler =
            DesktopDisplayEventHandler(
                context,
@@ -111,7 +123,11 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
                mockWindowManager,
                mockDesktopUserRepositories,
                mockDesktopTasksController,
                shellTaskOrganizer,
            )
        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
        runningTasks.add(freeformTask)
        runningTasks.add(fullscreenTask)
        shellInit.init()
        verify(displayController)
            .addDisplayWindowListener(onDisplaysChangedListenerCaptor.capture())
@@ -127,9 +143,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
        extendedDisplayEnabled: Boolean,
        expectTransition: Boolean,
    ) {
        val externalDisplayId = 100
        val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
        tda.configuration.windowConfiguration.windowingMode = defaultWindowingMode
        defaultTDA.configuration.windowConfiguration.windowingMode = defaultWindowingMode
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer { defaultWindowingMode }
        val settingsSession =
            ExtendedDisplaySettingsSession(
@@ -138,23 +152,17 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
            )

        settingsSession.use {
            // The external display connected.
            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
                .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
            onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
            tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
            // The external display disconnected.
            whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
                .thenReturn(intArrayOf(DEFAULT_DISPLAY))
            onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
            connectExternalDisplay()
            defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
            disconnectExternalDisplay()

            if (expectTransition) {
                val arg = argumentCaptor<WindowContainerTransaction>()
                verify(transitions, times(2))
                    .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
                assertThat(arg.firstValue.changes[tda.token.asBinder()]?.windowingMode)
                assertThat(arg.firstValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
                    .isEqualTo(WINDOWING_MODE_FREEFORM)
                assertThat(arg.secondValue.changes[tda.token.asBinder()]?.windowingMode)
                assertThat(arg.secondValue.changes[defaultTDA.token.asBinder()]?.windowingMode)
                    .isEqualTo(defaultWindowingMode)
            } else {
                verify(transitions, never()).startTransition(eq(TRANSIT_CHANGE), any(), isNull())
@@ -227,6 +235,58 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
        verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
    }

    @Test
    fun displayWindowingModeSwitch_existingTasksOnConnected() {
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
            WINDOWING_MODE_FULLSCREEN
        }

        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
            connectExternalDisplay()

            val arg = argumentCaptor<WindowContainerTransaction>()
            verify(transitions, times(1))
                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
                .isEqualTo(WINDOWING_MODE_UNDEFINED)
            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
                .isEqualTo(WINDOWING_MODE_FULLSCREEN)
        }
    }

    @Test
    fun displayWindowingModeSwitch_existingTasksOnDisconnected() {
        defaultTDA.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
        whenever(mockWindowManager.getWindowingMode(anyInt())).thenAnswer {
            WINDOWING_MODE_FULLSCREEN
        }

        ExtendedDisplaySettingsSession(context.contentResolver, 1).use {
            disconnectExternalDisplay()

            val arg = argumentCaptor<WindowContainerTransaction>()
            verify(transitions, times(1))
                .startTransition(eq(TRANSIT_CHANGE), arg.capture(), isNull())
            assertThat(arg.firstValue.changes[freeformTask.token.asBinder()]?.windowingMode)
                .isEqualTo(WINDOWING_MODE_FREEFORM)
            assertThat(arg.firstValue.changes[fullscreenTask.token.asBinder()]?.windowingMode)
                .isEqualTo(WINDOWING_MODE_UNDEFINED)
        }
    }

    private fun connectExternalDisplay() {
        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
            .thenReturn(intArrayOf(DEFAULT_DISPLAY, externalDisplayId))
        onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(externalDisplayId)
    }

    private fun disconnectExternalDisplay() {
        whenever(rootTaskDisplayAreaOrganizer.getDisplayIds())
            .thenReturn(intArrayOf(DEFAULT_DISPLAY))
        onDisplaysChangedListenerCaptor.lastValue.onDisplayRemoved(externalDisplayId)
    }

    private class ExtendedDisplaySettingsSession(
        private val contentResolver: ContentResolver,
        private val overrideValue: Int,