summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-12-10 10:46:03 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-10 10:46:03 +0000
commit7de137da3097a356b657a872bb4c52169fb052b9 (patch)
treed09cb79150871948c0d50b2586a0d89503e7cf7b
parent5497fdbbc72eab0f08cb94713adb55a4007e3743 (diff)
parentb6f8509519f80ae64a75cce3d41e42b4b1295bdc (diff)
Merge "Allow multiple adjacent TFs (1/n)" into main
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java2
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java207
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java45
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java94
5 files changed, 317 insertions, 39 deletions
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 46312aff1fb6..68f812ff25b1 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2124,7 +2124,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (!tf.isOrganizedTaskFragment()) {
return;
}
- tf.resetAdjacentTaskFragment();
+ tf.clearAdjacentTaskFragments();
tf.setCompanionTaskFragment(null /* companionTaskFragment */);
tf.setAnimationParams(TaskFragmentAnimationParams.DEFAULT);
if (tf.getTopNonFinishingActivity() != null) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 51b8bd1f0091..9fb5bea132ef 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -94,6 +94,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.os.UserHandle;
+import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
@@ -239,12 +240,20 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** This task fragment will be removed when the cleanup of its children are done. */
private boolean mIsRemovalRequested;
- /** The TaskFragment that is adjacent to this one. */
+ /** @deprecated b/373709676 replace with {@link #mAdjacentTaskFragments} */
+ @Deprecated
@Nullable
private TaskFragment mAdjacentTaskFragment;
/**
- * Unlike the {@link mAdjacentTaskFragment}, the companion TaskFragment is not always visually
+ * The TaskFragments that are adjacent to each other, including this TaskFragment.
+ * All TaskFragments in this set share the same set instance.
+ */
+ @Nullable
+ private AdjacentSet mAdjacentTaskFragments;
+
+ /**
+ * Unlike the {@link #mAdjacentTaskFragments}, the companion TaskFragment is not always visually
* adjacent to this one, but this TaskFragment will be removed by the organizer if the
* companion TaskFragment is removed.
*/
@@ -442,15 +451,24 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
- if (mAdjacentTaskFragment == taskFragment) {
- return;
- }
- resetAdjacentTaskFragment();
- if (taskFragment != null) {
+ /** @deprecated b/373709676 replace with {@link #setAdjacentTaskFragments}. */
+ @Deprecated
+ void setAdjacentTaskFragment(@NonNull TaskFragment taskFragment) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ if (mAdjacentTaskFragment == taskFragment) {
+ return;
+ }
+ resetAdjacentTaskFragment();
mAdjacentTaskFragment = taskFragment;
taskFragment.setAdjacentTaskFragment(this);
+ return;
}
+
+ setAdjacentTaskFragments(new AdjacentSet(this, taskFragment));
+ }
+
+ void setAdjacentTaskFragments(@NonNull AdjacentSet adjacentTaskFragments) {
+ adjacentTaskFragments.setAsAdjacent();
}
void setCompanionTaskFragment(@Nullable TaskFragment companionTaskFragment) {
@@ -461,7 +479,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return mCompanionTaskFragment;
}
- void resetAdjacentTaskFragment() {
+ /** @deprecated b/373709676 replace with {@link #clearAdjacentTaskFragments()}. */
+ @Deprecated
+ private void resetAdjacentTaskFragment() {
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("resetAdjacentTaskFragment shouldn't be called when"
+ + " allowMultipleAdjacentTaskFragments is enabled. Use either"
+ + " #clearAdjacentTaskFragments or #removeFromAdjacentTaskFragments");
+ }
// Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
@@ -471,6 +496,57 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mDelayLastActivityRemoval = false;
}
+ void clearAdjacentTaskFragments() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ resetAdjacentTaskFragment();
+ return;
+ }
+
+ if (mAdjacentTaskFragments != null) {
+ mAdjacentTaskFragments.clear();
+ }
+ }
+
+ void removeFromAdjacentTaskFragments() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ resetAdjacentTaskFragment();
+ return;
+ }
+
+ if (mAdjacentTaskFragments != null) {
+ mAdjacentTaskFragments.remove(this);
+ }
+ }
+
+ // TODO(b/373709676): update usages.
+ /** @deprecated b/373709676 replace with {@link #getAdjacentTaskFragments()}. */
+ @Deprecated
+ @Nullable
+ TaskFragment getAdjacentTaskFragment() {
+ return mAdjacentTaskFragment;
+ }
+
+ @Nullable
+ AdjacentSet getAdjacentTaskFragments() {
+ return mAdjacentTaskFragments;
+ }
+
+ boolean hasAdjacentTaskFragment() {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ return mAdjacentTaskFragment != null;
+ }
+ return mAdjacentTaskFragments != null;
+ }
+
+ boolean isAdjacentTo(@NonNull TaskFragment other) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ return mAdjacentTaskFragment == other;
+ }
+ return other != this
+ && mAdjacentTaskFragments != null
+ && mAdjacentTaskFragments.contains(other);
+ }
+
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@NonNull String processName) {
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(organizer.asBinder());
@@ -566,10 +642,6 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return isEmbedded() && mPinned;
}
- TaskFragment getAdjacentTaskFragment() {
- return mAdjacentTaskFragment;
- }
-
/** Returns the currently topmost resumed activity. */
@Nullable
ActivityRecord getTopResumedActivity() {
@@ -616,7 +688,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mResumedActivity = r;
final ActivityRecord topResumed = mTaskSupervisor.updateTopResumedActivityIfNeeded(reason);
if (mResumedActivity != null && topResumed != null && topResumed.isEmbedded()
- && topResumed.getTaskFragment().getAdjacentTaskFragment() == this) {
+ && topResumed.getTaskFragment().isAdjacentTo(this)) {
// Explicitly updates the last resumed Activity if the resumed activity is
// adjacent to the top-resumed embedded activity.
mAtmService.setLastResumedActivityUncheckLocked(mResumedActivity, reason);
@@ -2036,7 +2108,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
private boolean shouldReportOrientationUnspecified() {
// Apps and their containers are not allowed to specify orientation from adjacent
// TaskFragment.
- return getAdjacentTaskFragment() != null && isVisibleRequested();
+ return hasAdjacentTaskFragment() && isVisibleRequested();
}
@Override
@@ -3086,7 +3158,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
EventLogTags.writeWmTfRemoved(System.identityHashCode(this), getTaskId());
}
mIsRemovalRequested = false;
- resetAdjacentTaskFragment();
+ removeFromAdjacentTaskFragments();
cleanUpEmbeddedTaskFragment();
final boolean shouldExecuteAppTransition =
mClearedTaskFragmentForPip && isTaskVisibleRequested();
@@ -3267,9 +3339,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
sb.append(" organizerProc=");
sb.append(mTaskFragmentOrganizerProcessName);
}
- if (mAdjacentTaskFragment != null) {
- sb.append(" adjacent=");
- sb.append(mAdjacentTaskFragment);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ if (mAdjacentTaskFragments != null) {
+ sb.append(" adjacent=");
+ sb.append(mAdjacentTaskFragments);
+ }
+ } else {
+ if (mAdjacentTaskFragment != null) {
+ sb.append(" adjacent=");
+ sb.append(mAdjacentTaskFragment);
+ }
}
sb.append('}');
return sb.toString();
@@ -3385,4 +3464,94 @@ class TaskFragment extends WindowContainer<WindowContainer> {
proto.end(token);
}
+
+ /** Set of {@link TaskFragment}s that are adjacent to each other. */
+ static class AdjacentSet {
+ private final ArraySet<TaskFragment> mAdjacentSet;
+
+ AdjacentSet(@NonNull TaskFragment... taskFragments) {
+ this(new ArraySet<>(taskFragments));
+ }
+
+ AdjacentSet(@NonNull ArraySet<TaskFragment> taskFragments) {
+ if (!Flags.allowMultipleAdjacentTaskFragments()) {
+ throw new IllegalStateException("allowMultipleAdjacentTaskFragments must be"
+ + " enabled to set more than two TaskFragments adjacent to each other.");
+ }
+ if (taskFragments.size() < 2) {
+ throw new IllegalArgumentException("Adjacent TaskFragments must contain at least"
+ + " two TaskFragments, but only " + taskFragments.size()
+ + " were provided.");
+ }
+ mAdjacentSet = taskFragments;
+ }
+
+ /** Updates each {@link TaskFragment} in the set to be adjacent to each other. */
+ private void setAsAdjacent() {
+ if (mAdjacentSet.isEmpty()
+ || equals(mAdjacentSet.valueAt(0).mAdjacentTaskFragments)) {
+ // No need to update if any TaskFragment in the set has already been updated to the
+ // same set.
+ return;
+ }
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ taskFragment.removeFromAdjacentTaskFragments();
+ taskFragment.mAdjacentTaskFragments = this;
+ }
+ }
+
+ /** Removes the {@link TaskFragment} from the adjacent set. */
+ private void remove(@NonNull TaskFragment taskFragment) {
+ taskFragment.mAdjacentTaskFragments = null;
+ taskFragment.mDelayLastActivityRemoval = false;
+ mAdjacentSet.remove(taskFragment);
+ if (mAdjacentSet.size() < 2) {
+ // To be considered as adjacent, there must be at least 2 TaskFragments in the set.
+ clear();
+ }
+ }
+
+ /** Clears the adjacent relationship. */
+ private void clear() {
+ for (int i = mAdjacentSet.size() - 1; i >= 0; i--) {
+ final TaskFragment taskFragment = mAdjacentSet.valueAt(i);
+ // Clear all reference.
+ taskFragment.mAdjacentTaskFragments = null;
+ taskFragment.mDelayLastActivityRemoval = false;
+ }
+ mAdjacentSet.clear();
+ }
+
+ /** Whether the {@link TaskFragment} is in this adjacent set. */
+ boolean contains(@NonNull TaskFragment taskFragment) {
+ return mAdjacentSet.contains(taskFragment);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AdjacentSet other)) {
+ return false;
+ }
+ return mAdjacentSet.equals(other.mAdjacentSet);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("AdjacentSet{");
+ final int size = mAdjacentSet.size();
+ for (int i = 0; i < size; i++) {
+ if (i != 0) {
+ sb.append(", ");
+ }
+ sb.append(mAdjacentSet.valueAt(i));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 66921ff3adeb..1eb84650d591 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1155,7 +1155,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
} else if (!task.mCreatedByOrganizer) {
throw new UnsupportedOperationException(
"Cannot set non-organized task as adjacent flag root: " + wc);
- } else if (task.getAdjacentTaskFragment() == null && !clearRoot) {
+ } else if (!task.hasAdjacentTaskFragment() && !clearRoot) {
throw new UnsupportedOperationException(
"Cannot set non-adjacent task as adjacent flag root: " + wc);
}
@@ -1645,9 +1645,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
opType, exception);
break;
}
- if (taskFragment.getAdjacentTaskFragment() != secondaryTaskFragment) {
+ if (!taskFragment.isAdjacentTo(secondaryTaskFragment)) {
// Only have lifecycle effect if the adjacent changed.
- taskFragment.setAdjacentTaskFragment(secondaryTaskFragment);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ // Activity Embedding only set two TFs adjacent.
+ taskFragment.setAdjacentTaskFragments(
+ new TaskFragment.AdjacentSet(taskFragment, secondaryTaskFragment));
+ } else {
+ taskFragment.setAdjacentTaskFragment(secondaryTaskFragment);
+ }
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -1663,21 +1669,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break;
}
case OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS: {
- final TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
- if (adjacentTaskFragment == null) {
+ if (!taskFragment.hasAdjacentTaskFragment()) {
break;
}
- taskFragment.resetAdjacentTaskFragment();
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- // Clear the focused app if the focused app is no longer visible after reset the
- // adjacent TaskFragments.
+ // Check if the focused app is in the adjacent set that will be cleared.
final ActivityRecord focusedApp = taskFragment.getDisplayContent().mFocusedApp;
final TaskFragment focusedTaskFragment = focusedApp != null
? focusedApp.getTaskFragment()
: null;
- if ((focusedTaskFragment == taskFragment
- || focusedTaskFragment == adjacentTaskFragment)
+ final boolean wasFocusedInAdjacent = focusedTaskFragment == taskFragment
+ || (focusedTaskFragment != null
+ && taskFragment.isAdjacentTo(focusedTaskFragment));
+
+ taskFragment.removeFromAdjacentTaskFragments();
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+
+ // Clear the focused app if the focused app is no longer visible after reset the
+ // adjacent TaskFragments.
+ if (wasFocusedInAdjacent
&& !focusedTaskFragment.shouldBeVisible(null /* starting */)) {
focusedTaskFragment.getDisplayContent().setFocusedApp(null /* newFocus */);
}
@@ -2207,10 +2217,15 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- if (root1.getAdjacentTaskFragment() == root2) {
+ if (root1.isAdjacentTo(root2)) {
return TRANSACT_EFFECTS_NONE;
}
- root1.setAdjacentTaskFragment(root2);
+ if (Flags.allowMultipleAdjacentTaskFragments()) {
+ // TODO(b/373709676): allow three roots.
+ root1.setAdjacentTaskFragments(new TaskFragment.AdjacentSet(root1, root2));
+ } else {
+ root1.setAdjacentTaskFragment(root2);
+ }
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -2225,10 +2240,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("clearAdjacentRootsHierarchyOp: Not created by"
+ " organizer root=" + root);
}
- if (root.getAdjacentTaskFragment() == null) {
+ if (!root.hasAdjacentTaskFragment()) {
return TRANSACT_EFFECTS_NONE;
}
- root.resetAdjacentTaskFragment();
+ root.removeFromAdjacentTaskFragments();
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 40da9ea2d718..579ed6659976 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -365,8 +365,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue(outPrevActivities.isEmpty());
assertTrue(predictable);
// reset
- tf1.setAdjacentTaskFragment(null);
- tf2.setAdjacentTaskFragment(null);
+ tf1.clearAdjacentTaskFragments();
+ tf2.clearAdjacentTaskFragments();
tf1.setCompanionTaskFragment(null);
tf2.setCompanionTaskFragment(null);
@@ -398,8 +398,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertTrue(predictable);
// reset
outPrevActivities.clear();
- tf2.setAdjacentTaskFragment(null);
- tf3.setAdjacentTaskFragment(null);
+ tf2.clearAdjacentTaskFragments();
+ tf3.clearAdjacentTaskFragments();
final TaskFragment tf4 = createTaskFragmentWithActivity(task);
// Stacked + next companion to top => predict for previous activity below companion.
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 65a6a69fc45e..dafa96f91812 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -51,6 +51,7 @@ import static org.junit.Assert.assertEquals;
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.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -62,6 +63,7 @@ import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Binder;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
import android.view.View;
@@ -1066,6 +1068,98 @@ public class TaskFragmentTest extends WindowTestsBase {
Math.min(outConfig.screenWidthDp, outConfig.screenHeightDp));
}
+ @EnableFlags(Flags.FLAG_ALLOW_MULTIPLE_ADJACENT_TASK_FRAGMENTS)
+ @Test
+ public void testSetAdjacentTaskFragments() {
+ 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());
+ }
+
+ @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();
+
+ 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 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));
+ }
+
+ @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));
+ }
+
private WindowState createAppWindow(ActivityRecord app, String name) {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());