summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Li <lihongyu@google.com> 2024-12-23 21:18:20 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-23 21:18:20 -0800
commitb012242117fc852069753b404c59dcfd32133694 (patch)
treef22d0f04594f2d456296767fb9ab00f788d32287
parent33b821c22d95b1007e40a668894ecf4480de1c99 (diff)
parent6d95ffe00fc0b208e0aa1bc8e4b22880700459d2 (diff)
Merge "Allow multiple adjacent TFs (5/n)" into main
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java18
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java64
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java151
3 files changed, 146 insertions, 87 deletions
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index cb6b69072e14..7b7b3eb17ddd 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -3523,10 +3523,18 @@ class TaskFragment extends WindowContainer<WindowContainer> {
throw new IllegalStateException("allowMultipleAdjacentTaskFragments must be"
+ " enabled to set more than two TaskFragments adjacent to each other.");
}
- if (taskFragments.size() < 2) {
+ final int size = taskFragments.size();
+ if (size < 2) {
throw new IllegalArgumentException("Adjacent TaskFragments must contain at least"
- + " two TaskFragments, but only " + taskFragments.size()
- + " were provided.");
+ + " two TaskFragments, but only " + size + " were provided.");
+ }
+ if (size > 2) {
+ for (int i = 0; i < size; i++) {
+ if (taskFragments.valueAt(i).asTask() == null) {
+ throw new IllegalArgumentException(
+ "Not yet support 3+ adjacent for non-Task TFs");
+ }
+ }
}
mAdjacentSet = taskFragments;
}
@@ -3604,6 +3612,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
+ int size() {
+ return mAdjacentSet.size();
+ }
+
@Override
public boolean equals(@Nullable Object o) {
if (this == o) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 793f18992109..965b22473a2c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -9150,7 +9150,7 @@ public class WindowManagerService extends IWindowManager.Stub
// handling the touch-outside event to prevent focus rapid changes back-n-forth.
final boolean shouldDelayTouchForEmbeddedActivity = activity != null
&& activity.isEmbedded()
- && activity.getTaskFragment().getAdjacentTaskFragment() != null;
+ && activity.getTaskFragment().hasAdjacentTaskFragment();
// For cases when there are multiple freeform windows where non-top windows are blocking
// the gesture zones, delay handling the touch-outside event to prevent refocusing the
@@ -9529,21 +9529,41 @@ public class WindowManagerService extends IWindowManager.Stub
return focusedActivity;
}
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- final ActivityRecord adjacentTopActivity =
- adjacentTaskFragment != null ? adjacentTaskFragment.topRunningActivity() : null;
- if (adjacentTopActivity == null) {
- // Return if no adjacent activity.
+ if (!taskFragment.hasAdjacentTaskFragment()) {
return focusedActivity;
}
- if (adjacentTopActivity.getLastWindowCreateTime()
- < focusedActivity.getLastWindowCreateTime()) {
- // Return if the current focus activity has more recently active window.
- return focusedActivity;
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
+ final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
+ if (adjacentTopActivity == null) {
+ // Return if no adjacent activity.
+ return focusedActivity;
+ }
+
+ if (adjacentTopActivity.getLastWindowCreateTime()
+ < focusedActivity.getLastWindowCreateTime()) {
+ // Return if the current focus activity has more recently active window.
+ return focusedActivity;
+ }
+
+ return adjacentTopActivity;
}
- return adjacentTopActivity;
+ // Find the adjacent activity with more recently active window.
+ final ActivityRecord[] mostRecentActiveActivity = { focusedActivity };
+ final long[] mostRecentActiveTime = { focusedActivity.getLastWindowCreateTime() };
+ taskFragment.forOtherAdjacentTaskFragments(adjacentTaskFragment -> {
+ final ActivityRecord adjacentTopActivity = adjacentTaskFragment.topRunningActivity();
+ if (adjacentTopActivity != null) {
+ final long lastWindowCreateTime = adjacentTopActivity.getLastWindowCreateTime();
+ if (lastWindowCreateTime > mostRecentActiveTime[0]) {
+ mostRecentActiveTime[0] = lastWindowCreateTime;
+ mostRecentActiveActivity[0] = adjacentTopActivity;
+ }
+ }
+ });
+ return mostRecentActiveActivity[0];
}
@NonNull
@@ -9592,14 +9612,28 @@ public class WindowManagerService extends IWindowManager.Stub
return false;
}
final TaskFragment fromFragment = fromWin.getTaskFragment();
- if (fromFragment == null) {
+ if (fromFragment == null || fromFragment.asTask() != null) {
+ // Don't move the focus to another task.
return false;
}
- final TaskFragment adjacentFragment = fromFragment.getAdjacentTaskFragment();
- if (adjacentFragment == null || adjacentFragment.asTask() != null) {
- // Don't move the focus to another task.
+ if (!fromFragment.hasAdjacentTaskFragment()) {
+ // No adjacent window.
return false;
}
+ final TaskFragment adjacentFragment;
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ if (fromFragment.getAdjacentTaskFragments().size() > 2) {
+ throw new IllegalStateException("Not yet support 3+ adjacent for non-Task TFs");
+ }
+ final TaskFragment[] tmpAdjacent = new TaskFragment[1];
+ fromFragment.forOtherAdjacentTaskFragments(adjacentTF -> {
+ tmpAdjacent[0] = adjacentTF;
+ return true;
+ });
+ adjacentFragment = tmpAdjacent[0];
+ } else {
+ adjacentFragment = fromFragment.getAdjacentTaskFragment();
+ }
if (adjacentFragment.isIsolatedNav()) {
// Don't move the focus if the adjacent TF is isolated navigation.
return false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 35a2546fca1a..c0f251e06d17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -52,6 +52,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -1070,94 +1071,106 @@ public class TaskFragmentTest extends WindowTestsBase {
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
- public void testSetAdjacentTaskFragments() {
+ public void testAdjacentSetForTaskFragments() {
final Task task = createTask(mDisplayContent);
final TaskFragment tf0 = createTaskFragmentWithActivity(task);
final TaskFragment tf1 = createTaskFragmentWithActivity(task);
final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- assertFalse(tf0.hasAdjacentTaskFragment());
-
- tf0.setAdjacentTaskFragments(adjacentTfs);
-
- assertSame(adjacentTfs, tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertTrue(tf0.hasAdjacentTaskFragment());
- assertTrue(tf1.hasAdjacentTaskFragment());
- assertTrue(tf2.hasAdjacentTaskFragment());
-
- final TaskFragment.AdjacentSet adjacentTfs2 = new TaskFragment.AdjacentSet(tf0, tf1);
- tf0.setAdjacentTaskFragments(adjacentTfs2);
-
- assertSame(adjacentTfs2, tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs2, tf1.getAdjacentTaskFragments());
- assertNull(tf2.getAdjacentTaskFragments());
- assertTrue(tf0.hasAdjacentTaskFragment());
- assertTrue(tf1.hasAdjacentTaskFragment());
- assertFalse(tf2.hasAdjacentTaskFragment());
+
+ // Can have two TFs adjacent,
+ new TaskFragment.AdjacentSet(tf0, tf1);
+
+ // 3+ TFs adjacent is not yet supported.
+ assertThrows(IllegalArgumentException.class,
+ () -> new TaskFragment.AdjacentSet(tf0, tf1, tf2));
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
- public void testClearAdjacentTaskFragments() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
-
- tf0.clearAdjacentTaskFragments();
+ public void testSetAdjacentTaskFragments() {
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ assertFalse(task0.hasAdjacentTaskFragment());
+
+ task0.setAdjacentTaskFragments(adjTasks);
+
+ assertSame(adjTasks, task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertTrue(task0.hasAdjacentTaskFragment());
+ assertTrue(task1.hasAdjacentTaskFragment());
+ assertTrue(task2.hasAdjacentTaskFragment());
+
+ final TaskFragment.AdjacentSet adjTasks2 = new TaskFragment.AdjacentSet(task0, task1);
+ task0.setAdjacentTaskFragments(adjTasks2);
+
+ assertSame(adjTasks2, task0.getAdjacentTaskFragments());
+ assertSame(adjTasks2, task1.getAdjacentTaskFragments());
+ assertNull(task2.getAdjacentTaskFragments());
+ assertTrue(task0.hasAdjacentTaskFragment());
+ assertTrue(task1.hasAdjacentTaskFragment());
+ assertFalse(task2.hasAdjacentTaskFragment());
+ }
- assertNull(tf0.getAdjacentTaskFragments());
- assertNull(tf1.getAdjacentTaskFragments());
- assertNull(tf2.getAdjacentTaskFragments());
- assertFalse(tf0.hasAdjacentTaskFragment());
- assertFalse(tf1.hasAdjacentTaskFragment());
- assertFalse(tf2.hasAdjacentTaskFragment());
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testClearAdjacentTaskFragments() {
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
+
+ task0.clearAdjacentTaskFragments();
+
+ assertNull(task0.getAdjacentTaskFragments());
+ assertNull(task1.getAdjacentTaskFragments());
+ assertNull(task2.getAdjacentTaskFragments());
+ assertFalse(task0.hasAdjacentTaskFragment());
+ assertFalse(task1.hasAdjacentTaskFragment());
+ assertFalse(task2.hasAdjacentTaskFragment());
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
public void testRemoveFromAdjacentTaskFragments() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
-
- tf0.removeFromAdjacentTaskFragments();
-
- assertNull(tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertFalse(adjacentTfs.contains(tf0));
- assertTrue(tf1.isAdjacentTo(tf2));
- assertTrue(tf2.isAdjacentTo(tf1));
- assertFalse(tf1.isAdjacentTo(tf0));
- assertFalse(tf0.isAdjacentTo(tf1));
- assertFalse(tf0.isAdjacentTo(tf0));
- assertFalse(tf1.isAdjacentTo(tf1));
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
+
+ task0.removeFromAdjacentTaskFragments();
+
+ assertNull(task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertFalse(adjTasks.contains(task0));
+ assertTrue(task1.isAdjacentTo(task2));
+ assertTrue(task2.isAdjacentTo(task1));
+ assertFalse(task1.isAdjacentTo(task0));
+ assertFalse(task0.isAdjacentTo(task1));
+ assertFalse(task0.isAdjacentTo(task0));
+ assertFalse(task1.isAdjacentTo(task1));
}
@EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
@Test
public void testRemoveFromAdjacentTaskFragmentsWhenRemove() {
- final Task task = createTask(mDisplayContent);
- final TaskFragment tf0 = createTaskFragmentWithActivity(task);
- final TaskFragment tf1 = createTaskFragmentWithActivity(task);
- final TaskFragment tf2 = createTaskFragmentWithActivity(task);
- final TaskFragment.AdjacentSet adjacentTfs = new TaskFragment.AdjacentSet(tf0, tf1, tf2);
- tf0.setAdjacentTaskFragments(adjacentTfs);
-
- tf0.removeImmediately();
-
- assertNull(tf0.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf1.getAdjacentTaskFragments());
- assertSame(adjacentTfs, tf2.getAdjacentTaskFragments());
- assertFalse(adjacentTfs.contains(tf0));
+ final Task task0 = createTask(mDisplayContent);
+ final Task task1 = createTask(mDisplayContent);
+ final Task task2 = createTask(mDisplayContent);
+ final TaskFragment.AdjacentSet adjTasks = new TaskFragment.AdjacentSet(task0, task1, task2);
+ task0.setAdjacentTaskFragments(adjTasks);
+
+ task0.removeImmediately();
+
+ assertNull(task0.getAdjacentTaskFragments());
+ assertSame(adjTasks, task1.getAdjacentTaskFragments());
+ assertSame(adjTasks, task2.getAdjacentTaskFragments());
+ assertFalse(adjTasks.contains(task0));
}
private WindowState createAppWindow(ActivityRecord app, String name) {