|  | /* | 
|  | * Copyright (C) 2020 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 static android.app.ActivityOptions.ANIM_CLIP_REVEAL; | 
|  | import static android.app.ActivityOptions.ANIM_CUSTOM; | 
|  | import static android.app.ActivityOptions.ANIM_FROM_STYLE; | 
|  | import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; | 
|  | import static android.app.ActivityOptions.ANIM_SCALE_UP; | 
|  | import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; | 
|  | import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; | 
|  | import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; | 
|  | import static android.app.WindowConfiguration.ROTATION_UNDEFINED; | 
|  | import static android.view.Display.INVALID_DISPLAY; | 
|  | import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; | 
|  | import static android.view.WindowManager.TRANSIT_CHANGE; | 
|  | import static android.view.WindowManager.TRANSIT_CLOSE; | 
|  | import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_APPEARING; | 
|  | import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; | 
|  | import static android.view.WindowManager.TRANSIT_NONE; | 
|  | import static android.view.WindowManager.TRANSIT_OPEN; | 
|  | import static android.view.WindowManager.TRANSIT_TO_BACK; | 
|  | import static android.view.WindowManager.TRANSIT_TO_FRONT; | 
|  | import static android.view.WindowManager.TransitionFlags; | 
|  | import static android.view.WindowManager.TransitionType; | 
|  | import static android.view.WindowManager.transitTypeToString; | 
|  |  | 
|  | import android.annotation.ColorInt; | 
|  | import android.annotation.IntDef; | 
|  | import android.annotation.NonNull; | 
|  | import android.annotation.Nullable; | 
|  | import android.app.ActivityManager; | 
|  | import android.content.ComponentName; | 
|  | import android.graphics.Point; | 
|  | import android.graphics.Rect; | 
|  | import android.hardware.HardwareBuffer; | 
|  | import android.os.Parcel; | 
|  | import android.os.Parcelable; | 
|  | import android.view.Surface; | 
|  | import android.view.SurfaceControl; | 
|  | import android.view.WindowManager; | 
|  |  | 
|  | import java.util.ArrayList; | 
|  | import java.util.List; | 
|  | import java.util.Objects; | 
|  |  | 
|  | /** | 
|  | * Used to communicate information about what is changing during a transition to a TransitionPlayer. | 
|  | * @hide | 
|  | */ | 
|  | public final class TransitionInfo implements Parcelable { | 
|  | private static final String TAG = "TransitionInfo"; | 
|  |  | 
|  | /** | 
|  | * Modes are only a sub-set of all the transit-types since they are per-container | 
|  | * @hide | 
|  | */ | 
|  | @IntDef(prefix = { "TRANSIT_" }, value = { | 
|  | TRANSIT_NONE, | 
|  | TRANSIT_OPEN, | 
|  | TRANSIT_CLOSE, | 
|  | // Note: to_front/to_back really mean show/hide respectively at the container level. | 
|  | TRANSIT_TO_FRONT, | 
|  | TRANSIT_TO_BACK, | 
|  | TRANSIT_CHANGE | 
|  | }) | 
|  | public @interface TransitionMode {} | 
|  |  | 
|  | /** No flags */ | 
|  | public static final int FLAG_NONE = 0; | 
|  |  | 
|  | /** The container shows the wallpaper behind it. */ | 
|  | public static final int FLAG_SHOW_WALLPAPER = 1; | 
|  |  | 
|  | /** The container IS the wallpaper. */ | 
|  | public static final int FLAG_IS_WALLPAPER = 1 << 1; | 
|  |  | 
|  | /** The container is translucent. */ | 
|  | public static final int FLAG_TRANSLUCENT = 1 << 2; | 
|  |  | 
|  | // TODO: remove when starting-window is moved to Task | 
|  | /** The container is the recipient of a transferred starting-window */ | 
|  | public static final int FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT = 1 << 3; | 
|  |  | 
|  | /** The container has voice session. */ | 
|  | public static final int FLAG_IS_VOICE_INTERACTION = 1 << 4; | 
|  |  | 
|  | /** The container is the display. */ | 
|  | public static final int FLAG_IS_DISPLAY = 1 << 5; | 
|  |  | 
|  | /** | 
|  | * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is | 
|  | * used to prevent seamless rotation. | 
|  | * TODO(b/194540864): Once we can include all windows in transition, then replace this with | 
|  | *         something like FLAG_IS_SYSTEM_ALERT instead. Then we can do mixed rotations. | 
|  | */ | 
|  | public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7; | 
|  |  | 
|  | /** The container is an input-method window. */ | 
|  | public static final int FLAG_IS_INPUT_METHOD = 1 << 8; | 
|  |  | 
|  | /** The container is in a Task with embedded activity. */ | 
|  | public static final int FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY = 1 << 9; | 
|  |  | 
|  | /** The container fills its parent Task before and after the transition. */ | 
|  | public static final int FLAG_FILLS_TASK = 1 << 10; | 
|  |  | 
|  | /** The container is going to show IME on its task after the transition. */ | 
|  | public static final int FLAG_WILL_IME_SHOWN = 1 << 11; | 
|  |  | 
|  | /** The container attaches owner profile thumbnail for cross profile animation. */ | 
|  | public static final int FLAG_CROSS_PROFILE_OWNER_THUMBNAIL = 1 << 12; | 
|  |  | 
|  | /** The container attaches work profile thumbnail for cross profile animation. */ | 
|  | public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13; | 
|  |  | 
|  | /** | 
|  | * Whether the window is covered by an app starting window. This is different from | 
|  | * {@link #FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT} which is only set on the Activity window | 
|  | * that contains the starting window. | 
|  | */ | 
|  | public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14; | 
|  |  | 
|  | /** This change happened underneath something else. */ | 
|  | public static final int FLAG_IS_OCCLUDED = 1 << 15; | 
|  |  | 
|  | /** The container is a system window, excluding wallpaper and input-method. */ | 
|  | public static final int FLAG_IS_SYSTEM_WINDOW = 1 << 16; | 
|  |  | 
|  | /** The window was animated by back gesture. */ | 
|  | public static final int FLAG_BACK_GESTURE_ANIMATED = 1 << 17; | 
|  |  | 
|  | /** The window should have no animation (by policy). */ | 
|  | public static final int FLAG_NO_ANIMATION = 1 << 18; | 
|  |  | 
|  | /** The task is launching behind home. */ | 
|  | public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19; | 
|  |  | 
|  | /** The task became the top-most task even if it didn't change visibility. */ | 
|  | public static final int FLAG_MOVED_TO_TOP = 1 << 20; | 
|  |  | 
|  | /** | 
|  | * This transition must be the only transition when it starts (ie. it must wait for all other | 
|  | * transition animations to finish). | 
|  | */ | 
|  | public static final int FLAG_SYNC = 1 << 21; | 
|  |  | 
|  | /** This change represents its start configuration for the duration of the animation. */ | 
|  | public static final int FLAG_CONFIG_AT_END = 1 << 22; | 
|  |  | 
|  | /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ | 
|  | public static final int FLAG_FIRST_CUSTOM = 1 << 23; | 
|  |  | 
|  | /** The change belongs to a window that won't contain activities. */ | 
|  | public static final int FLAGS_IS_NON_APP_WINDOW = | 
|  | FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD | FLAG_IS_SYSTEM_WINDOW; | 
|  |  | 
|  | /** The change will not participate in the animation. */ | 
|  | public static final int FLAGS_IS_OCCLUDED_NO_ANIMATION = FLAG_IS_OCCLUDED | FLAG_NO_ANIMATION; | 
|  |  | 
|  | /** @hide */ | 
|  | @IntDef(prefix = { "FLAG_" }, value = { | 
|  | FLAG_NONE, | 
|  | FLAG_SHOW_WALLPAPER, | 
|  | FLAG_IS_WALLPAPER, | 
|  | FLAG_TRANSLUCENT, | 
|  | FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT, | 
|  | FLAG_IS_VOICE_INTERACTION, | 
|  | FLAG_IS_DISPLAY, | 
|  | FLAG_DISPLAY_HAS_ALERT_WINDOWS, | 
|  | FLAG_IS_INPUT_METHOD, | 
|  | FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY, | 
|  | FLAG_FILLS_TASK, | 
|  | FLAG_WILL_IME_SHOWN, | 
|  | FLAG_CROSS_PROFILE_OWNER_THUMBNAIL, | 
|  | FLAG_CROSS_PROFILE_WORK_THUMBNAIL, | 
|  | FLAG_IS_BEHIND_STARTING_WINDOW, | 
|  | FLAG_IS_OCCLUDED, | 
|  | FLAG_IS_SYSTEM_WINDOW, | 
|  | FLAG_BACK_GESTURE_ANIMATED, | 
|  | FLAG_NO_ANIMATION, | 
|  | FLAG_TASK_LAUNCHING_BEHIND, | 
|  | FLAG_MOVED_TO_TOP, | 
|  | FLAG_SYNC, | 
|  | FLAG_CONFIG_AT_END, | 
|  | FLAG_FIRST_CUSTOM | 
|  | }) | 
|  | public @interface ChangeFlags {} | 
|  |  | 
|  | private final @TransitionType int mType; | 
|  | private @TransitionFlags int mFlags; | 
|  | private int mTrack = 0; | 
|  | private final ArrayList<Change> mChanges = new ArrayList<>(); | 
|  | private final ArrayList<Root> mRoots = new ArrayList<>(); | 
|  |  | 
|  | private AnimationOptions mOptions; | 
|  |  | 
|  | /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */ | 
|  | private int mDebugId = -1; | 
|  |  | 
|  | /** @hide */ | 
|  | public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) { | 
|  | mType = type; | 
|  | mFlags = flags; | 
|  | } | 
|  |  | 
|  | private TransitionInfo(Parcel in) { | 
|  | mType = in.readInt(); | 
|  | mFlags = in.readInt(); | 
|  | in.readTypedList(mChanges, Change.CREATOR); | 
|  | in.readTypedList(mRoots, Root.CREATOR); | 
|  | mOptions = in.readTypedObject(AnimationOptions.CREATOR); | 
|  | mDebugId = in.readInt(); | 
|  | mTrack = in.readInt(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | /** @hide */ | 
|  | public void writeToParcel(@NonNull Parcel dest, int flags) { | 
|  | dest.writeInt(mType); | 
|  | dest.writeInt(mFlags); | 
|  | dest.writeTypedList(mChanges); | 
|  | dest.writeTypedList(mRoots, flags); | 
|  | dest.writeTypedObject(mOptions, flags); | 
|  | dest.writeInt(mDebugId); | 
|  | dest.writeInt(mTrack); | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public static final Creator<TransitionInfo> CREATOR = | 
|  | new Creator<TransitionInfo>() { | 
|  | @Override | 
|  | public TransitionInfo createFromParcel(Parcel in) { | 
|  | return new TransitionInfo(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public TransitionInfo[] newArray(int size) { | 
|  | return new TransitionInfo[size]; | 
|  | } | 
|  | }; | 
|  |  | 
|  | @Override | 
|  | /** @hide */ | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** @see #getRoot */ | 
|  | public void addRootLeash(int displayId, @NonNull SurfaceControl leash, | 
|  | int offsetLeft, int offsetTop) { | 
|  | mRoots.add(new Root(displayId, leash, offsetLeft, offsetTop)); | 
|  | } | 
|  |  | 
|  | /** @see #getRoot */ | 
|  | public void addRoot(Root other) { | 
|  | mRoots.add(other); | 
|  | } | 
|  |  | 
|  | public void setAnimationOptions(AnimationOptions options) { | 
|  | mOptions = options; | 
|  | } | 
|  |  | 
|  | public @TransitionType int getType() { | 
|  | return mType; | 
|  | } | 
|  |  | 
|  | public void setFlags(int flags) { | 
|  | mFlags = flags; | 
|  | } | 
|  |  | 
|  | public int getFlags() { | 
|  | return mFlags; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return The number of animation roots. Most transitions should have 1, but there may be more | 
|  | *         in some cases (such as a transition spanning multiple displays). | 
|  | */ | 
|  | public int getRootCount() { | 
|  | return mRoots.size(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the transition-root at a specific index. | 
|  | */ | 
|  | @NonNull | 
|  | public Root getRoot(int idx) { | 
|  | return mRoots.get(idx); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the index of the transition-root associated with `displayId` or -1 if not found. | 
|  | */ | 
|  | public int findRootIndex(int displayId) { | 
|  | for (int i = 0; i < mRoots.size(); ++i) { | 
|  | if (mRoots.get(i).mDisplayId == displayId) { | 
|  | return i; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return a surfacecontrol that can serve as a parent surfacecontrol for all the changing | 
|  | * participants to animate within. This will generally be placed at the highest-z-order | 
|  | * shared ancestor of all participants. While this is non-null, it's possible for the rootleash | 
|  | * to be invalid if the transition is a no-op. | 
|  | * | 
|  | * @deprecated Use {@link #getRoot} instead. This call assumes there is only one root. | 
|  | */ | 
|  | @Deprecated | 
|  | @NonNull | 
|  | public SurfaceControl getRootLeash() { | 
|  | if (mRoots.isEmpty()) { | 
|  | throw new IllegalStateException("Trying to get a root leash from a no-op transition."); | 
|  | } | 
|  | if (mRoots.size() > 1) { | 
|  | android.util.Log.e(TAG, "Assuming one animation root when there are more.", | 
|  | new Throwable()); | 
|  | } | 
|  | return mRoots.get(0).mLeash; | 
|  | } | 
|  |  | 
|  | public AnimationOptions getAnimationOptions() { | 
|  | return mOptions; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom | 
|  | *         in Z (meaning index 0 is the top-most container). | 
|  | */ | 
|  | @NonNull | 
|  | public List<Change> getChanges() { | 
|  | return mChanges; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the Change that a window is undergoing or {@code null} if not directly | 
|  | * represented. | 
|  | */ | 
|  | @Nullable | 
|  | public Change getChange(@NonNull WindowContainerToken token) { | 
|  | for (int i = mChanges.size() - 1; i >= 0; --i) { | 
|  | if (token.equals(mChanges.get(i).mContainer)) { | 
|  | return mChanges.get(i); | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Add a {@link Change} to this transition. | 
|  | */ | 
|  | public void addChange(@NonNull Change change) { | 
|  | mChanges.add(change); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Whether this transition contains any changes to the window hierarchy, | 
|  | * including keyguard visibility. | 
|  | */ | 
|  | public boolean hasChangesOrSideEffects() { | 
|  | return !mChanges.isEmpty() || isKeyguardGoingAway() | 
|  | || (mFlags & TRANSIT_FLAG_KEYGUARD_APPEARING) != 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Whether this transition includes keyguard going away. | 
|  | */ | 
|  | public boolean isKeyguardGoingAway() { | 
|  | return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0; | 
|  | } | 
|  |  | 
|  | /** Gets which animation track this transition should run on. */ | 
|  | public int getTrack() { | 
|  | return mTrack; | 
|  | } | 
|  |  | 
|  | /** Sets which animation track this transition should run on. */ | 
|  | public void setTrack(int track) { | 
|  | mTrack = track; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set an arbitrary "debug" id for this info. This id will not be used for any "real work", | 
|  | * it is just for debugging and logging. | 
|  | */ | 
|  | public void setDebugId(int id) { | 
|  | mDebugId = id; | 
|  | } | 
|  |  | 
|  | /** Get the "debug" id of this info. Do NOT use this for real work, only use for debugging. */ | 
|  | public int getDebugId() { | 
|  | return mDebugId; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return toString(""); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a string representation of this transition info. | 
|  | * @hide | 
|  | */ | 
|  | public String toString(@NonNull String prefix) { | 
|  | final boolean shouldPrettyPrint = !prefix.isEmpty() && !mChanges.isEmpty(); | 
|  | final String innerPrefix = shouldPrettyPrint ? prefix + "    " : ""; | 
|  | final String changesLineStart = shouldPrettyPrint ? "\n" + prefix : ""; | 
|  | final String perChangeLineStart = shouldPrettyPrint ? "\n" + innerPrefix : ""; | 
|  | StringBuilder sb = new StringBuilder(); | 
|  | sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType)) | 
|  | .append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack); | 
|  | if (mOptions != null) { | 
|  | sb.append(" opt=").append(mOptions); | 
|  | } | 
|  | sb.append(" r=["); | 
|  | for (int i = 0; i < mRoots.size(); ++i) { | 
|  | if (i > 0) { | 
|  | sb.append(','); | 
|  | } | 
|  | sb.append(mRoots.get(i).mDisplayId).append("@").append(mRoots.get(i).mOffset); | 
|  | } | 
|  | sb.append("] c=["); | 
|  | sb.append(perChangeLineStart); | 
|  | for (int i = 0; i < mChanges.size(); ++i) { | 
|  | if (i > 0) { | 
|  | sb.append(','); | 
|  | sb.append(perChangeLineStart); | 
|  | } | 
|  | sb.append(mChanges.get(i)); | 
|  | } | 
|  | sb.append(changesLineStart); | 
|  | sb.append("]}"); | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | /** Converts a transition mode/action to its string representation. */ | 
|  | @NonNull | 
|  | public static String modeToString(@TransitionMode int mode) { | 
|  | switch(mode) { | 
|  | case TRANSIT_NONE: return "NONE"; | 
|  | case TRANSIT_OPEN: return "OPEN"; | 
|  | case TRANSIT_CLOSE: return "CLOSE"; | 
|  | case TRANSIT_TO_FRONT: return "TO_FRONT"; | 
|  | case TRANSIT_TO_BACK: return "TO_BACK"; | 
|  | case TRANSIT_CHANGE: return "CHANGE"; | 
|  | default: return "<unknown:" + mode + ">"; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Converts change flags into a string representation. */ | 
|  | @NonNull | 
|  | public static String flagsToString(@ChangeFlags int flags) { | 
|  | if (flags == 0) return "NONE"; | 
|  | final StringBuilder sb = new StringBuilder(); | 
|  | if ((flags & FLAG_SHOW_WALLPAPER) != 0) { | 
|  | sb.append("SHOW_WALLPAPER"); | 
|  | } | 
|  | if ((flags & FLAG_IS_WALLPAPER) != 0) { | 
|  | sb.append("IS_WALLPAPER"); | 
|  | } | 
|  | if ((flags & FLAG_IS_INPUT_METHOD) != 0) { | 
|  | sb.append("IS_INPUT_METHOD"); | 
|  | } | 
|  | if ((flags & FLAG_TRANSLUCENT) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("TRANSLUCENT"); | 
|  | } | 
|  | if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("STARTING_WINDOW_TRANSFER"); | 
|  | } | 
|  | if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("IS_VOICE_INTERACTION"); | 
|  | } | 
|  | if ((flags & FLAG_IS_DISPLAY) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("IS_DISPLAY"); | 
|  | } | 
|  | if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("DISPLAY_HAS_ALERT_WINDOWS"); | 
|  | } | 
|  | if ((flags & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("IN_TASK_WITH_EMBEDDED_ACTIVITY"); | 
|  | } | 
|  | if ((flags & FLAG_FILLS_TASK) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK"); | 
|  | } | 
|  | if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW"); | 
|  | } | 
|  | if ((flags & FLAG_IS_OCCLUDED) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("IS_OCCLUDED"); | 
|  | } | 
|  | if ((flags & FLAG_IS_SYSTEM_WINDOW) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("FLAG_IS_SYSTEM_WINDOW"); | 
|  | } | 
|  | if ((flags & FLAG_BACK_GESTURE_ANIMATED) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("FLAG_BACK_GESTURE_ANIMATED"); | 
|  | } | 
|  | if ((flags & FLAG_NO_ANIMATION) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("NO_ANIMATION"); | 
|  | } | 
|  | if ((flags & FLAG_TASK_LAUNCHING_BEHIND) != 0) { | 
|  | sb.append((sb.length() == 0 ? "" : "|") + "TASK_LAUNCHING_BEHIND"); | 
|  | } | 
|  | if ((flags & FLAG_SYNC) != 0) { | 
|  | sb.append((sb.length() == 0 ? "" : "|") + "SYNC"); | 
|  | } | 
|  | if ((flags & FLAG_FIRST_CUSTOM) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); | 
|  | } | 
|  | if ((flags & FLAG_CONFIG_AT_END) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("CONFIG_AT_END"); | 
|  | } | 
|  | if ((flags & FLAG_MOVED_TO_TOP) != 0) { | 
|  | sb.append(sb.length() == 0 ? "" : "|").append("MOVE_TO_TOP"); | 
|  | } | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Indication that `change` is independent of parents (ie. it has a different type of | 
|  | * transition vs. "going along for the ride") | 
|  | */ | 
|  | public static boolean isIndependent(@NonNull TransitionInfo.Change change, | 
|  | @NonNull TransitionInfo info) { | 
|  | // If the change has no parent (it is root), then it is independent | 
|  | if (change.getParent() == null) return true; | 
|  |  | 
|  | if (change.getLastParent() != null && !change.getLastParent().equals(change.getParent())) { | 
|  | // If the change has been reparented, then it's independent. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // non-visibility changes will just be folded into the parent change, so they aren't | 
|  | // independent either. | 
|  | if (change.getMode() == TRANSIT_CHANGE) return false; | 
|  |  | 
|  | // Always fold the activity embedding change into the parent change. | 
|  | if (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) return false; | 
|  |  | 
|  | TransitionInfo.Change parentChg = info.getChange(change.getParent()); | 
|  | while (parentChg != null) { | 
|  | // If the parent is a visibility change, it will include the results of all child | 
|  | // changes into itself, so none of its children can be independent. | 
|  | if (parentChg.getMode() != TRANSIT_CHANGE) return false; | 
|  |  | 
|  | // If there are no more parents left, then all the parents, so far, have not been | 
|  | // visibility changes which means this change is independent. | 
|  | if (parentChg.getParent() == null) return true; | 
|  |  | 
|  | parentChg = info.getChange(parentChg.getParent()); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Releases temporary-for-animation surfaces referenced by this to potentially free up memory. | 
|  | * This includes root-leash and snapshots. | 
|  | */ | 
|  | public void releaseAnimSurfaces() { | 
|  | for (int i = mChanges.size() - 1; i >= 0; --i) { | 
|  | final Change c = mChanges.get(i); | 
|  | if (c.mSnapshot != null) { | 
|  | c.mSnapshot.release(); | 
|  | c.mSnapshot = null; | 
|  | } | 
|  | } | 
|  | for (int i = 0; i < mRoots.size(); ++i) { | 
|  | mRoots.get(i).mLeash.release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Releases ALL the surfaces referenced by this to potentially free up memory. Do NOT use this | 
|  | * if the surface-controls get stored and used elsewhere in the process. To just release | 
|  | * temporary-for-animation surfaces, use {@link #releaseAnimSurfaces}. | 
|  | */ | 
|  | public void releaseAllSurfaces() { | 
|  | releaseAnimSurfaces(); | 
|  | for (int i = mChanges.size() - 1; i >= 0; --i) { | 
|  | mChanges.get(i).getLeash().release(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Updates the callsites of all the surfaces in this transition, which aids in the debugging of | 
|  | * lingering surfaces. | 
|  | */ | 
|  | public void setUnreleasedWarningCallSiteForAllSurfaces(String callsite) { | 
|  | for (int i = mChanges.size() - 1; i >= 0; --i) { | 
|  | mChanges.get(i).getLeash().setUnreleasedWarningCallSite(callsite); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol | 
|  | * refcounts are incremented which allows the "remote" receiver to release them without breaking | 
|  | * the caller's references. Use this only if you need to "send" this to a local function which | 
|  | * assumes it is being called from a remote caller. | 
|  | */ | 
|  | public TransitionInfo localRemoteCopy() { | 
|  | final TransitionInfo out = new TransitionInfo(mType, mFlags); | 
|  | out.mTrack = mTrack; | 
|  | out.mDebugId = mDebugId; | 
|  | for (int i = 0; i < mChanges.size(); ++i) { | 
|  | out.mChanges.add(mChanges.get(i).localRemoteCopy()); | 
|  | } | 
|  | for (int i = 0; i < mRoots.size(); ++i) { | 
|  | out.mRoots.add(mRoots.get(i).localRemoteCopy()); | 
|  | } | 
|  | // Doesn't have any native stuff, so no need for actual copy | 
|  | out.mOptions = mOptions; | 
|  | return out; | 
|  | } | 
|  |  | 
|  | /** Represents the change a WindowContainer undergoes during a transition */ | 
|  | public static final class Change implements Parcelable { | 
|  | private final WindowContainerToken mContainer; | 
|  | private WindowContainerToken mParent; | 
|  | private WindowContainerToken mLastParent; | 
|  | private SurfaceControl mLeash; | 
|  | private @TransitionMode int mMode = TRANSIT_NONE; | 
|  | private @ChangeFlags int mFlags = FLAG_NONE; | 
|  | private final Rect mStartAbsBounds = new Rect(); | 
|  | private final Rect mEndAbsBounds = new Rect(); | 
|  | private final Point mEndRelOffset = new Point(); | 
|  | private ActivityManager.RunningTaskInfo mTaskInfo = null; | 
|  | private boolean mAllowEnterPip; | 
|  | private int mStartDisplayId = INVALID_DISPLAY; | 
|  | private int mEndDisplayId = INVALID_DISPLAY; | 
|  | private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED; | 
|  | private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED; | 
|  | /** | 
|  | * The end rotation of the top activity after fixed rotation is finished. If the top | 
|  | * activity is not in fixed rotation, it will be {@link ROTATION_UNDEFINED}. | 
|  | */ | 
|  | private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED; | 
|  | private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED; | 
|  | private @ColorInt int mBackgroundColor; | 
|  | private SurfaceControl mSnapshot = null; | 
|  | private float mSnapshotLuma; | 
|  | private ComponentName mActivityComponent = null; | 
|  |  | 
|  | public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) { | 
|  | mContainer = container; | 
|  | mLeash = leash; | 
|  | } | 
|  |  | 
|  | private Change(Parcel in) { | 
|  | mContainer = in.readTypedObject(WindowContainerToken.CREATOR); | 
|  | mParent = in.readTypedObject(WindowContainerToken.CREATOR); | 
|  | mLastParent = in.readTypedObject(WindowContainerToken.CREATOR); | 
|  | mLeash = new SurfaceControl(); | 
|  | mLeash.readFromParcel(in); | 
|  | mMode = in.readInt(); | 
|  | mFlags = in.readInt(); | 
|  | mStartAbsBounds.readFromParcel(in); | 
|  | mEndAbsBounds.readFromParcel(in); | 
|  | mEndRelOffset.readFromParcel(in); | 
|  | mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR); | 
|  | mAllowEnterPip = in.readBoolean(); | 
|  | mStartDisplayId = in.readInt(); | 
|  | mEndDisplayId = in.readInt(); | 
|  | mStartRotation = in.readInt(); | 
|  | mEndRotation = in.readInt(); | 
|  | mEndFixedRotation = in.readInt(); | 
|  | mRotationAnimation = in.readInt(); | 
|  | mBackgroundColor = in.readInt(); | 
|  | mSnapshot = in.readTypedObject(SurfaceControl.CREATOR); | 
|  | mSnapshotLuma = in.readFloat(); | 
|  | mActivityComponent = in.readTypedObject(ComponentName.CREATOR); | 
|  | } | 
|  |  | 
|  | private Change localRemoteCopy() { | 
|  | final Change out = new Change(mContainer, new SurfaceControl(mLeash, "localRemote")); | 
|  | out.mParent = mParent; | 
|  | out.mLastParent = mLastParent; | 
|  | out.mMode = mMode; | 
|  | out.mFlags = mFlags; | 
|  | out.mStartAbsBounds.set(mStartAbsBounds); | 
|  | out.mEndAbsBounds.set(mEndAbsBounds); | 
|  | out.mEndRelOffset.set(mEndRelOffset); | 
|  | out.mTaskInfo = mTaskInfo; | 
|  | out.mAllowEnterPip = mAllowEnterPip; | 
|  | out.mStartDisplayId = mStartDisplayId; | 
|  | out.mEndDisplayId = mEndDisplayId; | 
|  | out.mStartRotation = mStartRotation; | 
|  | out.mEndRotation = mEndRotation; | 
|  | out.mEndFixedRotation = mEndFixedRotation; | 
|  | out.mRotationAnimation = mRotationAnimation; | 
|  | out.mBackgroundColor = mBackgroundColor; | 
|  | out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null; | 
|  | out.mSnapshotLuma = mSnapshotLuma; | 
|  | out.mActivityComponent = mActivityComponent; | 
|  | return out; | 
|  | } | 
|  |  | 
|  | /** Sets the parent of this change's container. The parent must be a participant or null. */ | 
|  | public void setParent(@Nullable WindowContainerToken parent) { | 
|  | mParent = parent; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the parent of this change's container before the transition if this change's | 
|  | * container is reparented in the transition. | 
|  | */ | 
|  | public void setLastParent(@Nullable WindowContainerToken lastParent) { | 
|  | mLastParent = lastParent; | 
|  | } | 
|  |  | 
|  | /** Sets the animation leash for controlling this change's container */ | 
|  | public void setLeash(@NonNull SurfaceControl leash) { | 
|  | mLeash = Objects.requireNonNull(leash); | 
|  | } | 
|  |  | 
|  | /** Sets the transition mode for this change */ | 
|  | public void setMode(@TransitionMode int mode) { | 
|  | mMode = mode; | 
|  | } | 
|  |  | 
|  | /** Sets the flags for this change */ | 
|  | public void setFlags(@ChangeFlags int flags) { | 
|  | mFlags = flags; | 
|  | } | 
|  |  | 
|  | /** Sets the bounds this container occupied before the change in screen space */ | 
|  | public void setStartAbsBounds(@Nullable Rect rect) { | 
|  | mStartAbsBounds.set(rect); | 
|  | } | 
|  |  | 
|  | /** Sets the bounds this container will occupy after the change in screen space */ | 
|  | public void setEndAbsBounds(@Nullable Rect rect) { | 
|  | mEndAbsBounds.set(rect); | 
|  | } | 
|  |  | 
|  | /** Sets the offset of this container from its parent surface */ | 
|  | public void setEndRelOffset(int left, int top) { | 
|  | mEndRelOffset.set(left, top); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the taskinfo of this container if this is a task. WARNING: this takes the | 
|  | * reference, so don't modify it afterwards. | 
|  | */ | 
|  | public void setTaskInfo(@Nullable ActivityManager.RunningTaskInfo taskInfo) { | 
|  | mTaskInfo = taskInfo; | 
|  | } | 
|  |  | 
|  | /** Sets the allowEnterPip flag which represents AppOpsManager check on PiP permission */ | 
|  | public void setAllowEnterPip(boolean allowEnterPip) { | 
|  | mAllowEnterPip = allowEnterPip; | 
|  | } | 
|  |  | 
|  | /** Sets the start and end rotation of this container. */ | 
|  | public void setDisplayId(int start, int end) { | 
|  | mStartDisplayId = start; | 
|  | mEndDisplayId = end; | 
|  | } | 
|  |  | 
|  | /** Sets the start and end rotation of this container. */ | 
|  | public void setRotation(@Surface.Rotation int start, @Surface.Rotation int end) { | 
|  | mStartRotation = start; | 
|  | mEndRotation = end; | 
|  | } | 
|  |  | 
|  | /** Sets end rotation that top activity will be launched to after fixed rotation. */ | 
|  | public void setEndFixedRotation(@Surface.Rotation int endFixedRotation) { | 
|  | mEndFixedRotation = endFixedRotation; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Sets the app-requested animation type for rotation. Will be one of the | 
|  | * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams}; | 
|  | */ | 
|  | public void setRotationAnimation(int anim) { | 
|  | mRotationAnimation = anim; | 
|  | } | 
|  |  | 
|  | /** Sets the background color of this change's container. */ | 
|  | public void setBackgroundColor(@ColorInt int backgroundColor) { | 
|  | mBackgroundColor = backgroundColor; | 
|  | } | 
|  |  | 
|  | /** Sets a snapshot surface for the "start" state of the container. */ | 
|  | public void setSnapshot(@Nullable SurfaceControl snapshot, float luma) { | 
|  | mSnapshot = snapshot; | 
|  | mSnapshotLuma = luma; | 
|  | } | 
|  |  | 
|  | /** Sets the component-name of the container. Container must be an Activity. */ | 
|  | public void setActivityComponent(@Nullable ComponentName component) { | 
|  | mActivityComponent = component; | 
|  | } | 
|  |  | 
|  | /** @return the container that is changing. May be null if non-remotable (eg. activity) */ | 
|  | @Nullable | 
|  | public WindowContainerToken getContainer() { | 
|  | return mContainer; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the parent of the changing container. This is the parent within the participants, | 
|  | * not necessarily the actual parent. | 
|  | */ | 
|  | @Nullable | 
|  | public WindowContainerToken getParent() { | 
|  | return mParent; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the parent of the changing container before the transition if it is reparented | 
|  | * in the transition. The parent window may not be collected in the transition as a | 
|  | * participant, and it may have been detached from the display. {@code null} if the changing | 
|  | * container has not been reparented in the transition, or if the parent is not organizable. | 
|  | */ | 
|  | @Nullable | 
|  | public WindowContainerToken getLastParent() { | 
|  | return mLastParent; | 
|  | } | 
|  |  | 
|  | /** @return which action this change represents. */ | 
|  | public @TransitionMode int getMode() { | 
|  | return mMode; | 
|  | } | 
|  |  | 
|  | /** @return the flags for this change. */ | 
|  | public @ChangeFlags int getFlags() { | 
|  | return mFlags; | 
|  | } | 
|  |  | 
|  | /** Whether this change contains any of the given change flags. */ | 
|  | public boolean hasFlags(@ChangeFlags int flags) { | 
|  | return (mFlags & flags) != 0; | 
|  | } | 
|  |  | 
|  | /** Whether this change contains all of the given change flags. */ | 
|  | public boolean hasAllFlags(@ChangeFlags int flags) { | 
|  | return (mFlags & flags) == flags; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the bounds of the container before the change. It may be empty if the container | 
|  | * is coming into existence. | 
|  | */ | 
|  | @NonNull | 
|  | public Rect getStartAbsBounds() { | 
|  | return mStartAbsBounds; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the bounds of the container after the change. It may be empty if the container | 
|  | * is disappearing. | 
|  | */ | 
|  | @NonNull | 
|  | public Rect getEndAbsBounds() { | 
|  | return mEndAbsBounds; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * @return the offset of the container's surface from its parent surface after the change. | 
|  | */ | 
|  | @NonNull | 
|  | public Point getEndRelOffset() { | 
|  | return mEndRelOffset; | 
|  | } | 
|  |  | 
|  | /** @return the leash or surface to animate for this container */ | 
|  | @NonNull | 
|  | public SurfaceControl getLeash() { | 
|  | return mLeash; | 
|  | } | 
|  |  | 
|  | /** @return the task info or null if this isn't a task */ | 
|  | @Nullable | 
|  | public ActivityManager.RunningTaskInfo getTaskInfo() { | 
|  | return mTaskInfo; | 
|  | } | 
|  |  | 
|  | public boolean getAllowEnterPip() { | 
|  | return mAllowEnterPip; | 
|  | } | 
|  |  | 
|  | public int getStartDisplayId() { | 
|  | return mStartDisplayId; | 
|  | } | 
|  |  | 
|  | public int getEndDisplayId() { | 
|  | return mEndDisplayId; | 
|  | } | 
|  |  | 
|  | @Surface.Rotation | 
|  | public int getStartRotation() { | 
|  | return mStartRotation; | 
|  | } | 
|  |  | 
|  | @Surface.Rotation | 
|  | public int getEndRotation() { | 
|  | return mEndRotation; | 
|  | } | 
|  |  | 
|  | @Surface.Rotation | 
|  | public int getEndFixedRotation() { | 
|  | return mEndFixedRotation; | 
|  | } | 
|  |  | 
|  | /** @return the rotation animation. */ | 
|  | public int getRotationAnimation() { | 
|  | return mRotationAnimation; | 
|  | } | 
|  |  | 
|  | /** @return get the background color of this change's container. */ | 
|  | @ColorInt | 
|  | public int getBackgroundColor() { | 
|  | return mBackgroundColor; | 
|  | } | 
|  |  | 
|  | /** @return a snapshot surface (if applicable). */ | 
|  | @Nullable | 
|  | public SurfaceControl getSnapshot() { | 
|  | return mSnapshot; | 
|  | } | 
|  |  | 
|  | /** @return the luma calculated for the snapshot surface (if applicable). */ | 
|  | public float getSnapshotLuma() { | 
|  | return mSnapshotLuma; | 
|  | } | 
|  |  | 
|  | /** @return the component-name of this container (if it is an activity). */ | 
|  | @Nullable | 
|  | public ComponentName getActivityComponent() { | 
|  | return mActivityComponent; | 
|  | } | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | public void writeToParcel(@NonNull Parcel dest, int flags) { | 
|  | dest.writeTypedObject(mContainer, flags); | 
|  | dest.writeTypedObject(mParent, flags); | 
|  | dest.writeTypedObject(mLastParent, flags); | 
|  | mLeash.writeToParcel(dest, flags); | 
|  | dest.writeInt(mMode); | 
|  | dest.writeInt(mFlags); | 
|  | mStartAbsBounds.writeToParcel(dest, flags); | 
|  | mEndAbsBounds.writeToParcel(dest, flags); | 
|  | mEndRelOffset.writeToParcel(dest, flags); | 
|  | dest.writeTypedObject(mTaskInfo, flags); | 
|  | dest.writeBoolean(mAllowEnterPip); | 
|  | dest.writeInt(mStartDisplayId); | 
|  | dest.writeInt(mEndDisplayId); | 
|  | dest.writeInt(mStartRotation); | 
|  | dest.writeInt(mEndRotation); | 
|  | dest.writeInt(mEndFixedRotation); | 
|  | dest.writeInt(mRotationAnimation); | 
|  | dest.writeInt(mBackgroundColor); | 
|  | dest.writeTypedObject(mSnapshot, flags); | 
|  | dest.writeFloat(mSnapshotLuma); | 
|  | dest.writeTypedObject(mActivityComponent, flags); | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public static final Creator<Change> CREATOR = | 
|  | new Creator<Change>() { | 
|  | @Override | 
|  | public Change createFromParcel(Parcel in) { | 
|  | return new Change(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Change[] newArray(int size) { | 
|  | return new Change[size]; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | final StringBuilder sb = new StringBuilder(); | 
|  | sb.append('{'); sb.append(mContainer); | 
|  | sb.append(" m="); sb.append(modeToString(mMode)); | 
|  | sb.append(" f="); sb.append(flagsToString(mFlags)); | 
|  | if (mParent != null) { | 
|  | sb.append(" p="); sb.append(mParent); | 
|  | } | 
|  | if (mLeash != null) { | 
|  | sb.append(" leash="); sb.append(mLeash); | 
|  | } | 
|  | sb.append(" sb="); sb.append(mStartAbsBounds); | 
|  | sb.append(" eb="); sb.append(mEndAbsBounds); | 
|  | if (mEndRelOffset.x != 0 || mEndRelOffset.y != 0) { | 
|  | sb.append(" eo="); sb.append(mEndRelOffset); | 
|  | } | 
|  | sb.append(" d="); | 
|  | if (mStartDisplayId != mEndDisplayId) { | 
|  | sb.append(mStartDisplayId).append("->"); | 
|  | } | 
|  | sb.append(mEndDisplayId); | 
|  | if (mStartRotation != mEndRotation) { | 
|  | sb.append(" r="); sb.append(mStartRotation); | 
|  | sb.append("->"); sb.append(mEndRotation); | 
|  | sb.append(':'); sb.append(mRotationAnimation); | 
|  | } | 
|  | if (mEndFixedRotation != ROTATION_UNDEFINED) { | 
|  | sb.append(" endFixedRotation="); sb.append(mEndFixedRotation); | 
|  | } | 
|  | if (mSnapshot != null) { | 
|  | sb.append(" snapshot="); sb.append(mSnapshot); | 
|  | } | 
|  | if (mLastParent != null) { | 
|  | sb.append(" lastParent="); sb.append(mLastParent); | 
|  | } | 
|  | if (mActivityComponent != null) { | 
|  | sb.append(" component="); | 
|  | sb.append(mActivityComponent.flattenToShortString()); | 
|  | } | 
|  | if (mTaskInfo != null) { | 
|  | sb.append(" taskParent="); | 
|  | sb.append(mTaskInfo.parentTaskId); | 
|  | } | 
|  | sb.append('}'); | 
|  | return sb.toString(); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Represents animation options during a transition */ | 
|  | public static final class AnimationOptions implements Parcelable { | 
|  |  | 
|  | private int mType; | 
|  | private int mEnterResId; | 
|  | private int mExitResId; | 
|  | private boolean mOverrideTaskTransition; | 
|  | private String mPackageName; | 
|  | private final Rect mTransitionBounds = new Rect(); | 
|  | private HardwareBuffer mThumbnail; | 
|  | private int mAnimations; | 
|  | private @ColorInt int mBackgroundColor; | 
|  | // Customize activity transition animation | 
|  | private CustomActivityTransition mCustomActivityOpenTransition; | 
|  | private CustomActivityTransition mCustomActivityCloseTransition; | 
|  |  | 
|  | private AnimationOptions(int type) { | 
|  | mType = type; | 
|  | } | 
|  |  | 
|  | public AnimationOptions(Parcel in) { | 
|  | mType = in.readInt(); | 
|  | mEnterResId = in.readInt(); | 
|  | mExitResId = in.readInt(); | 
|  | mBackgroundColor = in.readInt(); | 
|  | mOverrideTaskTransition = in.readBoolean(); | 
|  | mPackageName = in.readString(); | 
|  | mTransitionBounds.readFromParcel(in); | 
|  | mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR); | 
|  | mAnimations = in.readInt(); | 
|  | mCustomActivityOpenTransition = in.readTypedObject(CustomActivityTransition.CREATOR); | 
|  | mCustomActivityCloseTransition = in.readTypedObject(CustomActivityTransition.CREATOR); | 
|  | } | 
|  |  | 
|  | /** Make basic customized animation for a package */ | 
|  | public static AnimationOptions makeCommonAnimOptions(String packageName) { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_FROM_STYLE); | 
|  | options.mPackageName = packageName; | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeAnimOptionsFromLayoutParameters( | 
|  | WindowManager.LayoutParams lp) { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_FROM_STYLE); | 
|  | options.mPackageName = lp.packageName; | 
|  | options.mAnimations = lp.windowAnimations; | 
|  | return options; | 
|  | } | 
|  |  | 
|  | /** Add customized window animations */ | 
|  | public void addOptionsFromLayoutParameters(WindowManager.LayoutParams lp) { | 
|  | mAnimations = lp.windowAnimations; | 
|  | } | 
|  |  | 
|  | /** Add customized activity animation attributes */ | 
|  | public void addCustomActivityTransition(boolean isOpen, | 
|  | int enterResId, int exitResId, int backgroundColor) { | 
|  | CustomActivityTransition customTransition = isOpen | 
|  | ? mCustomActivityOpenTransition : mCustomActivityCloseTransition; | 
|  | if (customTransition == null) { | 
|  | customTransition = new CustomActivityTransition(); | 
|  | if (isOpen) { | 
|  | mCustomActivityOpenTransition = customTransition; | 
|  | } else { | 
|  | mCustomActivityCloseTransition = customTransition; | 
|  | } | 
|  | } | 
|  | customTransition.addCustomActivityTransition(enterResId, exitResId, backgroundColor); | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId, | 
|  | int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_CUSTOM); | 
|  | options.mPackageName = packageName; | 
|  | options.mEnterResId = enterResId; | 
|  | options.mExitResId = exitResId; | 
|  | options.mBackgroundColor = backgroundColor; | 
|  | options.mOverrideTaskTransition = overrideTaskTransition; | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeClipRevealAnimOptions(int startX, int startY, int width, | 
|  | int height) { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_CLIP_REVEAL); | 
|  | options.mTransitionBounds.set(startX, startY, startX + width, startY + height); | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeScaleUpAnimOptions(int startX, int startY, int width, | 
|  | int height) { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_SCALE_UP); | 
|  | options.mTransitionBounds.set(startX, startY, startX + width, startY + height); | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeThumbnailAnimOptions(HardwareBuffer srcThumb, | 
|  | int startX, int startY, boolean scaleUp) { | 
|  | AnimationOptions options = new AnimationOptions( | 
|  | scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN); | 
|  | options.mTransitionBounds.set(startX, startY, startX, startY); | 
|  | options.mThumbnail = srcThumb; | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeCrossProfileAnimOptions() { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_OPEN_CROSS_PROFILE_APPS); | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public static AnimationOptions makeSceneTransitionAnimOptions() { | 
|  | AnimationOptions options = new AnimationOptions(ANIM_SCENE_TRANSITION); | 
|  | return options; | 
|  | } | 
|  |  | 
|  | public int getType() { | 
|  | return mType; | 
|  | } | 
|  |  | 
|  | public int getEnterResId() { | 
|  | return mEnterResId; | 
|  | } | 
|  |  | 
|  | public int getExitResId() { | 
|  | return mExitResId; | 
|  | } | 
|  |  | 
|  | public @ColorInt int getBackgroundColor() { | 
|  | return mBackgroundColor; | 
|  | } | 
|  |  | 
|  | public boolean getOverrideTaskTransition() { | 
|  | return mOverrideTaskTransition; | 
|  | } | 
|  |  | 
|  | public String getPackageName() { | 
|  | return mPackageName; | 
|  | } | 
|  |  | 
|  | public Rect getTransitionBounds() { | 
|  | return mTransitionBounds; | 
|  | } | 
|  |  | 
|  | public HardwareBuffer getThumbnail() { | 
|  | return mThumbnail; | 
|  | } | 
|  |  | 
|  | public int getAnimations() { | 
|  | return mAnimations; | 
|  | } | 
|  |  | 
|  | /** Return customized activity transition if existed. */ | 
|  | public CustomActivityTransition getCustomActivityTransition(boolean open) { | 
|  | return open ? mCustomActivityOpenTransition : mCustomActivityCloseTransition; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeToParcel(Parcel dest, int flags) { | 
|  | dest.writeInt(mType); | 
|  | dest.writeInt(mEnterResId); | 
|  | dest.writeInt(mExitResId); | 
|  | dest.writeInt(mBackgroundColor); | 
|  | dest.writeBoolean(mOverrideTaskTransition); | 
|  | dest.writeString(mPackageName); | 
|  | mTransitionBounds.writeToParcel(dest, flags); | 
|  | dest.writeTypedObject(mThumbnail, flags); | 
|  | dest.writeInt(mAnimations); | 
|  | dest.writeTypedObject(mCustomActivityOpenTransition, flags); | 
|  | dest.writeTypedObject(mCustomActivityCloseTransition, flags); | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public static final Creator<AnimationOptions> CREATOR = | 
|  | new Creator<AnimationOptions>() { | 
|  | @Override | 
|  | public AnimationOptions createFromParcel(Parcel in) { | 
|  | return new AnimationOptions(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AnimationOptions[] newArray(int size) { | 
|  | return new AnimationOptions[size]; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | private static String typeToString(int mode) { | 
|  | return switch (mode) { | 
|  | case ANIM_CUSTOM -> "CUSTOM"; | 
|  | case ANIM_SCALE_UP -> "SCALE_UP"; | 
|  | case ANIM_THUMBNAIL_SCALE_UP -> "THUMBNAIL_SCALE_UP"; | 
|  | case ANIM_THUMBNAIL_SCALE_DOWN -> "THUMBNAIL_SCALE_DOWN"; | 
|  | case ANIM_SCENE_TRANSITION -> "SCENE_TRANSITION"; | 
|  | case ANIM_CLIP_REVEAL -> "CLIP_REVEAL"; | 
|  | case ANIM_OPEN_CROSS_PROFILE_APPS -> "OPEN_CROSS_PROFILE_APPS"; | 
|  | case ANIM_FROM_STYLE -> "FROM_STYLE"; | 
|  | default -> "<" + mode + ">"; | 
|  | }; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | final StringBuilder sb = new StringBuilder(32); | 
|  | sb.append("{t=").append(typeToString(mType)); | 
|  | if (mOverrideTaskTransition) { | 
|  | sb.append(" overrideTask=true"); | 
|  | } | 
|  | if (!mTransitionBounds.isEmpty()) { | 
|  | sb.append(" bounds=").append(mTransitionBounds); | 
|  | } | 
|  | sb.append('}'); | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | /** Customized activity transition. */ | 
|  | public static class CustomActivityTransition implements Parcelable { | 
|  | private int mCustomEnterResId; | 
|  | private int mCustomExitResId; | 
|  | private int mCustomBackgroundColor; | 
|  |  | 
|  | /** Returns customize activity animation enter resource id */ | 
|  | public int getCustomEnterResId() { | 
|  | return mCustomEnterResId; | 
|  | } | 
|  |  | 
|  | /** Returns customize activity animation exit resource id */ | 
|  | public int getCustomExitResId() { | 
|  | return mCustomExitResId; | 
|  | } | 
|  |  | 
|  | /** Returns customize activity animation background color */ | 
|  | public int getCustomBackgroundColor() { | 
|  | return mCustomBackgroundColor; | 
|  | } | 
|  | CustomActivityTransition() {} | 
|  |  | 
|  | CustomActivityTransition(Parcel in) { | 
|  | mCustomEnterResId = in.readInt(); | 
|  | mCustomExitResId = in.readInt(); | 
|  | mCustomBackgroundColor = in.readInt(); | 
|  | } | 
|  |  | 
|  | /** Add customized activity animation attributes */ | 
|  | public void addCustomActivityTransition( | 
|  | int enterResId, int exitResId, int backgroundColor) { | 
|  | mCustomEnterResId = enterResId; | 
|  | mCustomExitResId = exitResId; | 
|  | mCustomBackgroundColor = backgroundColor; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeToParcel(Parcel dest, int flags) { | 
|  | dest.writeInt(mCustomEnterResId); | 
|  | dest.writeInt(mCustomExitResId); | 
|  | dest.writeInt(mCustomBackgroundColor); | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public static final Creator<CustomActivityTransition> CREATOR = | 
|  | new Creator<CustomActivityTransition>() { | 
|  | @Override | 
|  | public CustomActivityTransition createFromParcel(Parcel in) { | 
|  | return new CustomActivityTransition(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public CustomActivityTransition[] newArray(int size) { | 
|  | return new CustomActivityTransition[size]; | 
|  | } | 
|  | }; | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * An animation root in a transition. There is one of these for each display that contains | 
|  | * participants. It will be placed, in z-order, right above the top-most participant and at the | 
|  | * same position in the hierarchy. As a result, if all participants are animating within a | 
|  | * part of the screen, the root-leash will only be in that part of the screen. In these cases, | 
|  | * it's relative position (from the screen) is stored in {@link Root#getOffset}. | 
|  | */ | 
|  | public static final class Root implements Parcelable { | 
|  | private final int mDisplayId; | 
|  | private final SurfaceControl mLeash; | 
|  | private final Point mOffset = new Point(); | 
|  |  | 
|  | public Root(int displayId, @NonNull SurfaceControl leash, int offsetLeft, int offsetTop) { | 
|  | mDisplayId = displayId; | 
|  | mLeash = leash; | 
|  | mOffset.set(offsetLeft, offsetTop); | 
|  | } | 
|  |  | 
|  | private Root(Parcel in) { | 
|  | mDisplayId = in.readInt(); | 
|  | mLeash = new SurfaceControl(); | 
|  | mLeash.readFromParcel(in); | 
|  | mLeash.setUnreleasedWarningCallSite("TransitionInfo.Root"); | 
|  | mOffset.readFromParcel(in); | 
|  | } | 
|  |  | 
|  | private Root localRemoteCopy() { | 
|  | return new Root(mDisplayId, new SurfaceControl(mLeash, "localRemote"), | 
|  | mOffset.x, mOffset.y); | 
|  | } | 
|  |  | 
|  | /** @return the id of the display this root is on. */ | 
|  | public int getDisplayId() { | 
|  | return mDisplayId; | 
|  | } | 
|  |  | 
|  | /** @return the root's leash. Surfaces should be parented to this while animating. */ | 
|  | @NonNull | 
|  | public SurfaceControl getLeash() { | 
|  | return mLeash; | 
|  | } | 
|  |  | 
|  | /** @return the offset (relative to its screen) of the root leash. */ | 
|  | @NonNull | 
|  | public Point getOffset() { | 
|  | return mOffset; | 
|  | } | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | public void writeToParcel(@NonNull Parcel dest, int flags) { | 
|  | dest.writeInt(mDisplayId); | 
|  | mLeash.writeToParcel(dest, flags); | 
|  | mOffset.writeToParcel(dest, flags); | 
|  | } | 
|  |  | 
|  | @NonNull | 
|  | public static final Creator<Root> CREATOR = | 
|  | new Creator<Root>() { | 
|  | @Override | 
|  | public Root createFromParcel(Parcel in) { | 
|  | return new Root(in); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Root[] newArray(int size) { | 
|  | return new Root[size]; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /** @hide */ | 
|  | @Override | 
|  | public int describeContents() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return mDisplayId + "@" + mOffset + ":" + mLeash; | 
|  | } | 
|  | } | 
|  | } |