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

Commit 61c7d5b4 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Extend close window shortcut to split-screen tasks

This change extends the Action+Ctrl+W keyboard shortcut to close the
focused task in a split-screen pair. This builds upon the existing
functionality that allows closing desktop and fullscreen windows.

The feature is controlled by the
`close_fullscreen_and_splitscreen_keyboard_shortcut` flag.

Bug: 441147192
Test: DesktopModeKeyGestureHandlerTest, DesktopModeWindowDecorViewModelTests
Flag: com.android.window.flags.close_fullscreen_and_splitscreen_keyboard_shortcut
Change-Id: Ibb9758db29714c02fdc785a5b93b2d9f27a56b52
parent f9fe3a00
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -1259,7 +1259,8 @@ public abstract class WMShellModule {
            FocusTransitionObserver focusTransitionObserver,
            @ShellMainThread ShellExecutor mainExecutor,
            DisplayController displayController,
            DesktopState desktopState) {
            DesktopState desktopState,
            Optional<SplitScreenController> splitScreenController) {
        if (desktopState.canEnterDesktopMode()
                && (DesktopExperienceFlags.ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT.isTrue()
                || DesktopModeFlags.ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS.isTrue())) {
@@ -1267,7 +1268,7 @@ public abstract class WMShellModule {
                    desktopModeWindowDecorViewModel, desktopTasksController,
                    desktopUserRepositories,
                    inputManager, shellTaskOrganizer, focusTransitionObserver,
                    mainExecutor, displayController, desktopState));
                    mainExecutor, displayController, desktopState, splitScreenController));
        }
        return Optional.empty();
    }
+17 −1
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ 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_MULTI_WINDOW
import android.content.Context
import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventHandler
@@ -37,6 +38,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.desktopmode.DesktopState
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
import java.util.Optional
@@ -53,6 +55,7 @@ class DesktopModeKeyGestureHandler(
    @ShellMainThread private val mainExecutor: ShellExecutor,
    private val displayController: DisplayController,
    private val desktopState: DesktopState,
    private val splitScreenController: Optional<SplitScreenController>,
) : KeyGestureEventHandler {

    init {
@@ -340,8 +343,21 @@ class DesktopModeKeyGestureHandler(
                    null
                }
            }
            2 -> {
                val task = DesktopTasksController.getSplitFocusedTask(tasks[0], tasks[1])
                if (task.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
                    logV("getGloballyFocusedTaskToClose: Found split screen task: %d", task.taskId)
                    task
                } else {
                    logW(
                        "getGloballyFocusedTaskToClose: Ignored focused pair non-split-screen " +
                            "tasks."
                    )
                    null
                }
            }
            else -> {
                logW("getGloballyFocusedTaskToClose: Ignored focused 2+ tasks.")
                logW("getGloballyFocusedTaskToClose: Ignored focused 3+ tasks.")
                null
            }
        }
+4 −4
Original line number Diff line number Diff line
@@ -526,10 +526,6 @@ class DesktopTasksController(
        }
    }

    /** Returns child task from two focused tasks in split screen mode. */
    private fun getSplitFocusedTask(task1: RunningTaskInfo, task2: RunningTaskInfo) =
        if (task1.taskId == task2.parentTaskId) task2 else task1

    /** Moves a desktop task into fullscreen mode. */
    private fun moveDesktopTaskToFullscreen(
        task: RunningTaskInfo,
@@ -6604,6 +6600,10 @@ class DesktopTasksController(
                    UnminimizeReason.TASKBAR_MANAGE_WINDOW
            }

        /** Returns child task from two focused tasks in split screen mode. */
        fun getSplitFocusedTask(task1: RunningTaskInfo, task2: RunningTaskInfo) =
            if (task1.taskId == task2.parentTaskId) task2 else task1

        @JvmField
        /**
         * A placeholder for a synthetic transition that isn't backed by a true system transition.
+39 −0
Original line number Diff line number Diff line
@@ -52,11 +52,13 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterRe
import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createSplitScreenTask
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.desktopmode.data.DesktopRepository
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.desktopmode.FakeDesktopConfig
import com.android.wm.shell.shared.desktopmode.FakeDesktopState
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
@@ -104,6 +106,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
    private val desktopTasksController = mock<DesktopTasksController>()
    private val desktopState = FakeDesktopState()
    private val shellController = mock<ShellController>()
    private val splitScreenController = mock<SplitScreenController>()

    private lateinit var desktopModeKeyGestureHandler: DesktopModeKeyGestureHandler
    private lateinit var keyGestureEventHandler: KeyGestureEventHandler
@@ -174,6 +177,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
                testExecutor,
                displayController,
                desktopState,
                Optional.of(splitScreenController),
            )
    }

@@ -413,6 +417,34 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
        verify(desktopModeWindowDecorViewModel).closeTask(task)
    }

    @Test
    @EnableFlags(FLAG_CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT)
    fun keyGestureQuitFocusedDesktopTask_shouldQuitSplitScreenTask() {
        // Setup a focused split screen task
        val task = setUpSplitScreenTask()
        val splitRoot = setUpFullscreenTask()
        task.parentTaskId = splitRoot.taskId
        whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(task.displayId)
        whenever(
                desktopTasksController.getFocusedNonDesktopTasks(task.displayId, repository.userId)
            )
            .thenReturn(listOf(splitRoot, task))
        whenever(splitScreenController.isTaskInSplitScreen(task.taskId)).thenReturn(true)

        // Create and handle the key gesture event
        val event =
            KeyGestureEvent.Builder()
                .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_QUIT_FOCUSED_DESKTOP_TASK)
                .setKeycodes(intArrayOf(KeyEvent.KEYCODE_Q))
                .setModifierState(KeyEvent.META_META_ON)
                .build()
        keyGestureEventHandler.handleKeyGestureEvent(event, null)
        testExecutor.flushAll()

        // Verify closeTask is called
        verify(desktopModeWindowDecorViewModel).closeTask(task)
    }

    @Test
    fun keyGestureSwitchToPreviousDesk_activatesDesk() {
        val displayId = 2
@@ -492,6 +524,13 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
        return task
    }

    private fun setUpSplitScreenTask(): RunningTaskInfo {
        val task = createSplitScreenTask()
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
        runningTasks.add(task)
        return task
    }

    private companion object {
        private const val DEFAULT_USER_ID = 0
        const val SECOND_DISPLAY = 2
+26 −0
Original line number Diff line number Diff line
@@ -78,6 +78,8 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.util.StubTransaction
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DefaultWindowDecorationActions
@@ -375,6 +377,30 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
        assertThat(hierarchyOp.container).isEqualTo(decor.taskInfo.token.asBinder())
    }

    @Test
    fun testCloseTask_splitScreen_movesOtherToFullscreen() {
        desktopModeWindowDecorViewModel.setFreeformTaskTransitionStarter(
            mockFreeformTaskTransitionStarter
        )
        val decor = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_MULTI_WINDOW)
        val otherTask = createTask(windowingMode = WINDOWING_MODE_MULTI_WINDOW)

        whenever(mockSplitScreenController.isTaskInSplitScreen(decor.taskInfo.taskId))
            .thenReturn(true)
        whenever(mockSplitScreenController.getSplitPosition(decor.taskInfo.taskId))
            .thenReturn(SPLIT_POSITION_TOP_OR_LEFT)
        whenever(mockSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT))
            .thenReturn(otherTask)

        desktopModeWindowDecorViewModel.closeTask(decor.taskInfo)

        verify(mockSplitScreenController)
            .moveTaskToFullscreen(
                eq(otherTask.taskId),
                eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE),
            )
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MINIMIZE_BUTTON)
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS)