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

Commit 7874a02c authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Move fullscreen tasks with move-to-next-display shortcut

This CL enables the move-to-next-display shortcut to work with
fullscreen tasks, in addition to desktop (freeform) tasks. This commit
specifically focuses on changing DesktopModeKeyGestureHandler so that it
can pick fullscreen task for devices in projected mode.

When the user activates the shortcut, the system will now check for a
focused fullscreen task if no desktop task is focused. This allows
moving a fullscreen app from a projected display to another display.

This behavior is gated by the
`move_to_next_display_shortcut_with_projected_mode` flag.

Bug: 424748132
Test: DesktopModeKeyGestureHandlerTest
Flag: com.android.window.flags.move_to_next_display_shortcut_with_projected_mode
Change-Id: I232e7e28aefb51301f057a78f4d39c14120e53f7
parent 20780af5
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -258,6 +258,9 @@ public enum DesktopExperienceFlags {
    LIMIT_SYSTEM_FULLSCREEN_OVERRIDE_TO_DEFAULT_DISPLAY(
            Flags::limitSystemFullscreenOverrideToDefaultDisplay, false,
            Flags.FLAG_LIMIT_SYSTEM_FULLSCREEN_OVERRIDE_TO_DEFAULT_DISPLAY),
    MOVE_TO_NEXT_DISPLAY_SHORTCUT_WITH_PROJECTED_MODE(
            Flags::moveToNextDisplayShortcutWithProjectedMode, false,
            Flags.FLAG_MOVE_TO_NEXT_DISPLAY_SHORTCUT_WITH_PROJECTED_MODE),
    PRESERVE_RECENTS_TASK_CONFIGURATION_ON_RELAUNCH(
            Flags::preserveRecentsTaskConfigurationOnRelaunch, true,
            Flags.FLAG_PRESERVE_RECENTS_TASK_CONFIGURATION_ON_RELAUNCH),
+3 −1
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ class FakeDesktopState : DesktopState {

    /** Override [canEnterDesktopMode] for a specific display. */
    val overrideDesktopModeSupportPerDisplay = mutableMapOf<Int, Boolean>()
    /** Overrides [isProjectedMode] */
    var isProjected: Boolean = false

    override fun isMultipleDesktopFrontendEnabledOnDisplay(display: Display): Boolean =
        enableMultipleDesktops && isDesktopModeSupportedOnDisplay(display)
@@ -63,7 +65,7 @@ class FakeDesktopState : DesktopState {
    }

    override fun isProjectedMode(): Boolean {
        return false
        return isProjected
    }

    override var overridesShowAppHandle: Boolean = false
+1 −1
Original line number Diff line number Diff line
@@ -1184,7 +1184,7 @@ public abstract class WMShellModule {
                    desktopModeWindowDecorViewModel, desktopTasksController,
                    desktopUserRepositories,
                    inputManager, shellTaskOrganizer, focusTransitionObserver,
                    mainExecutor, displayController));
                    mainExecutor, displayController, desktopState));
        }
        return Optional.empty();
    }
+58 −8
Original line number Diff line number Diff line
@@ -18,11 +18,13 @@ 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.content.Context
import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventHandler
import android.hardware.input.KeyGestureEvent
import android.os.IBinder
import android.view.Display.DEFAULT_DISPLAY
import android.window.DesktopExperienceFlags
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -32,6 +34,7 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.Minimiz
import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
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.DesktopState
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
import java.util.Optional
@@ -47,6 +50,7 @@ class DesktopModeKeyGestureHandler(
    private val focusTransitionObserver: FocusTransitionObserver,
    @ShellMainThread private val mainExecutor: ShellExecutor,
    private val displayController: DisplayController,
    private val desktopState: DesktopState,
) : KeyGestureEventHandler {

    init {
@@ -69,18 +73,11 @@ class DesktopModeKeyGestureHandler(
        when (event.keyGestureType) {
            KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
                logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
                getGloballyFocusedDesktopTask()?.let {
                    logV("Found globally focused desktop task to move: ${it.taskId}")
                getGloballyFocusedTaskToMoveToNextDisplay()?.let {
                    mainExecutor.execute {
                        desktopTasksController.get().moveToNextDesktopDisplay(it.taskId)
                    }
                }
                    ?: logW(
                        "No globally focused desktop task to move: " +
                            "globallyFocusedTaskId=%d globallyFocusedDisplayId=%d",
                        focusTransitionObserver.globallyFocusedTaskId,
                        focusTransitionObserver.globallyFocusedDisplayId,
                    )
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_SWITCH_TO_PREVIOUS_DESK -> {
                logV("Key gesture SWITCH_TO_PREVIOUS_DESK is handled")
@@ -177,6 +174,59 @@ class DesktopModeKeyGestureHandler(
        }
    }

    private fun getGloballyFocusedTaskToMoveToNextDisplay(): RunningTaskInfo? {
        getGloballyFocusedDesktopTask()?.let { desktopTask ->
            logV(
                "getGloballyFocusedTaskToMoveToNextDisplay: Found globally focused desktop task to move: %d",
                desktopTask.taskId,
            )
            return@getGloballyFocusedTaskToMoveToNextDisplay desktopTask
        }

        if (!DesktopExperienceFlags.MOVE_TO_NEXT_DISPLAY_SHORTCUT_WITH_PROJECTED_MODE.isTrue) {
            logV(
                "getGloballyFocusedTaskToMoveToNextDisplay: Skip focusing fullscreen task because " +
                    "MOVE_TO_NEXT_DISPLAY_SHORTCUT_WITH_PROJECTED_MODE is disabled"
            )
            return null
        }

        if (!desktopState.isProjectedMode()) {
            logV(
                "getGloballyFocusedTaskToMoveToNextDisplay: Skip focusing fullscreen task because the device is not " +
                    "in the projected mode"
            )
            return null
        }

        if (focusTransitionObserver.globallyFocusedDisplayId != DEFAULT_DISPLAY) {
            logV(
                "getGloballyFocusedTaskToMoveToNextDisplay: Skip focusing fullscreen task because the focused " +
                    "display is not default display"
            )
            return null
        }

        desktopTasksController
            .get()
            .getFocusedNonDesktopTasks(focusTransitionObserver.globallyFocusedDisplayId)
            .find { it.windowingMode == WINDOWING_MODE_FULLSCREEN }
            ?.let { fullscreenTask ->
                logV(
                    "getGloballyFocusedTaskToMoveToNextDisplay: Found globally focused fullscreen task to move: %d",
                    fullscreenTask.taskId,
                )
                return@getGloballyFocusedTaskToMoveToNextDisplay fullscreenTask
            }

        logW(
            "No globally focused task to move: globallyFocusedTaskId=%d globallyFocusedDisplayId=%d",
            focusTransitionObserver.globallyFocusedTaskId,
            focusTransitionObserver.globallyFocusedDisplayId,
        )
        return null
    }

    private fun logV(msg: String, vararg arguments: Any?) {
        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }
+1 −1
Original line number Diff line number Diff line
@@ -459,7 +459,7 @@ class DesktopTasksController(
     * Returns all focused tasks in full screen or split screen mode in [displayId] excluding home
     * activity and desk roots.
     */
    private fun getFocusedNonDesktopTasks(displayId: Int): List<RunningTaskInfo> =
    fun getFocusedNonDesktopTasks(displayId: Int): List<RunningTaskInfo> =
        shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo ->
            val focused = taskInfo.isFocused
            val isNotDesktop =
Loading