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

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

Merge "Extend close window shortcut to fullscreen tasks" into main

parents 2a14ce04 f9fe3a00
Loading
Loading
Loading
Loading
+63 −1
Original line number Diff line number Diff line
@@ -177,7 +177,27 @@ class DesktopModeKeyGestureHandler(
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_QUIT_FOCUSED_DESKTOP_TASK -> {
                logV("Key gesture KEY_GESTURE_TYPE_QUIT_FOCUSED_DESKTOP_TASK is handled")
                quitFocusedDesktopTask()
                val focusedTask =
                    if (
                        DesktopExperienceFlags.CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT
                            .isTrue
                    ) {
                        getGloballyFocusedTaskToClose()
                    } else {
                        getGloballyFocusedDesktopTask().also { task ->
                            if (task != null) {
                                logV("Found focused desktop task %d to close", task.taskId)
                            } else {
                                logV(
                                    "Globally focused desktop task is not found to close. focusedDisplay=%d",
                                    focusTransitionObserver.globallyFocusedDisplayId,
                                )
                            }
                        }
                    } ?: return
                mainExecutor.execute {
                    desktopModeWindowDecorViewModel.get().closeTask(focusedTask)
                }
            }
            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_FULLSCREEN -> {
                logV("Key gesture TOGGLE_FULLSCREEN is handled")
@@ -285,6 +305,48 @@ class DesktopModeKeyGestureHandler(
        return null
    }

    private fun getGloballyFocusedTaskToClose(): RunningTaskInfo? {
        getGloballyFocusedDesktopTask()?.let { desktopTask ->
            logV("getGloballyFocusedTaskToClose: Found desktop task: %d", desktopTask.taskId)
            return@getGloballyFocusedTaskToClose desktopTask
        }
        val tasks =
            desktopTasksController
                .get()
                .getFocusedNonDesktopTasks(
                    displayId = focusTransitionObserver.globallyFocusedDisplayId,
                    userId = desktopUserRepositories.current.userId,
                )
        return when (tasks.size) {
            0 -> {
                logW(
                    "getGloballyFocusedTaskToClose: Task not found to close: " +
                        "globallyFocusedTaskId=%d globallyFocusedDisplayId=%d",
                    focusTransitionObserver.globallyFocusedTaskId,
                    focusTransitionObserver.globallyFocusedDisplayId,
                )
                null
            }
            1 -> {
                val task = tasks.single()
                if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) {
                    logV("getGloballyFocusedTaskToClose: Found fullscreen task: %d", task.taskId)
                    task
                } else {
                    logW(
                        "getGloballyFocusedTaskToClose: Ignored focused single non-fullscreen " +
                            "task."
                    )
                    null
                }
            }
            else -> {
                logW("getGloballyFocusedTaskToClose: Ignored focused 2+ tasks.")
                null
            }
        }
    }

    private fun logV(msg: String, vararg arguments: Any?) {
        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }
+39 −24
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ import android.view.InsetsState;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
@@ -1224,13 +1225,26 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,

    private void onCloseTask(int taskId) {
        if (isTaskInSplitScreen(taskId)) {
            ProtoLog.i(WM_SHELL_WINDOW_DECORATION,
                    "%s: onCloseTask(taskId=%d): closing split screen", TAG, taskId);
            mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(taskId).taskId,
                    SplitScreenController.EXIT_REASON_DESKTOP_MODE);
        } else {
            return;
        }
        final WindowDecorationWrapper decoration = mWindowDecorByTaskId.get(taskId);
        if (decoration == null) {
            ProtoLog.e(WM_SHELL_WINDOW_DECORATION,
                        "%s: handled close key gesture but decoration is null, ignoring", TAG);
                    "%s: onCloseTask(taskId=%d): decoration is null, ignoring", TAG, taskId);
            return;
        }
        if (DesktopExperienceFlags
                .CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT.isTrue()
                && decoration.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
            ProtoLog.i(WM_SHELL_WINDOW_DECORATION,
                    "%s: onCloseTask(taskId=%d): closing fullscreen task", TAG, taskId);
            final WindowContainerTransaction wct = new WindowContainerTransaction();
            wct.removeTask(decoration.getTaskInfo().token);
            mTransitions.startTransition(WindowManager.TRANSIT_CLOSE, wct, null);
            return;
        }
        if (DesktopExperienceFlags
@@ -1243,6 +1257,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                nextFocusedWindow.a11yAnnounceNewFocusedWindow();
            }
        }
        ProtoLog.w(WM_SHELL_WINDOW_DECORATION,
                "%s: onCloseTask(taskId=%d): closing desktop task", TAG, taskId);
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final Function1<IBinder, Unit> runOnTransitionStart =
                mDesktopTasksController.onDesktopWindowClose(wct,
@@ -1253,7 +1269,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
            runOnTransitionStart.invoke(transition);
        }
    }
    }

    /** Listener for caption touch events. */
    public interface CaptionTouchStatusListener {
+26 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer
import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags.FLAG_CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_ENABLE_DISPLAY_FOCUS_IN_SHELL_TRANSITIONS
import com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT
@@ -387,6 +388,31 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
        verify(desktopModeWindowDecorViewModel).closeTask(task)
    }

    @Test
    @EnableFlags(FLAG_CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT)
    fun keyGestureQuitFocusedDesktopTask_shouldQuitFullscreenTask() {
        // Setup a focused fullscreen task
        val task = setUpFullscreenTask()
        whenever(focusTransitionObserver.globallyFocusedDisplayId).thenReturn(task.displayId)
        whenever(
                desktopTasksController.getFocusedNonDesktopTasks(task.displayId, repository.userId)
            )
            .thenReturn(listOf(task))

        // 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
+26 −0
Original line number Diff line number Diff line
@@ -57,11 +57,13 @@ import android.view.SurfaceView
import android.view.View
import android.view.ViewRootImpl
import android.view.WindowInsets.Type.statusBars
import android.view.WindowManager
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.window.flags.Flags
import com.android.window.flags.Flags.FLAG_CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.DesktopImmersiveController
@@ -349,6 +351,30 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
        verify(mockFreeformTaskTransitionStarter, never()).startRemoveTransition(any())
    }

    @Test
    @EnableFlags(FLAG_CLOSE_FULLSCREEN_AND_SPLITSCREEN_KEYBOARD_SHORTCUT)
    fun testCloseTask_fullscreen_closesTask() {
        desktopModeWindowDecorViewModel.setFreeformTaskTransitionStarter(
            mockFreeformTaskTransitionStarter
        )
        val decor = createOpenTaskDecoration(windowingMode = WINDOWING_MODE_FULLSCREEN)

        desktopModeWindowDecorViewModel.closeTask(decor.taskInfo)

        val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
        verify(mockTransitions)
            .startTransition(
                eq(WindowManager.TRANSIT_CLOSE),
                transactionCaptor.capture(),
                anyOrNull(),
            )
        val wct = transactionCaptor.firstValue
        assertThat(wct.hierarchyOps).hasSize(1)
        val hierarchyOp = wct.hierarchyOps[0]
        assertThat(hierarchyOp.type).isEqualTo(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK)
        assertThat(hierarchyOp.container).isEqualTo(decor.taskInfo.token.asBinder())
    }

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