From 5aedd1250799c5e81f530c1d321ecbcfb4031155 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Wed, 21 Dec 2022 19:39:30 +0800 Subject: [PATCH] Introduce new API overrideActivityTransition(1/N) ...which should be able to replace overridePendingTransition. This provide an API set to customize activity animation, and it can be called at any time while the activity still alive. 1. If an Activity has calls overrideActivityTransition, and it also set another activity animation in Window#windowAnimations, system will choose the animation which set from overrideActivityTransition. 2. The animation set from overridePendingTransition still have highest priority when system is looking for next transition animation, but by calling the new API overrideActivityTransition before activity lifecycle change, system will support to play the predict-back animation if the app has opt-in enableOnBackInvokedCallback. Bug: 259427810 Test: atest ActivityTransitionTests Change-Id: I33be7f7b0ee1122fd29f9f3bd6f023bb00b00ad8 --- core/api/current.txt | 5 + core/java/android/app/Activity.java | 140 ++++++++++++++++++ core/java/android/app/ActivityClient.java | 18 +++ .../app/IActivityClientController.aidl | 3 + core/java/android/window/TransitionInfo.java | 103 +++++++++++++ .../transition/TransitionAnimationHelper.java | 40 ++++- .../server/wm/ActivityClientController.java | 25 ++++ .../com/android/server/wm/ActivityRecord.java | 39 +++++ .../com/android/server/wm/AppTransition.java | 58 +++++++- .../com/android/server/wm/Transition.java | 31 +++- 10 files changed, 453 insertions(+), 9 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 75f57271dacc9..fcf265068539d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4260,6 +4260,7 @@ package android.app { @UiContext public class Activity extends android.view.ContextThemeWrapper implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback android.view.LayoutInflater.Factory2 android.view.View.OnCreateContextMenuListener android.view.Window.Callback { ctor public Activity(); method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams); + method public void clearOverrideActivityTransition(int); method public void closeContextMenu(); method public void closeOptionsMenu(); method public android.app.PendingIntent createPendingResult(int, @NonNull android.content.Intent, int); @@ -4428,6 +4429,8 @@ package android.app { method @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int); method public void openContextMenu(android.view.View); method public void openOptionsMenu(); + method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int); + method public void overrideActivityTransition(int, @AnimRes int, @AnimRes int, @ColorInt int); method public void overridePendingTransition(int, int); method public void overridePendingTransition(int, int, int); method public void postponeEnterTransition(); @@ -4529,6 +4532,8 @@ package android.app { field protected static final int[] FOCUSED_STATE_SET; field public static final int FULLSCREEN_MODE_REQUEST_ENTER = 1; // 0x1 field public static final int FULLSCREEN_MODE_REQUEST_EXIT = 0; // 0x0 + field public static final int OVERRIDE_TRANSITION_CLOSE = 1; // 0x1 + field public static final int OVERRIDE_TRANSITION_OPEN = 0; // 0x0 field public static final int RESULT_CANCELED = 0; // 0x0 field public static final int RESULT_FIRST_USER = 1; // 0x1 field public static final int RESULT_OK = -1; // 0xffffffff diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3c17a3375acb2..1d03d84860ea6 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -26,8 +26,10 @@ import static android.os.Process.myUid; import static java.lang.Character.MIN_VALUE; +import android.annotation.AnimRes; import android.annotation.CallSuper; import android.annotation.CallbackExecutor; +import android.annotation.ColorInt; import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.IntDef; @@ -108,6 +110,7 @@ import android.util.AttributeSet; import android.util.Dumpable; import android.util.EventLog; import android.util.Log; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; @@ -1006,6 +1009,25 @@ public class Activity extends ContextThemeWrapper */ public static final int FULLSCREEN_MODE_REQUEST_ENTER = 1; + /** @hide */ + @IntDef(prefix = { "OVERRIDE_TRANSITION_" }, value = { + OVERRIDE_TRANSITION_OPEN, + OVERRIDE_TRANSITION_CLOSE + }) + public @interface OverrideTransition {} + + /** + * Request type of {@link #overrideActivityTransition(int, int, int)} or + * {@link #overrideActivityTransition(int, int, int, int)}, to override the + * opening transition. + */ + public static final int OVERRIDE_TRANSITION_OPEN = 0; + /** + * Request type of {@link #overrideActivityTransition(int, int, int)} or + * {@link #overrideActivityTransition(int, int, int, int)}, to override the + * closing transition. + */ + public static final int OVERRIDE_TRANSITION_CLOSE = 1; private boolean mShouldDockBigOverlays; private UiTranslationController mUiTranslationController; @@ -6461,6 +6483,124 @@ public class Activity extends ContextThemeWrapper flagsMask, flagsValues, options); } + /** + * Customizes the animation for the activity transition with this activity. This can be called + * at any time while the activity still alive. + * + *

This is a more robust method of overriding the transition animation at runtime without + * relying on {@link #overridePendingTransition(int, int)} which doesn't work for predictive + * back. However, the animation set from {@link #overridePendingTransition(int, int)} still + * has higher priority when the system is looking for the next transition animation.

+ *

The animations resources set by this method will be chosen if and only if the activity is + * on top of the task while activity transitions are being played. + * For example, if we want to customize the opening transition when launching Activity B which + * gets started from Activity A, we should call this method inside B's onCreate with + * {@code overrideType = OVERRIDE_TRANSITION_OPEN} because the Activity B will on top of the + * task. And if we want to customize the closing transition when finishing Activity B and back + * to Activity A, since B is still is above A, we should call this method in Activity B with + * {@code overrideType = OVERRIDE_TRANSITION_CLOSE}.

+ * + *

If an Activity has called this method, and it also set another activity animation + * by {@link Window#setWindowAnimations(int)}, the system will choose the animation set from + * this method.

+ * + *

Note that {@link Window#setWindowAnimations}, + * {@link #overridePendingTransition(int, int)} and this method will be ignored if the Activity + * is started with {@link ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])}. Also + * note that this method can only be used to customize cross-activity transitions but not + * cross-task transitions which are fully non-customizable as of Android 11.

+ * + * @param overrideType {@code OVERRIDE_TRANSITION_OPEN} This animation will be used when + * starting/entering an activity. {@code OVERRIDE_TRANSITION_CLOSE} This + * animation will be used when finishing/closing an activity. + * @param enterAnim A resource ID of the animation resource to use for the incoming activity. + * Use 0 for no animation. + * @param exitAnim A resource ID of the animation resource to use for the outgoing activity. + * Use 0 for no animation. + * + * @see #overrideActivityTransition(int, int, int, int) + * @see #clearOverrideActivityTransition(int) + * @see OnBackInvokedCallback + * @see #overridePendingTransition(int, int) + * @see Window#setWindowAnimations(int) + * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[]) + */ + public void overrideActivityTransition(@OverrideTransition int overrideType, + @AnimRes int enterAnim, @AnimRes int exitAnim) { + overrideActivityTransition(overrideType, enterAnim, exitAnim, Color.TRANSPARENT); + } + + /** + * Customizes the animation for the activity transition with this activity. This can be called + * at any time while the activity still alive. + * + *

This is a more robust method of overriding the transition animation at runtime without + * relying on {@link #overridePendingTransition(int, int)} which doesn't work for predictive + * back. However, the animation set from {@link #overridePendingTransition(int, int)} still + * has higher priority when the system is looking for the next transition animation.

+ *

The animations resources set by this method will be chosen if and only if the activity is + * on top of the task while activity transitions are being played. + * For example, if we want to customize the opening transition when launching Activity B which + * gets started from Activity A, we should call this method inside B's onCreate with + * {@code overrideType = OVERRIDE_TRANSITION_OPEN} because the Activity B will on top of the + * task. And if we want to customize the closing transition when finishing Activity B and back + * to Activity A, since B is still is above A, we should call this method in Activity B with + * {@code overrideType = OVERRIDE_TRANSITION_CLOSE}.

+ * + *

If an Activity has called this method, and it also set another activity animation + * by {@link Window#setWindowAnimations(int)}, the system will choose the animation set from + * this method.

+ * + *

Note that {@link Window#setWindowAnimations}, + * {@link #overridePendingTransition(int, int)} and this method will be ignored if the Activity + * is started with {@link ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[])}. Also + * note that this method can only be used to customize cross-activity transitions but not + * cross-task transitions which are fully non-customizable as of Android 11.

+ * + * @param overrideType {@code OVERRIDE_TRANSITION_OPEN} This animation will be used when + * starting/entering an activity. {@code OVERRIDE_TRANSITION_CLOSE} This + * animation will be used when finishing/closing an activity. + * @param enterAnim A resource ID of the animation resource to use for the incoming activity. + * Use 0 for no animation. + * @param exitAnim A resource ID of the animation resource to use for the outgoing activity. + * Use 0 for no animation. + * @param backgroundColor The background color to use for the background during the animation + * if the animation requires a background. Set to + * {@link Color#TRANSPARENT} to not override the default color. + * @see #overrideActivityTransition(int, int, int) + * @see #clearOverrideActivityTransition(int) + * @see OnBackInvokedCallback + * @see #overridePendingTransition(int, int) + * @see Window#setWindowAnimations(int) + * @see ActivityOptions#makeSceneTransitionAnimation(Activity, Pair[]) + */ + public void overrideActivityTransition(@OverrideTransition int overrideType, + @AnimRes int enterAnim, @AnimRes int exitAnim, @ColorInt int backgroundColor) { + if (overrideType != OVERRIDE_TRANSITION_OPEN && overrideType != OVERRIDE_TRANSITION_CLOSE) { + throw new IllegalArgumentException("Override type must be either open or close"); + } + + ActivityClient.getInstance().overrideActivityTransition(mToken, + overrideType == OVERRIDE_TRANSITION_OPEN, enterAnim, exitAnim, backgroundColor); + } + + /** + * Clears the animations which are set from {@link #overrideActivityTransition}. + * @param overrideType {@code OVERRIDE_TRANSITION_OPEN} clear the animation set for starting a + * new activity. {@code OVERRIDE_TRANSITION_CLOSE} clear the animation set + * for finishing an activity. + * + * @see #overrideActivityTransition(int, int, int) + * @see #overrideActivityTransition(int, int, int, int) + */ + public void clearOverrideActivityTransition(@OverrideTransition int overrideType) { + if (overrideType != OVERRIDE_TRANSITION_OPEN && overrideType != OVERRIDE_TRANSITION_CLOSE) { + throw new IllegalArgumentException("Override type must be either open or close"); + } + ActivityClient.getInstance().clearOverrideActivityTransition(mToken, + overrideType == OVERRIDE_TRANSITION_OPEN); + } + /** * Call immediately after one of the flavors of {@link #startActivity(Intent)} * or {@link #finish} to specify an explicit transition animation to diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index aa868a768747f..5354a44ae9547 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -486,6 +486,24 @@ public class ActivityClient { } } + void overrideActivityTransition(IBinder token, boolean open, int enterAnim, int exitAnim, + int backgroundColor) { + try { + getActivityClientController().overrideActivityTransition( + token, open, enterAnim, exitAnim, backgroundColor); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + void clearOverrideActivityTransition(IBinder token, boolean open) { + try { + getActivityClientController().clearOverrideActivityTransition(token, open); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim, int backgroundColor) { try { diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 03646c693831e..5e40399867ad3 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -121,6 +121,9 @@ interface IActivityClientController { oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked); oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn); oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle); + oneway void overrideActivityTransition(IBinder token, boolean open, int enterAnim, int exitAnim, + int backgroundColor); + oneway void clearOverrideActivityTransition(IBinder token, boolean open); /** * Overrides the animation of activity pending transition. This call is not one-way because * the method is usually used after startActivity or Activity#finish. If this is non-blocking, diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index c8a69e274e819..60763be331493 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -845,6 +845,9 @@ public final class TransitionInfo implements Parcelable { 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; @@ -860,6 +863,15 @@ public final class TransitionInfo implements Parcelable { 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( @@ -870,6 +882,27 @@ public final class TransitionInfo implements Parcelable { 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); @@ -945,6 +978,11 @@ public final class TransitionInfo implements Parcelable { 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); @@ -956,6 +994,8 @@ public final class TransitionInfo implements Parcelable { mTransitionBounds.writeToParcel(dest, flags); dest.writeTypedObject(mThumbnail, flags); dest.writeInt(mAnimations); + dest.writeTypedObject(mCustomActivityOpenTransition, flags); + dest.writeTypedObject(mCustomActivityCloseTransition, flags); } @NonNull @@ -996,5 +1036,68 @@ public final class TransitionInfo implements Parcelable { return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}"; } + + /** 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 CREATOR = + new Creator() { + @Override + public CustomActivityTransition createFromParcel(Parcel in) { + return new CustomActivityTransition(in); + } + + @Override + public CustomActivityTransition[] newArray(int size) { + return new CustomActivityTransition[size]; + } + }; + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index 1549355c6b2b2..5a5ceab383e75 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -142,9 +142,12 @@ public class TransitionAnimationHelper { Animation a = null; if (animAttr != 0) { if (overrideType == ANIM_FROM_STYLE && !isTask) { - a = transitionAnimation - .loadAnimationAttr(options.getPackageName(), options.getAnimations(), - animAttr, translucent); + a = loadCustomActivityTransition(animAttr, options, enter, transitionAnimation); + if (a == null) { + a = transitionAnimation + .loadAnimationAttr(options.getPackageName(), options.getAnimations(), + animAttr, translucent); + } } else { a = transitionAnimation.loadDefaultAnimationAttr(animAttr, translucent); } @@ -157,6 +160,37 @@ public class TransitionAnimationHelper { return a; } + static Animation loadCustomActivityTransition(int animAttr, + TransitionInfo.AnimationOptions options, boolean enter, + TransitionAnimation transitionAnimation) { + Animation a = null; + boolean isOpen = false; + switch (animAttr) { + case R.styleable.WindowAnimation_activityOpenEnterAnimation: + case R.styleable.WindowAnimation_activityOpenExitAnimation: + isOpen = true; + break; + case R.styleable.WindowAnimation_activityCloseEnterAnimation: + case R.styleable.WindowAnimation_activityCloseExitAnimation: + break; + default: + return null; + } + + final TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim = + options.getCustomActivityTransition(isOpen); + if (transitionAnim != null) { + a = transitionAnimation.loadAppTransitionAnimation(options.getPackageName(), + enter ? transitionAnim.getCustomEnterResId() + : transitionAnim.getCustomExitResId()); + if (a != null && transitionAnim.getCustomBackgroundColor() != 0) { + a.setBackdropColor(transitionAnim.getCustomBackgroundColor()); + } + } + + return a; + } + /** * Gets the background {@link ColorInt} for the given transition animation if it is set. * diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 502bfd1a5d67b..7a9598797485d 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -1381,6 +1381,31 @@ class ActivityClientController extends IActivityClientController.Stub { } } + @Override + public void overrideActivityTransition(IBinder token, boolean open, int enterAnim, int exitAnim, + int backgroundColor) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); + if (r != null) { + r.overrideCustomTransition(open, enterAnim, exitAnim, backgroundColor); + } + } + Binder.restoreCallingIdentity(origId); + } + + @Override + public void clearOverrideActivityTransition(IBinder token, boolean open) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); + if (r != null) { + r.clearCustomTransition(open); + } + } + Binder.restoreCallingIdentity(origId); + } + @Override public void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim, @ColorInt int backgroundColor) { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2b9364c2f6923..8033bcc072509 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -945,6 +945,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Whether the ActivityEmbedding is enabled on the app. private final boolean mAppActivityEmbeddingSplitsEnabled; + // Records whether client has overridden the WindowAnimation_(Open/Close)(Enter/Exit)Animation. + private CustomAppTransition mCustomOpenTransition; + private CustomAppTransition mCustomCloseTransition; + private final Runnable mPauseTimeoutRunnable = new Runnable() { @Override public void run() { @@ -10341,6 +10345,41 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && !inPinnedWindowingMode() && !inFreeformWindowingMode(); } + void overrideCustomTransition(boolean open, int enterAnim, int exitAnim, int backgroundColor) { + CustomAppTransition transition = getCustomAnimation(open); + if (transition == null) { + transition = new CustomAppTransition(); + if (open) { + mCustomOpenTransition = transition; + } else { + mCustomCloseTransition = transition; + } + } + + transition.mEnterAnim = enterAnim; + transition.mExitAnim = exitAnim; + transition.mBackgroundColor = backgroundColor; + } + + void clearCustomTransition(boolean open) { + if (open) { + mCustomOpenTransition = null; + } else { + mCustomCloseTransition = null; + } + } + + CustomAppTransition getCustomAnimation(boolean open) { + return open ? mCustomOpenTransition : mCustomCloseTransition; + } + + // Override the WindowAnimation_(Open/Close)(Enter/Exit)Animation + static class CustomAppTransition { + int mEnterAnim; + int mExitAnim; + int mBackgroundColor; + } + static class Builder { private final ActivityTaskManagerService mAtmService; private WindowProcessController mCallerApp; diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index b9a4ed874a691..f73c68a42ec75 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -142,6 +142,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.DumpUtils.Dump; import com.android.internal.util.function.pooled.PooledLambda; import com.android.internal.util.function.pooled.PooledPredicate; +import com.android.server.wm.ActivityRecord.CustomAppTransition; import java.io.PrintWriter; import java.util.ArrayList; @@ -886,9 +887,18 @@ public class AppTransition implements Dump { a, appTransitionOldToString(transit), enter, Debug.getCallers(3)); } else { int animAttr = mapOpenCloseTransitTypes(transit, enter); - a = animAttr == 0 ? null : (canCustomizeAppTransition - ? loadAnimationAttr(lp, animAttr, transit) - : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit)); + if (animAttr != 0) { + a = loadCustomActivityAnimation(animAttr, enter, container); + if (a == null) { + if (canCustomizeAppTransition) { + a = loadAnimationAttr(lp, animAttr, transit); + } else { + a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit); + } + } + } else { + a = null; + } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b " @@ -901,6 +911,48 @@ public class AppTransition implements Dump { return a; } + Animation loadCustomActivityAnimation(int animAttr, boolean enter, WindowContainer container) { + ActivityRecord customAnimationSource = container.asActivityRecord(); + if (customAnimationSource == null) { + return null; + } + + // Only top activity can customize activity animation. + // If the animation is for the one below, try to get from the above activity. + if (animAttr == WindowAnimation_activityOpenExitAnimation + || animAttr == WindowAnimation_activityCloseEnterAnimation) { + customAnimationSource = customAnimationSource.getTask() + .getActivityAbove(customAnimationSource); + if (customAnimationSource == null) { + return null; + } + } + final CustomAppTransition custom; + switch (animAttr) { + case WindowAnimation_activityOpenEnterAnimation: + case WindowAnimation_activityOpenExitAnimation: + custom = customAnimationSource.getCustomAnimation(true /* open */); + break; + case WindowAnimation_activityCloseEnterAnimation: + case WindowAnimation_activityCloseExitAnimation: + custom = customAnimationSource.getCustomAnimation(false /* open */); + break; + default: + return null; + } + if (custom != null) { + final Animation a = mTransitionAnimation.loadAppTransitionAnimation( + customAnimationSource.packageName, enter + ? custom.mEnterAnim : custom.mExitAnim); + if (a != null && custom.mBackgroundColor != 0) { + a.setBackdropColor(custom.mBackgroundColor); + a.setShowBackdrop(true); + } + return a; + } + return null; + } + int getAppRootTaskClipMode() { return mNextAppTransitionRequests.contains(TRANSIT_RELAUNCH) || mNextAppTransitionRequests.contains(TRANSIT_KEYGUARD_GOING_AWAY) diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 71bb99cbf5154..7d6ac9b24b548 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1876,6 +1876,12 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { out.addChange(change); } + TransitionInfo.AnimationOptions animOptions = null; + if (topApp.asActivityRecord() != null) { + final ActivityRecord topActivity = topApp.asActivityRecord(); + animOptions = addCustomActivityTransition(topActivity, true/* open */, null); + animOptions = addCustomActivityTransition(topActivity, false/* open */, animOptions); + } final WindowManager.LayoutParams animLp = getLayoutParamsForAnimationsStyle(type, sortedTargets); if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING @@ -1883,14 +1889,33 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Don't send animation options if no windowAnimations have been set or if the we are // running an app starting animation, in which case we don't want the app to be able to // change its animation directly. - TransitionInfo.AnimationOptions animOptions = - TransitionInfo.AnimationOptions.makeAnimOptionsFromLayoutParameters(animLp); + if (animOptions != null) { + animOptions.addOptionsFromLayoutParameters(animLp); + } else { + animOptions = TransitionInfo.AnimationOptions + .makeAnimOptionsFromLayoutParameters(animLp); + } + } + if (animOptions != null) { out.setAnimationOptions(animOptions); } - return out; } + static TransitionInfo.AnimationOptions addCustomActivityTransition(ActivityRecord topActivity, + boolean open, TransitionInfo.AnimationOptions animOptions) { + final ActivityRecord.CustomAppTransition customAnim = + topActivity.getCustomAnimation(open); + if (customAnim != null) { + if (animOptions == null) { + animOptions = TransitionInfo.AnimationOptions + .makeCommonAnimOptions(topActivity.packageName); + } + animOptions.addCustomActivityTransition(open, customAnim.mEnterAnim, + customAnim.mExitAnim, customAnim.mBackgroundColor); + } + return animOptions; + } /** * Finds the top-most common ancestor of app targets. *