Merge "Pass TaskFragmentAnimationParams to TaskFragment" into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2023-01-05 07:52:02 +00:00
committed by Android (Google) Code Review
13 changed files with 640 additions and 7 deletions

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -762,6 +762,30 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
/**
* 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)}.
@@ -1262,6 +1286,7 @@ public final class WindowContainerTransaction implements Parcelable {
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 @@ public final class WindowContainerTransaction implements Parcelable {
@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 @@ public final class WindowContainerTransaction implements Parcelable {
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 @@ public final class WindowContainerTransaction implements Parcelable {
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();
@@ -1528,6 +1559,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mTaskFragmentCreationOptions;
}
@Nullable
public TaskFragmentOperation getTaskFragmentOperation() {
return mTaskFragmentOperation;
}
@Nullable
public PendingIntent getPendingIntent() {
return mPendingIntent;
@@ -1604,6 +1640,9 @@ public final class WindowContainerTransaction implements Parcelable {
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 @@ public final class WindowContainerTransaction implements Parcelable {
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);
@@ -1687,6 +1727,9 @@ public final class WindowContainerTransaction implements Parcelable {
@Nullable
private TaskFragmentCreationParams mTaskFragmentCreationOptions;
@Nullable
private TaskFragmentOperation mTaskFragmentOperation;
@Nullable
private PendingIntent mPendingIntent;
@@ -1767,6 +1810,12 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
Builder setTaskFragmentOperation(
@Nullable TaskFragmentOperation taskFragmentOperation) {
mTaskFragmentOperation = taskFragmentOperation;
return this;
}
Builder setReparentLeafTaskIfRelaunch(boolean reparentLeafTaskIfRelaunch) {
mReparentLeafTaskIfRelaunch = reparentLeafTaskIfRelaunch;
return this;
@@ -1796,6 +1845,7 @@ public final class WindowContainerTransaction implements Parcelable {
hierarchyOp.mPendingIntent = mPendingIntent;
hierarchyOp.mAlwaysOnTop = mAlwaysOnTop;
hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions;
hierarchyOp.mTaskFragmentOperation = mTaskFragmentOperation;
hierarchyOp.mShortcutInfo = mShortcutInfo;
hierarchyOp.mReparentLeafTaskIfRelaunch = mReparentLeafTaskIfRelaunch;

View File

@@ -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.graphics.Rect;
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 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
* @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 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
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 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
.setPairedPrimaryFragmentToken(launchingFragmentToken)
.build();
createTaskFragment(wct, fragmentOptions);
updateAnimationParams(wct, secondaryFragmentToken, splitAttributes);
wct.startActivityInTaskFragment(secondaryFragmentToken, ownerToken, activityIntent,
activityOptions);
@@ -163,6 +169,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
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 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
createTaskFragmentAndReparentActivity(
wct, fragmentToken, activity.getActivityToken(), new Rect(),
WINDOWING_MODE_UNDEFINED, activity);
updateAnimationParams(wct, fragmentToken, TaskFragmentAnimationParams.DEFAULT);
}
/**
@@ -270,6 +278,24 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
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 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
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();
}
}

View File

@@ -65,6 +65,7 @@ import android.util.Pair;
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 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
taskId);
mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
mPresenter.updateAnimationParams(wct, expandedContainer.getTaskFragmentToken(),
TaskFragmentAnimationParams.DEFAULT);
return expandedContainer;
}

View File

@@ -36,6 +36,7 @@ import android.util.Size;
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
*/
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
.getWindowingModeForSplitTaskFragment(bounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
return container;
}
@@ -314,7 +318,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
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.

View File

@@ -26,6 +26,7 @@ import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
import android.util.Size;
import android.window.TaskFragmentAnimationParams;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -107,6 +108,13 @@ class TaskFragmentContainer {
@WindowingMode
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 @@ class TaskFragmentContainer {
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();

View File

@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
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.Intent;
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 @@ public class SplitPresenterTest {
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

View File

@@ -94,6 +94,7 @@ import android.view.DisplayInfo;
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 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@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 @@ class TaskFragment extends WindowContainer<WindowContainer> {
&& organizer.asBinder().equals(mTaskFragmentOrganizer.asBinder());
}
void setAnimationParams(@NonNull TaskFragmentAnimationParams animationParams) {
mAnimationParams = animationParams;
}
@NonNull
TaskFragmentAnimationParams getAnimationParams() {
return mAnimationParams;
}
TaskFragment getAdjacentTaskFragment() {
return mAdjacentTaskFragment;
}

View File

@@ -21,6 +21,7 @@ import static android.app.ActivityManager.isStartResultSuccessful;
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
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.ITransitionMetricsReporter;
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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
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:

View File

@@ -19,6 +19,7 @@ package com.android.server.wm;
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.ComponentName;
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.platform.test.annotations.Presubmit;
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;
@@ -675,6 +679,57 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertApplyTransactionAllowed(mTransaction);
}
@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);