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

Commit 789c3395 authored by Jorge Gil's avatar Jorge Gil
Browse files

Sort tasks by Z-order instead of lastActiveTime

lastActiveTime is not a reliable way to determine the Z order of
freeform tasks because it misses Z order changes of already
visible tasks (e.g. when bringing a semi occluded task to the top).
This change keeps track of the freeform task's Z-order by observing
TaskListener's appear/vanish and focus changes.

Bug: 258056163
Test: atest DesktopModeControllerTest DesktopModeTaskRepositoryTest
Test: manual:
 1. Clear all open tasks and enable desktop mode
 2. Launch YT from taskbar
 3. Launch Gmail from taskbar
 4. Tap on YT to bring to front
 5. Launch Photos from taskbar
Top->bottom order is Photos, YT, Gmail

Change-Id: I6f2751b32f43d7b8ac2f97ce4c06be7ffa4dec11
parent 82e7167b
Loading
Loading
Loading
Loading
+7 −3
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;

/**
@@ -267,9 +268,12 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll
                taskInfos.add(taskInfo);
            }
        }
        // Order by lastActiveTime, descending
        taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime));
        WindowContainerTransaction wct = new WindowContainerTransaction();
        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);
        }
+26 −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>()
@@ -107,6 +109,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 +135,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));
+8 −8
Original line number Diff line number Diff line
@@ -234,13 +234,13 @@ public class DesktopModeControllerTest extends ShellTestCase {

    @Test
    public void testShowDesktopApps() {
        // Set up two active tasks on desktop
        // 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);
        RunningTaskInfo freeformTask2 = createFreeformTask();
        mDesktopModeTaskRepository.addActiveTask(freeformTask2.taskId);
        mDesktopModeTaskRepository.addOrMoveFreeformTaskToTop(freeformTask2.taskId);
        when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask1.taskId)).thenReturn(
                freeformTask1);
        when(mShellTaskOrganizer.getRunningTaskInfo(freeformTask2.taskId)).thenReturn(
@@ -260,15 +260,15 @@ public class DesktopModeControllerTest extends ShellTestCase {
        // Check wct has reorder calls
        assertThat(wct.getHierarchyOps()).hasSize(2);

        // Task 2 has activity later, must be first
        // Task 1 appeared first, must be first reorder to top.
        WindowContainerTransaction.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 1 should be second
        // Task 2 appeared last, must be last reorder to top.
        WindowContainerTransaction.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(freeformTask2.token.asBinder());
    }

    @Test
+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() {