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

Commit fa583524 authored by Jorge Gil's avatar Jorge Gil
Browse files

Only bring desktop app to front when necessary

If the active desktops apps are already in the front, then there's
no need to add WCT#reorder operations for every desktop app.
Instead, check first whether there is an invisble app, and if there
is, reorder all apps so that the Z order is maintained.

Bug: 255344915
Bug: 258056163
Test: atest DesktopModeControllerTest
Test: manual - in desktop mode,
 1. Launch Gmail
 2. Launch Photos
 3. Observe there is no reorder triggered anymore
Change-Id: Ib2cc8d900d1d0ccc8b9e2b49b95f1708531aa398
parent 789c3395
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -259,21 +259,36 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll

    @NonNull
    private WindowContainerTransaction bringDesktopAppsToFront() {
        ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        final ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
        ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
        ArrayList<RunningTaskInfo> taskInfos = new ArrayList<>();

        final List<RunningTaskInfo> taskInfos = new ArrayList<>();
        for (Integer taskId : activeTasks) {
            RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId);
            if (taskInfo != null) {
                taskInfos.add(taskInfo);
            }
        }

        if (taskInfos.isEmpty()) {
            return wct;
        }

        final boolean allActiveTasksAreVisible = taskInfos.stream()
                .allMatch(info -> mDesktopModeTaskRepository.isVisibleTask(info.taskId));
        if (allActiveTasksAreVisible) {
            ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                    "bringDesktopAppsToFront: active tasks are already in front, skipping.");
            return wct;
        }
        ProtoLog.d(WM_SHELL_DESKTOP_MODE,
                "bringDesktopAppsToFront: reordering all active tasks to the front");
        final List<Integer> allTasksInZOrder =
                mDesktopModeTaskRepository.getFreeformTasksInZOrder();
        // Sort by z-order, bottom to top, so that the top-most task is reordered to the top last
        // in the WCT.
        taskInfos.sort(Comparator.comparingInt(task -> -allTasksInZOrder.indexOf(task.taskId)));
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        for (RunningTaskInfo task : taskInfos) {
            wct.reorder(task.token, true);
        }
+7 −0
Original line number Diff line number Diff line
@@ -102,6 +102,13 @@ class DesktopModeTaskRepository {
        return activeTasks.contains(taskId)
    }

    /**
     * Whether a task is visible.
     */
    fun isVisibleTask(taskId: Int): Boolean {
        return visibleTasks.contains(taskId)
    }

    /**
     * Get a set of the active tasks
     */
+68 −13
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction.Change;
import android.window.WindowContainerTransaction.HierarchyOp;

import androidx.test.filters.SmallTest;

@@ -222,25 +223,29 @@ public class DesktopModeControllerTest extends ShellTestCase {
        // Check that there are hierarchy changes for home task and visible task
        assertThat(wct.getHierarchyOps()).hasSize(2);
        // First show home task
        WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
        HierarchyOp op1 = wct.getHierarchyOps().get(0);
        assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op1.getContainer()).isEqualTo(homeTask.token.asBinder());

        // Then visible task on top of it
        WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1);
        HierarchyOp op2 = wct.getHierarchyOps().get(1);
        assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op2.getContainer()).isEqualTo(fullscreenTask1.token.asBinder());
    }

    @Test
    public void testShowDesktopApps() {
    public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
        // Set up two active tasks on desktop, task2 is on top of task1.
        RunningTaskInfo freeformTask1 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(freeformTask1.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask1.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(
                freeformTask1.taskId, false /* visible */);
        RunningTaskInfo freeformTask2 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(
                freeformTask2.taskId, false /* visible */);
        when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
                freeformTask1);
        when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
@@ -248,29 +253,68 @@ public class DesktopModeControllerTest extends ShellTestCase {

        // Run show desktop apps logic
        mController.showDesktopApps();
        ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
                WindowContainerTransaction.class);
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), wctCaptor.capture(), any());
        } else {
            verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
        }
        WindowContainerTransaction wct = wctCaptor.getValue();

        final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
        // Check wct has reorder calls
        assertThat(wct.getHierarchyOps()).hasSize(2);

        // Task 1 appeared first, must be first reorder to top.
        WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
        HierarchyOp op1 = wct.getHierarchyOps().get(0);
        assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder());

        // Task 2 appeared last, must be last reorder to top.
        WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1);
        HierarchyOp op2 = wct.getHierarchyOps().get(1);
        assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op2.getContainer()).isEqualTo(freeformTask2.token.asBinder());
    }

    @Test
    public void testShowDesktopApps_appsAlreadyVisible_doesNothing() {
        final RunningTaskInfo task1 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(task1.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, true /* visible */);
        when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
        final RunningTaskInfo task2 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(task2.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
        when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);

        mController.showDesktopApps();

        final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
        // No reordering needed.
        assertThat(wct.getHierarchyOps()).isEmpty();
    }

    @Test
    public void testShowDesktopApps_someAppsInvisible_reordersAll() {
        final RunningTaskInfo task1 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(task1.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task1.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(task1.taskId, false /* visible */);
        when(mShellTaskOrganizer.getRunningTaskInfo(task1.taskId)).thenReturn(task1);
        final RunningTaskInfo task2 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(task2.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(task2.taskId);
        mDesktopModeTaskRepository.updateVisibleFreeformTasks(task2.taskId, true /* visible */);
        when(mShellTaskOrganizer.getRunningTaskInfo(task2.taskId)).thenReturn(task2);

        mController.showDesktopApps();

        final WindowContainerTransaction wct = getBringAppsToFrontTransaction();
        // Both tasks should be reordered to top, even if one was already visible.
        assertThat(wct.getHierarchyOps()).hasSize(2);
        final HierarchyOp op1 = wct.getHierarchyOps().get(0);
        assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op1.getContainer()).isEqualTo(task1.token.asBinder());
        final HierarchyOp op2 = wct.getHierarchyOps().get(1);
        assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
    }

    @Test
    public void testHandleTransitionRequest_desktopModeNotActive_returnsNull() {
        when(DesktopModeStatus.isActive(any())).thenReturn(false);
@@ -355,6 +399,17 @@ public class DesktopModeControllerTest extends ShellTestCase {
        return arg.getValue();
    }

    private WindowContainerTransaction getBringAppsToFrontTransaction() {
        final ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
                WindowContainerTransaction.class);
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            verify(mTransitions).startTransition(eq(TRANSIT_TO_FRONT), arg.capture(), any());
        } else {
            verify(mShellTaskOrganizer).applyTransaction(arg.capture());
        }
        return arg.getValue();
    }

    private void assertThatBoundsCleared(Change change) {
        assertThat((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) != 0).isTrue();
        assertThat(change.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();