Merge "[5/n] Pin ActivityStack" into udc-qpr-dev
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
index e153bb7..43fa0be 100644
--- a/core/java/android/window/TaskFragmentOperation.java
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -80,6 +80,14 @@
*/
public static final int OP_TYPE_REORDER_TO_FRONT = 10;
+ /**
+ * Sets the activity navigation to be isolated, where the activity navigation on the
+ * TaskFragment is separated from the rest activities in the Task. Activities cannot be
+ * started on an isolated TaskFragment unless the activities are launched from the same
+ * TaskFragment or explicitly requested to.
+ */
+ public static final int OP_TYPE_SET_ISOLATED_NAVIGATION = 11;
+
@IntDef(prefix = { "OP_TYPE_" }, value = {
OP_TYPE_UNKNOWN,
OP_TYPE_CREATE_TASK_FRAGMENT,
@@ -92,7 +100,8 @@
OP_TYPE_SET_COMPANION_TASK_FRAGMENT,
OP_TYPE_SET_ANIMATION_PARAMS,
OP_TYPE_SET_RELATIVE_BOUNDS,
- OP_TYPE_REORDER_TO_FRONT
+ OP_TYPE_REORDER_TO_FRONT,
+ OP_TYPE_SET_ISOLATED_NAVIGATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface OperationType {}
@@ -118,11 +127,14 @@
@Nullable
private final TaskFragmentAnimationParams mAnimationParams;
+ private final boolean mIsolatedNav;
+
private TaskFragmentOperation(@OperationType int opType,
@Nullable TaskFragmentCreationParams taskFragmentCreationParams,
@Nullable IBinder activityToken, @Nullable Intent activityIntent,
@Nullable Bundle bundle, @Nullable IBinder secondaryFragmentToken,
- @Nullable TaskFragmentAnimationParams animationParams) {
+ @Nullable TaskFragmentAnimationParams animationParams,
+ boolean isolatedNav) {
mOpType = opType;
mTaskFragmentCreationParams = taskFragmentCreationParams;
mActivityToken = activityToken;
@@ -130,6 +142,7 @@
mBundle = bundle;
mSecondaryFragmentToken = secondaryFragmentToken;
mAnimationParams = animationParams;
+ mIsolatedNav = isolatedNav;
}
private TaskFragmentOperation(Parcel in) {
@@ -140,6 +153,7 @@
mBundle = in.readBundle(getClass().getClassLoader());
mSecondaryFragmentToken = in.readStrongBinder();
mAnimationParams = in.readTypedObject(TaskFragmentAnimationParams.CREATOR);
+ mIsolatedNav = in.readBoolean();
}
@Override
@@ -151,6 +165,7 @@
dest.writeBundle(mBundle);
dest.writeStrongBinder(mSecondaryFragmentToken);
dest.writeTypedObject(mAnimationParams, flags);
+ dest.writeBoolean(mIsolatedNav);
}
@NonNull
@@ -223,6 +238,14 @@
return mAnimationParams;
}
+ /**
+ * Returns whether the activity navigation on this TaskFragment is isolated. This is only
+ * useful when the op type is {@link OP_TYPE_SET_ISOLATED_NAVIGATION}.
+ */
+ public boolean isIsolatedNav() {
+ return mIsolatedNav;
+ }
+
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
@@ -245,6 +268,7 @@
if (mAnimationParams != null) {
sb.append(", animationParams=").append(mAnimationParams);
}
+ sb.append(", isolatedNav=").append(mIsolatedNav);
sb.append('}');
return sb.toString();
@@ -253,7 +277,7 @@
@Override
public int hashCode() {
return Objects.hash(mOpType, mTaskFragmentCreationParams, mActivityToken, mActivityIntent,
- mBundle, mSecondaryFragmentToken, mAnimationParams);
+ mBundle, mSecondaryFragmentToken, mAnimationParams, mIsolatedNav);
}
@Override
@@ -268,7 +292,8 @@
&& Objects.equals(mActivityIntent, other.mActivityIntent)
&& Objects.equals(mBundle, other.mBundle)
&& Objects.equals(mSecondaryFragmentToken, other.mSecondaryFragmentToken)
- && Objects.equals(mAnimationParams, other.mAnimationParams);
+ && Objects.equals(mAnimationParams, other.mAnimationParams)
+ && mIsolatedNav == other.mIsolatedNav;
}
@Override
@@ -300,6 +325,8 @@
@Nullable
private TaskFragmentAnimationParams mAnimationParams;
+ private boolean mIsolatedNav;
+
/**
* @param opType the {@link OperationType} of this {@link TaskFragmentOperation}.
*/
@@ -363,12 +390,22 @@
}
/**
+ * Sets the activity navigation of this TaskFragment to be isolated.
+ */
+ @NonNull
+ public Builder setIsolatedNav(boolean isolatedNav) {
+ mIsolatedNav = isolatedNav;
+ return this;
+ }
+
+ /**
* Constructs the {@link TaskFragmentOperation}.
*/
@NonNull
public TaskFragmentOperation build() {
return new TaskFragmentOperation(mOpType, mTaskFragmentCreationParams, mActivityToken,
- mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams);
+ mActivityIntent, mBundle, mSecondaryFragmentToken, mAnimationParams,
+ mIsolatedNav);
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index d94e8e4..4d73c20 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -17,7 +17,9 @@
package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -340,6 +342,20 @@
wct.deleteTaskFragment(fragmentToken);
}
+ void reorderTaskFragmentToFront(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken) {
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_REORDER_TO_FRONT).build();
+ wct.addTaskFragmentOperation(fragmentToken, operation);
+ }
+
+ void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, boolean isolatedNav) {
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_ISOLATED_NAVIGATION).setIsolatedNav(isolatedNav).build();
+ wct.addTaskFragmentOperation(fragmentToken, operation);
+ }
+
void updateTaskFragmentInfo(@NonNull TaskFragmentInfo taskFragmentInfo) {
mFragmentInfos.put(taskFragmentInfo.getFragmentToken(), taskFragmentInfo);
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index a2f75e0..f95f3ff 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -250,6 +250,10 @@
// Updates the Split
final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
final WindowContainerTransaction wct = transactionRecord.getTransaction();
+
+ mPresenter.setTaskFragmentIsolatedNavigation(wct,
+ splitPinContainer.getSecondaryContainer().getTaskFragmentToken(),
+ true /* isolatedNav */);
mPresenter.updateSplitContainer(splitPinContainer, wct);
transactionRecord.apply(false /* shouldApplyIndependently */);
updateCallbackIfNecessary();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 4dafbd1..5de6acf 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -17,7 +17,6 @@
package androidx.window.extensions.embedding;
import static android.content.pm.PackageManager.MATCH_ALL;
-import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT;
import android.app.Activity;
import android.app.ActivityThread;
@@ -40,7 +39,6 @@
import android.view.WindowMetrics;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
-import android.window.TaskFragmentOperation;
import android.window.WindowContainerTransaction;
import androidx.annotation.IntDef;
@@ -427,10 +425,8 @@
final SplitPinContainer pinnedContainer =
container.getTaskContainer().getSplitPinContainer();
if (pinnedContainer != null) {
- final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
- OP_TYPE_REORDER_TO_FRONT).build();
- wct.addTaskFragmentOperation(
- pinnedContainer.getSecondaryContainer().getTaskFragmentToken(), operation);
+ reorderTaskFragmentToFront(wct,
+ pinnedContainer.getSecondaryContainer().getTaskFragmentToken());
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 884100c..d187d23 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -3100,7 +3100,18 @@
} else {
TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
if (candidateTf == null) {
- final ActivityRecord top = task.topRunningActivity(false /* focusableOnly */);
+ // Puts the activity on the top-most non-isolated navigation TF, unless the
+ // activity is launched from the same TF.
+ final TaskFragment sourceTaskFragment =
+ mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
+ final ActivityRecord top = task.getActivity(r -> {
+ if (!r.canBeTopRunning()) {
+ return false;
+ }
+ final TaskFragment taskFragment = r.getTaskFragment();
+ return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null
+ && sourceTaskFragment == taskFragment);
+ });
if (top != null) {
candidateTf = top.getTaskFragment();
}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 92c0987..57ce368 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -329,6 +329,15 @@
*/
private boolean mDelayLastActivityRemoval;
+ /**
+ * Whether the activity navigation should be isolated. That is, Activities cannot be launched
+ * on an isolated TaskFragment, unless the activity is launched from an Activity in the same
+ * isolated TaskFragment, or explicitly requested to be launched to.
+ * <p>
+ * Note that only an embedded TaskFragment can be isolated.
+ */
+ private boolean mIsolatedNav;
+
final Point mLastSurfaceSize = new Point();
private final Rect mTmpBounds = new Rect();
@@ -481,6 +490,19 @@
return mAnimationParams;
}
+ /** @see #mIsolatedNav */
+ void setIsolatedNav(boolean isolatedNav) {
+ if (!isEmbedded()) {
+ return;
+ }
+ mIsolatedNav = isolatedNav;
+ }
+
+ /** @see #mIsolatedNav */
+ boolean isIsolatedNav() {
+ return isEmbedded() && mIsolatedNav;
+ }
+
TaskFragment getAdjacentTaskFragment() {
return mAdjacentTaskFragment;
}
@@ -3034,7 +3056,8 @@
@Override
void dump(PrintWriter pw, String prefix, boolean dumpAll) {
super.dump(pw, prefix, dumpAll);
- pw.println(prefix + "bounds=" + getBounds().toShortString());
+ pw.println(prefix + "bounds=" + getBounds().toShortString()
+ + (mIsolatedNav ? ", isolatedNav" : ""));
final String doublePrefix = prefix + " ";
for (int i = mChildren.size() - 1; i >= 0; i--) {
final WindowContainer<?> child = mChildren.get(i);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5d239eb..be0f6db 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -30,6 +30,7 @@
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_COMPANION_TASK_FRAGMENT;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ISOLATED_NAVIGATION;
import static android.window.TaskFragmentOperation.OP_TYPE_SET_RELATIVE_BOUNDS;
import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.TaskFragmentOperation.OP_TYPE_UNKNOWN;
@@ -1356,6 +1357,11 @@
}
break;
}
+ case OP_TYPE_SET_ISOLATED_NAVIGATION: {
+ final boolean isolatedNav = operation.isIsolatedNav();
+ taskFragment.setIsolatedNav(isolatedNav);
+ break;
+ }
}
return effects;
}
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 9d597b1..6a9bb6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -653,4 +653,23 @@
assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(),
mDisplayContent.computeImeParent());
}
+
+ @Test
+ public void testIsolatedNavigation() {
+ final Task task = createTask(mDisplayContent);
+ final TaskFragment tf0 = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(1)
+ .setOrganizer(mOrganizer)
+ .setFragmentToken(new Binder())
+ .build();
+
+ // Cannot be isolated if not embedded.
+ task.setIsolatedNav(true);
+ assertFalse(task.isIsolatedNav());
+
+ // Ensure the TaskFragment is isolated once set.
+ tf0.setIsolatedNav(true);
+ assertTrue(tf0.isIsolatedNav());
+ }
}