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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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 -> {
|
||||
if (repository.addActiveTask(taskInfo.taskId)) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
|
||||
"Adding active freeform task: #%d", taskInfo.taskId);
|
||||
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);
|
||||
}
|
||||
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));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user