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

Commit 456e2fe9 authored by Jorge Gil's avatar Jorge Gil Committed by Android (Google) Code Review
Browse files

Merge changes Ib2cc8d90,I6f2751b3 into tm-qpr-dev

* changes:
  Only bring desktop app to front when necessary
  Sort tasks by Z-order instead of lastActiveTime
parents b250c79e fa583524
Loading
Loading
Loading
Loading
+24 −5
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.wm.shell.transition.Transitions;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executor;

/**
@@ -258,18 +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);
            }
        }
        // Order by lastActiveTime, descending
        taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime));
        WindowContainerTransaction wct = new WindowContainerTransaction();

        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)));
        for (RunningTaskInfo task : taskInfos) {
            wct.reorder(task.token, true);
        }
+33 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ class DesktopModeTaskRepository {
     */
    private val activeTasks = ArraySet<Int>()
    private val visibleTasks = ArraySet<Int>()
    // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
    private val freeformTasksInZOrder = mutableListOf<Int>()
    private val activeTasksListeners = ArraySet<ActiveTasksListener>()
    // Track visible tasks separately because a task may be part of the desktop but not visible.
    private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
@@ -100,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
     */
@@ -107,6 +116,13 @@ class DesktopModeTaskRepository {
        return ArraySet(activeTasks)
    }

    /**
     * Get a list of freeform tasks, ordered from top-bottom (top at index 0).
     */
    fun getFreeformTasksInZOrder(): List<Int> {
        return freeformTasksInZOrder
    }

    /**
     * Updates whether a freeform task with this id is visible or not and notifies listeners.
     */
@@ -126,6 +142,23 @@ class DesktopModeTaskRepository {
        }
    }

    /**
     * Add (or move if it already exists) the task to the top of the ordered list.
     */
    fun addOrMoveFreeformTaskToTop(taskId: Int) {
        if (freeformTasksInZOrder.contains(taskId)) {
            freeformTasksInZOrder.remove(taskId)
        }
        freeformTasksInZOrder.add(0, taskId)
    }

    /**
     * Remove the task from the ordered list.
     */
    fun removeFreeformTask(taskId: Int) {
        freeformTasksInZOrder.remove(taskId)
    }

    /**
     * Defines interface for classes that can listen to changes for active tasks in desktop mode.
     */
+26 −6
Original line number Diff line number Diff line
@@ -38,7 +38,8 @@ import java.util.Optional;
 * {@link ShellTaskOrganizer.TaskListener} for {@link
 * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
 */
public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
        ShellTaskOrganizer.FocusListener {
    private static final String TAG = "FreeformTaskListener";

    private final ShellTaskOrganizer mShellTaskOrganizer;
@@ -67,6 +68,9 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {

    private void onInit() {
        mShellTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_FREEFORM);
        if (DesktopModeStatus.IS_SUPPORTED) {
            mShellTaskOrganizer.addFocusListener(this);
        }
    }

    @Override
@@ -86,13 +90,16 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
            t.apply();
        }

        if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) {
        if (DesktopModeStatus.IS_SUPPORTED) {
            mDesktopModeTaskRepository.ifPresent(repository -> {
                repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
                if (taskInfo.isVisible) {
                    if (repository.addActiveTask(taskInfo.taskId)) {
                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                                "Adding active freeform task: #%d", taskInfo.taskId);
                    }
                    repository.updateVisibleFreeformTasks(taskInfo.taskId, true);
                }
            });
        }
    }
@@ -105,6 +112,7 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {

        if (DesktopModeStatus.IS_SUPPORTED) {
            mDesktopModeTaskRepository.ifPresent(repository -> {
                repository.removeFreeformTask(taskInfo.taskId);
                if (repository.removeActiveTask(taskInfo.taskId)) {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                            "Removing active freeform task: #%d", taskInfo.taskId);
@@ -139,6 +147,18 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
        }
    }

    @Override
    public void onFocusTaskChanged(RunningTaskInfo taskInfo) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG,
                "Freeform Task Focus Changed: #%d focused=%b",
                taskInfo.taskId, taskInfo.isFocused);
        if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isFocused) {
            mDesktopModeTaskRepository.ifPresent(repository -> {
                repository.addOrMoveFreeformTaskToTop(taskInfo.taskId);
            });
        }
    }

    @Override
    public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
        b.setParent(findTaskSurface(taskId));
+76 −21
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() {
        // Set up two active tasks on desktop
    public void testShowDesktopApps_allAppsInvisible_bringsToFront() {
        // Set up two active tasks on desktop, task2 is on top of task1.
        RunningTaskInfo freeformTask1 = createFreeformTask();
        freeformTask1.lastActiveTime = 100;
        RunningTaskInfo freeformTask2 = createFreeformTask();
        freeformTask2.lastActiveTime = 200;
        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,27 +253,66 @@ 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 2 has activity later, must be first
        WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
        // Task 1 appeared first, must be first reorder to top.
        HierarchyOp op1 = wct.getHierarchyOps().get(0);
        assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
        assertThat(op1.getContainer()).isEqualTo(freeformTask2.token.asBinder());
        assertThat(op1.getContainer()).isEqualTo(freeformTask1.token.asBinder());

        // Task 2 appeared last, must be last reorder to top.
        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);

        // Task 1 should be second
        WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(1);
        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(freeformTask1.token.asBinder());
        assertThat(op2.getContainer()).isEqualTo(task2.token.asBinder());
    }

    @Test
@@ -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();
+26 −0
Original line number Diff line number Diff line
@@ -140,6 +140,32 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
        assertThat(listener.visibleFreeformTaskChangedCalls).isEqualTo(3)
    }

    @Test
    fun addOrMoveFreeformTaskToTop_didNotExist_addsToTop() {
        repo.addOrMoveFreeformTaskToTop(5)
        repo.addOrMoveFreeformTaskToTop(6)
        repo.addOrMoveFreeformTaskToTop(7)

        val tasks = repo.getFreeformTasksInZOrder()
        assertThat(tasks.size).isEqualTo(3)
        assertThat(tasks[0]).isEqualTo(7)
        assertThat(tasks[1]).isEqualTo(6)
        assertThat(tasks[2]).isEqualTo(5)
    }

    @Test
    fun addOrMoveFreeformTaskToTop_alreadyExists_movesToTop() {
        repo.addOrMoveFreeformTaskToTop(5)
        repo.addOrMoveFreeformTaskToTop(6)
        repo.addOrMoveFreeformTaskToTop(7)

        repo.addOrMoveFreeformTaskToTop(6)

        val tasks = repo.getFreeformTasksInZOrder()
        assertThat(tasks.size).isEqualTo(3)
        assertThat(tasks.first()).isEqualTo(6)
    }

    class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
        var activeTaskChangedCalls = 0
        override fun onActiveTasksChanged() {