Merge "Pass TaskFragmentAnimationParams to TaskFragment" into tm-qpr-dev
diff --git a/core/java/android/window/TaskFragmentAnimationParams.aidl b/core/java/android/window/TaskFragmentAnimationParams.aidl
new file mode 100644
index 0000000..8ae84c2
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAnimationParams.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * Data object for animation related override of TaskFragment.
+ * @hide
+ */
+parcelable TaskFragmentAnimationParams;
diff --git a/core/java/android/window/TaskFragmentAnimationParams.java b/core/java/android/window/TaskFragmentAnimationParams.java
new file mode 100644
index 0000000..12ad914
--- /dev/null
+++ b/core/java/android/window/TaskFragmentAnimationParams.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data object for animation related override of TaskFragment.
+ * @hide
+ */
+// TODO(b/206557124): Add more animation customization options.
+public final class TaskFragmentAnimationParams implements Parcelable {
+
+ /** The default {@link TaskFragmentAnimationParams} to use when there is no app override. */
+ public static final TaskFragmentAnimationParams DEFAULT =
+ new TaskFragmentAnimationParams.Builder().build();
+
+ @ColorInt
+ private final int mAnimationBackgroundColor;
+
+ private TaskFragmentAnimationParams(@ColorInt int animationBackgroundColor) {
+ mAnimationBackgroundColor = animationBackgroundColor;
+ }
+
+ /**
+ * The {@link ColorInt} to use for the background during the animation with this TaskFragment if
+ * the animation requires a background.
+ *
+ * The default value is {@code 0}, which is to use the theme window background.
+ */
+ @ColorInt
+ public int getAnimationBackgroundColor() {
+ return mAnimationBackgroundColor;
+ }
+
+ private TaskFragmentAnimationParams(Parcel in) {
+ mAnimationBackgroundColor = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mAnimationBackgroundColor);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentAnimationParams> CREATOR =
+ new Creator<TaskFragmentAnimationParams>() {
+ @Override
+ public TaskFragmentAnimationParams createFromParcel(Parcel in) {
+ return new TaskFragmentAnimationParams(in);
+ }
+
+ @Override
+ public TaskFragmentAnimationParams[] newArray(int size) {
+ return new TaskFragmentAnimationParams[size];
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "TaskFragmentAnimationParams{"
+ + " animationBgColor=" + Integer.toHexString(mAnimationBackgroundColor)
+ + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return mAnimationBackgroundColor;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TaskFragmentAnimationParams)) {
+ return false;
+ }
+ final TaskFragmentAnimationParams other = (TaskFragmentAnimationParams) obj;
+ return mAnimationBackgroundColor == other.mAnimationBackgroundColor;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Builder to construct the {@link TaskFragmentAnimationParams}. */
+ public static final class Builder {
+
+ @ColorInt
+ private int mAnimationBackgroundColor = 0;
+
+ /**
+ * Sets the {@link ColorInt} to use for the background during the animation with this
+ * TaskFragment if the animation requires a background. The default value is
+ * {@code 0}, which is to use the theme window background.
+ *
+ * @param color a packed color int, {@code AARRGGBB}, for the animation background color.
+ * @return this {@link Builder}.
+ */
+ @NonNull
+ public Builder setAnimationBackgroundColor(@ColorInt int color) {
+ mAnimationBackgroundColor = color;
+ return this;
+ }
+
+ /** Constructs the {@link TaskFragmentAnimationParams}. */
+ @NonNull
+ public TaskFragmentAnimationParams build() {
+ return new TaskFragmentAnimationParams(mAnimationBackgroundColor);
+ }
+ }
+}
diff --git a/core/java/android/window/TaskFragmentOperation.aidl b/core/java/android/window/TaskFragmentOperation.aidl
new file mode 100644
index 0000000..c1ed0c7
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOperation.aidl
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+/**
+ * Data object of params for TaskFragment related {@link WindowContainerTransaction} operation.
+ * @hide
+ */
+parcelable TaskFragmentOperation;
diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java
new file mode 100644
index 0000000..0f30b79
--- /dev/null
+++ b/core/java/android/window/TaskFragmentOperation.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Data object of params for TaskFragment related {@link WindowContainerTransaction} operation.
+ *
+ * @see WindowContainerTransaction#setTaskFragmentOperation(IBinder, TaskFragmentOperation).
+ * @hide
+ */
+// TODO(b/263436063): move other TaskFragment related operation here.
+public final class TaskFragmentOperation implements Parcelable {
+
+ /** Sets the {@link TaskFragmentAnimationParams} for the given TaskFragment. */
+ public static final int OP_TYPE_SET_ANIMATION_PARAMS = 0;
+
+ @IntDef(prefix = { "OP_TYPE_" }, value = {
+ OP_TYPE_SET_ANIMATION_PARAMS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface OperationType {}
+
+ @OperationType
+ private final int mOpType;
+
+ @Nullable
+ private final TaskFragmentAnimationParams mAnimationParams;
+
+ private TaskFragmentOperation(@OperationType int opType,
+ @Nullable TaskFragmentAnimationParams animationParams) {
+ mOpType = opType;
+ mAnimationParams = animationParams;
+ }
+
+ private TaskFragmentOperation(Parcel in) {
+ mOpType = in.readInt();
+ mAnimationParams = in.readTypedObject(TaskFragmentAnimationParams.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mOpType);
+ dest.writeTypedObject(mAnimationParams, flags);
+ }
+
+ @NonNull
+ public static final Creator<TaskFragmentOperation> CREATOR =
+ new Creator<TaskFragmentOperation>() {
+ @Override
+ public TaskFragmentOperation createFromParcel(Parcel in) {
+ return new TaskFragmentOperation(in);
+ }
+
+ @Override
+ public TaskFragmentOperation[] newArray(int size) {
+ return new TaskFragmentOperation[size];
+ }
+ };
+
+ /**
+ * Gets the {@link OperationType} of this {@link TaskFragmentOperation}.
+ */
+ @OperationType
+ public int getOpType() {
+ return mOpType;
+ }
+
+ /**
+ * Gets the animation related override of TaskFragment.
+ */
+ @Nullable
+ public TaskFragmentAnimationParams getAnimationParams() {
+ return mAnimationParams;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("TaskFragmentOperation{ opType=").append(mOpType);
+ if (mAnimationParams != null) {
+ sb.append(", animationParams=").append(mAnimationParams);
+ }
+
+ sb.append('}');
+ return sb.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mOpType;
+ result = result * 31 + mAnimationParams.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof TaskFragmentOperation)) {
+ return false;
+ }
+ final TaskFragmentOperation other = (TaskFragmentOperation) obj;
+ return mOpType == other.mOpType
+ && Objects.equals(mAnimationParams, other.mAnimationParams);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Builder to construct the {@link TaskFragmentOperation}. */
+ public static final class Builder {
+
+ @OperationType
+ private final int mOpType;
+
+ @Nullable
+ private TaskFragmentAnimationParams mAnimationParams;
+
+ /**
+ * @param opType the {@link OperationType} of this {@link TaskFragmentOperation}.
+ */
+ public Builder(@OperationType int opType) {
+ mOpType = opType;
+ }
+
+ /**
+ * Sets the {@link TaskFragmentAnimationParams} for the given TaskFragment.
+ */
+ @NonNull
+ public Builder setAnimationParams(@Nullable TaskFragmentAnimationParams animationParams) {
+ mAnimationParams = animationParams;
+ return this;
+ }
+
+ /**
+ * Constructs the {@link TaskFragmentOperation}.
+ */
+ @NonNull
+ public TaskFragmentOperation build() {
+ return new TaskFragmentOperation(mOpType, mAnimationParams);
+ }
+ }
+}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index f90353a..d7b4929 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -763,6 +763,30 @@
}
/**
+ * Sets the {@link TaskFragmentOperation} to apply to the given TaskFragment.
+ *
+ * @param fragmentToken client assigned unique token to create TaskFragment with specified in
+ * {@link TaskFragmentCreationParams#getFragmentToken()}.
+ * @param taskFragmentOperation the {@link TaskFragmentOperation} to apply to the given
+ * TaskFramgent.
+ * @hide
+ */
+ @NonNull
+ public WindowContainerTransaction setTaskFragmentOperation(@NonNull IBinder fragmentToken,
+ @NonNull TaskFragmentOperation taskFragmentOperation) {
+ Objects.requireNonNull(fragmentToken);
+ Objects.requireNonNull(taskFragmentOperation);
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION)
+ .setContainer(fragmentToken)
+ .setTaskFragmentOperation(taskFragmentOperation)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
* Sets/removes the always on top flag for this {@code windowContainer}. See
* {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
* Please note that this method is only intended to be used for a
@@ -1262,6 +1286,7 @@
public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
public static final int HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT = 22;
public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 23;
+ public static final int HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION = 24;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1302,10 +1327,14 @@
@Nullable
private Intent mActivityIntent;
- // Used as options for WindowContainerTransaction#createTaskFragment().
+ /** Used as options for {@link #createTaskFragment}. */
@Nullable
private TaskFragmentCreationParams mTaskFragmentCreationOptions;
+ /** Used as options for {@link #setTaskFragmentOperation}. */
+ @Nullable
+ private TaskFragmentOperation mTaskFragmentOperation;
+
@Nullable
private PendingIntent mPendingIntent;
@@ -1418,6 +1447,7 @@
mLaunchOptions = copy.mLaunchOptions;
mActivityIntent = copy.mActivityIntent;
mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions;
+ mTaskFragmentOperation = copy.mTaskFragmentOperation;
mPendingIntent = copy.mPendingIntent;
mShortcutInfo = copy.mShortcutInfo;
mAlwaysOnTop = copy.mAlwaysOnTop;
@@ -1441,6 +1471,7 @@
mLaunchOptions = in.readBundle();
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR);
+ mTaskFragmentOperation = in.readTypedObject(TaskFragmentOperation.CREATOR);
mPendingIntent = in.readTypedObject(PendingIntent.CREATOR);
mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR);
mAlwaysOnTop = in.readBoolean();
@@ -1529,6 +1560,11 @@
}
@Nullable
+ public TaskFragmentOperation getTaskFragmentOperation() {
+ return mTaskFragmentOperation;
+ }
+
+ @Nullable
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
@@ -1604,6 +1640,9 @@
case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
return "{setReparentLeafTaskIfRelaunch: container= " + mContainer
+ " reparentLeafTaskIfRelaunch= " + mReparentLeafTaskIfRelaunch + "}";
+ case HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION:
+ return "{setTaskFragmentOperation: fragmentToken= " + mContainer
+ + " operation= " + mTaskFragmentOperation + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
@@ -1631,6 +1670,7 @@
dest.writeBundle(mLaunchOptions);
dest.writeTypedObject(mActivityIntent, flags);
dest.writeTypedObject(mTaskFragmentCreationOptions, flags);
+ dest.writeTypedObject(mTaskFragmentOperation, flags);
dest.writeTypedObject(mPendingIntent, flags);
dest.writeTypedObject(mShortcutInfo, flags);
dest.writeBoolean(mAlwaysOnTop);
@@ -1688,6 +1728,9 @@
private TaskFragmentCreationParams mTaskFragmentCreationOptions;
@Nullable
+ private TaskFragmentOperation mTaskFragmentOperation;
+
+ @Nullable
private PendingIntent mPendingIntent;
@Nullable
@@ -1767,6 +1810,12 @@
return this;
}
+ Builder setTaskFragmentOperation(
+ @Nullable TaskFragmentOperation taskFragmentOperation) {
+ mTaskFragmentOperation = taskFragmentOperation;
+ return this;
+ }
+
Builder setReparentLeafTaskIfRelaunch(boolean reparentLeafTaskIfRelaunch) {
mReparentLeafTaskIfRelaunch = reparentLeafTaskIfRelaunch;
return this;
@@ -1796,6 +1845,7 @@
hierarchyOp.mPendingIntent = mPendingIntent;
hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
+ hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation;
hierarchyOp.mShortcutInfo = mShortcutInfo;
hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;
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 b910287..87fa63d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -17,6 +17,7 @@
package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -31,8 +32,10 @@
import android.os.Bundle;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOperation;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -114,13 +117,14 @@
* @param activityIntent Intent to start the secondary Activity with.
* @param activityOptions ActivityOptions to start the secondary Activity with.
* @param windowingMode the windowing mode to set for the TaskFragments.
+ * @param splitAttributes the {@link SplitAttributes} to represent the split.
*/
void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull IBinder launchingFragmentToken, @NonNull Rect launchingFragmentBounds,
@NonNull Activity launchingActivity, @NonNull IBinder secondaryFragmentToken,
@NonNull Rect secondaryFragmentBounds, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions, @NonNull SplitRule rule,
- @WindowingMode int windowingMode) {
+ @WindowingMode int windowingMode, @NonNull SplitAttributes splitAttributes) {
final IBinder ownerToken = launchingActivity.getActivityToken();
// Create or resize the launching TaskFragment.
@@ -131,6 +135,7 @@
createTaskFragmentAndReparentActivity(wct, launchingFragmentToken, ownerToken,
launchingFragmentBounds, windowingMode, launchingActivity);
}
+ updateAnimationParams(wct, launchingFragmentToken, splitAttributes);
// Create a TaskFragment for the secondary activity.
final TaskFragmentCreationParams fragmentOptions = new TaskFragmentCreationParams.Builder(
@@ -144,6 +149,7 @@
.setPairedPrimaryFragmentToken(launchingFragmentToken)
.build();
createTaskFragment(wct, fragmentOptions);
+ updateAnimationParams(wct, secondaryFragmentToken, splitAttributes);
wct.startActivityInTaskFragment(secondaryFragmentToken, ownerToken, activityIntent,
activityOptions);
@@ -163,6 +169,7 @@
resizeTaskFragment(wct, fragmentToken, new Rect());
setAdjacentTaskFragments(wct, fragmentToken, null /* secondary */, null /* splitRule */);
updateWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED);
+ updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
}
/**
@@ -175,6 +182,7 @@
createTaskFragmentAndReparentActivity(
wct, fragmentToken, activity.getActivityToken(), new Rect(),
WINDOWING_MODE_UNDEFINED, activity);
+ updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
}
/**
@@ -270,6 +278,24 @@
wct.setWindowingMode(mFragmentInfos.get(fragmentToken).getToken(), windowingMode);
}
+ /**
+ * Updates the {@link TaskFragmentAnimationParams} for the given TaskFragment based on
+ * {@link SplitAttributes}.
+ */
+ void updateAnimationParams(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, @NonNull SplitAttributes splitAttributes) {
+ updateAnimationParams(wct, fragmentToken, createAnimationParamsOrDefault(splitAttributes));
+ }
+
+ void updateAnimationParams(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, @NonNull TaskFragmentAnimationParams animationParams) {
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_ANIMATION_PARAMS)
+ .setAnimationParams(animationParams)
+ .build();
+ wct.setTaskFragmentOperation(fragmentToken, operation);
+ }
+
void deleteTaskFragment(@NonNull WindowContainerTransaction wct,
@NonNull IBinder fragmentToken) {
if (!mFragmentInfos.containsKey(fragmentToken)) {
@@ -291,4 +317,14 @@
public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
mCallback.onTransactionReady(transaction);
}
+
+ private static TaskFragmentAnimationParams createAnimationParamsOrDefault(
+ @Nullable SplitAttributes splitAttributes) {
+ if (splitAttributes == null) {
+ return TaskFragmentAnimationParams.DEFAULT;
+ }
+ return new TaskFragmentAnimationParams.Builder()
+ .setAnimationBackgroundColor(splitAttributes.getAnimationBackgroundColor())
+ .build();
+ }
}
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 1af1313..8da5918 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -65,6 +65,7 @@
import android.util.Size;
import android.util.SparseArray;
import android.view.WindowMetrics;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
@@ -1149,6 +1150,8 @@
taskId);
mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
+ mPresenter.updateAnimationParams(wct, expandedContainer.getTaskFragmentToken(),
+ TaskFragmentAnimationParams.DEFAULT);
return expandedContainer;
}
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 9db9f87..7b2af49 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -36,6 +36,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowMetrics;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.WindowContainerTransaction;
@@ -176,7 +177,7 @@
final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
- primaryActivity, primaryRectBounds, null);
+ primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */);
// Create new empty task fragment
final int taskId = primaryContainer.getTaskId();
@@ -189,6 +190,7 @@
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRectBounds,
windowingMode);
+ updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
// Set adjacent to each other so that the containers below will be invisible.
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
@@ -222,7 +224,7 @@
final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
- primaryActivity, primaryRectBounds, null);
+ primaryActivity, primaryRectBounds, splitAttributes, null /* containerToAvoid */);
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
@@ -236,7 +238,7 @@
containerToAvoid = curSecondaryContainer;
}
final TaskFragmentContainer secondaryContainer = prepareContainerForActivity(wct,
- secondaryActivity, secondaryRectBounds, containerToAvoid);
+ secondaryActivity, secondaryRectBounds, splitAttributes, containerToAvoid);
// Set adjacent to each other so that the containers below will be invisible.
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
@@ -253,7 +255,8 @@
*/
private TaskFragmentContainer prepareContainerForActivity(
@NonNull WindowContainerTransaction wct, @NonNull Activity activity,
- @NonNull Rect bounds, @Nullable TaskFragmentContainer containerToAvoid) {
+ @NonNull Rect bounds, @NonNull SplitAttributes splitAttributes,
+ @Nullable TaskFragmentContainer containerToAvoid) {
TaskFragmentContainer container = mController.getContainerWithActivity(activity);
final int taskId = container != null ? container.getTaskId() : activity.getTaskId();
if (container == null || container == containerToAvoid) {
@@ -270,6 +273,7 @@
.getWindowingModeForSplitTaskFragment(bounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
+ updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
return container;
}
@@ -314,7 +318,7 @@
rule, splitAttributes);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
- activityIntent, activityOptions, rule, windowingMode);
+ activityIntent, activityOptions, rule, windowingMode, splitAttributes);
if (isPlaceholder) {
// When placeholder is launched in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
@@ -365,6 +369,8 @@
primaryRectBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
+ updateAnimationParams(wct, primaryContainer.getTaskFragmentToken(), splitAttributes);
+ updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
}
private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@@ -459,6 +465,24 @@
super.updateWindowingMode(wct, fragmentToken, windowingMode);
}
+ @Override
+ void updateAnimationParams(@NonNull WindowContainerTransaction wct,
+ @NonNull IBinder fragmentToken, @NonNull TaskFragmentAnimationParams animationParams) {
+ final TaskFragmentContainer container = mController.getContainer(fragmentToken);
+ if (container == null) {
+ throw new IllegalStateException("Setting animation params for a task fragment that is"
+ + " not registered with controller.");
+ }
+
+ if (container.areLastRequestedAnimationParamsEqual(animationParams)) {
+ // Return early if the animation params were already requested
+ return;
+ }
+
+ container.setLastRequestAnimationParams(animationParams);
+ super.updateAnimationParams(wct, fragmentToken, animationParams);
+ }
+
/**
* Expands the split container if the current split bounds are smaller than the Activity or
* Intent that is added to the container.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6bfdfe7..076856c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -26,6 +26,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.util.Size;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -108,6 +109,13 @@
private int mLastRequestedWindowingMode = WINDOWING_MODE_UNDEFINED;
/**
+ * TaskFragmentAnimationParams that was requested last via
+ * {@link android.window.WindowContainerTransaction}.
+ */
+ @NonNull
+ private TaskFragmentAnimationParams mLastAnimationParams = TaskFragmentAnimationParams.DEFAULT;
+
+ /**
* When the TaskFragment has appeared in server, but is empty, we should remove the TaskFragment
* if it is still empty after the timeout.
*/
@@ -560,6 +568,21 @@
mLastRequestedWindowingMode = windowingModes;
}
+ /**
+ * Checks if last requested {@link TaskFragmentAnimationParams} are equal to the provided value.
+ */
+ boolean areLastRequestedAnimationParamsEqual(
+ @NonNull TaskFragmentAnimationParams animationParams) {
+ return mLastAnimationParams.equals(animationParams);
+ }
+
+ /**
+ * Updates the last requested {@link TaskFragmentAnimationParams}.
+ */
+ void setLastRequestAnimationParams(@NonNull TaskFragmentAnimationParams animationParams) {
+ mLastAnimationParams = animationParams;
+ }
+
/** Gets the parent leaf Task id. */
int getTaskId() {
return mTaskContainer.getTaskId();
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 6dae0a1..fcd4d62 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
@@ -60,12 +61,15 @@
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.util.Pair;
import android.util.Size;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOperation;
import android.window.WindowContainerTransaction;
import androidx.test.core.app.ApplicationProvider;
@@ -163,7 +167,38 @@
WINDOWING_MODE_MULTI_WINDOW);
verify(mTransaction, never()).setWindowingMode(any(), anyInt());
+ }
+ @Test
+ public void testUpdateAnimationParams() {
+ final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+
+ // Verify the default.
+ assertTrue(container.areLastRequestedAnimationParamsEqual(
+ TaskFragmentAnimationParams.DEFAULT));
+
+ final int bgColor = Color.GREEN;
+ final TaskFragmentAnimationParams animationParams =
+ new TaskFragmentAnimationParams.Builder()
+ .setAnimationBackgroundColor(bgColor)
+ .build();
+ mPresenter.updateAnimationParams(mTransaction, container.getTaskFragmentToken(),
+ animationParams);
+
+ final TaskFragmentOperation expectedOperation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_ANIMATION_PARAMS)
+ .setAnimationParams(animationParams)
+ .build();
+ verify(mTransaction).setTaskFragmentOperation(container.getTaskFragmentToken(),
+ expectedOperation);
+ assertTrue(container.areLastRequestedAnimationParamsEqual(animationParams));
+
+ // No request to set the same animation params.
+ clearInvocations(mTransaction);
+ mPresenter.updateAnimationParams(mTransaction, container.getTaskFragmentToken(),
+ animationParams);
+
+ verify(mTransaction, never()).setTaskFragmentOperation(any(), any());
}
@Test
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index be541ae..689cae5 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.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizerToken;
@@ -304,6 +305,10 @@
@Nullable
private final IBinder mFragmentToken;
+ /** The animation override params for animation running on this TaskFragment. */
+ @NonNull
+ private TaskFragmentAnimationParams mAnimationParams = TaskFragmentAnimationParams.DEFAULT;
+
/**
* The bounds of the embedded TaskFragment relative to the parent Task.
* {@code null} if it is not {@link #mIsEmbedded}
@@ -454,6 +459,15 @@
&& organizer.asBinder().equals(mTaskFragmentOrganizer.asBinder());
}
+ void setAnimationParams(@NonNull TaskFragmentAnimationParams animationParams) {
+ mAnimationParams = animationParams;
+ }
+
+ @NonNull
+ TaskFragmentAnimationParams getAnimationParams() {
+ return mAnimationParams;
+ }
+
TaskFragment getAdjacentTaskFragment() {
return mAdjacentTaskFragment;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index eba3ea4..3187337 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -43,6 +44,7 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_SHORTCUT;
@@ -88,7 +90,9 @@
import android.window.ITransitionPlayer;
import android.window.IWindowContainerTransactionCallback;
import android.window.IWindowOrganizerController;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
+import android.window.TaskFragmentOperation;
import android.window.WindowContainerTransaction;
import com.android.internal.annotations.VisibleForTesting;
@@ -1142,6 +1146,10 @@
fragment.setCompanionTaskFragment(companion);
break;
}
+ case HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION: {
+ effects |= applyTaskFragmentOperation(hop, errorCallbackToken, organizer);
+ break;
+ }
default: {
// The other operations may change task order so they are skipped while in lock
// task mode. The above operations are still allowed because they don't move
@@ -1274,6 +1282,47 @@
return effects;
}
+ /** Applies change set through {@link WindowContainerTransaction#setTaskFragmentOperation}. */
+ private int applyTaskFragmentOperation(@NonNull WindowContainerTransaction.HierarchyOp hop,
+ @Nullable IBinder errorCallbackToken, @Nullable ITaskFragmentOrganizer organizer) {
+ final IBinder fragmentToken = hop.getContainer();
+ final TaskFragment taskFragment = mLaunchTaskFragments.get(fragmentToken);
+ final TaskFragmentOperation operation = hop.getTaskFragmentOperation();
+ if (operation == null) {
+ final Throwable exception = new IllegalArgumentException(
+ "TaskFragmentOperation must be non-null");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION, exception);
+ return 0;
+ }
+ final int opType = operation.getOpType();
+ if (taskFragment == null || !taskFragment.isAttached()) {
+ final Throwable exception = new IllegalArgumentException(
+ "Not allowed to apply operation on invalid fragment tokens opType=" + opType);
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION, exception);
+ return 0;
+ }
+
+ int effect = 0;
+ switch (opType) {
+ case OP_TYPE_SET_ANIMATION_PARAMS: {
+ final TaskFragmentAnimationParams animationParams = operation.getAnimationParams();
+ if (animationParams == null) {
+ final Throwable exception = new IllegalArgumentException(
+ "TaskFragmentAnimationParams must be non-null");
+ sendTaskFragmentOperationFailure(organizer, errorCallbackToken, taskFragment,
+ HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION, exception);
+ break;
+ }
+ taskFragment.setAnimationParams(animationParams);
+ break;
+ }
+ // TODO(b/263436063): move other TaskFragment related operation here.
+ }
+ return effect;
+ }
+
/** A helper method to send minimum dimension violation error to the client. */
private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
IBinder errorCallbackToken, String reason) {
@@ -1681,6 +1730,7 @@
break;
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT:
+ case HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION:
enforceTaskFragmentOrganized(func, hop.getContainer(), organizer);
break;
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 140051d..2420efc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.getTransitionType;
@@ -66,6 +67,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -76,8 +78,10 @@
import android.view.RemoteAnimationDefinition;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentOperation;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken;
import android.window.TaskFragmentParentInfo;
@@ -676,6 +680,57 @@
}
@Test
+ public void testApplyTransaction_enforceTaskFragmentOrganized_setTaskFragmentOperation() {
+ final Task task = createTask(mDisplayContent);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setFragmentToken(mFragmentToken)
+ .build();
+ mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_ANIMATION_PARAMS)
+ .setAnimationParams(TaskFragmentAnimationParams.DEFAULT)
+ .build();
+ mTransaction.setTaskFragmentOperation(mFragmentToken, operation);
+ mOrganizer.applyTransaction(mTransaction);
+
+ // Not allowed because TaskFragment is not organized by the caller organizer.
+ assertApplyTransactionDisallowed(mTransaction);
+
+ mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
+ "Test:TaskFragmentOrganizer" /* processName */);
+
+ assertApplyTransactionAllowed(mTransaction);
+ }
+
+ @Test
+ public void testSetTaskFragmentOperation() {
+ final Task task = createTask(mDisplayContent);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .setOrganizer(mOrganizer)
+ .setFragmentToken(mFragmentToken)
+ .build();
+ assertEquals(TaskFragmentAnimationParams.DEFAULT, mTaskFragment.getAnimationParams());
+
+ mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
+ final TaskFragmentAnimationParams animationParams =
+ new TaskFragmentAnimationParams.Builder()
+ .setAnimationBackgroundColor(Color.GREEN)
+ .build();
+ final TaskFragmentOperation operation = new TaskFragmentOperation.Builder(
+ OP_TYPE_SET_ANIMATION_PARAMS)
+ .setAnimationParams(animationParams)
+ .build();
+ mTransaction.setTaskFragmentOperation(mFragmentToken, operation);
+ mOrganizer.applyTransaction(mTransaction);
+ assertApplyTransactionAllowed(mTransaction);
+
+ assertEquals(animationParams, mTaskFragment.getAnimationParams());
+ assertEquals(Color.GREEN, mTaskFragment.getAnimationParams().getAnimationBackgroundColor());
+ }
+
+ @Test
public void testApplyTransaction_createTaskFragment_failForDifferentUid() {
final ActivityRecord activity = createActivityRecord(mDisplayContent);
final int uid = Binder.getCallingUid();