diff --git a/core/api/current.txt b/core/api/current.txt index 95d84c824dfce..627e568396d94 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4319,6 +4319,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); @@ -4487,6 +4488,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(); @@ -4588,6 +4591,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 e277b49ee508f..0b43eb5419f7b 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -846,6 +846,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; @@ -861,6 +864,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( @@ -871,6 +883,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); @@ -946,6 +979,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); @@ -957,6 +995,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 @@ -997,5 +1037,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 81ee9cd3cbbbb..828848b0d5cf1 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() { @@ -10352,6 +10356,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 43dbcc886d59a..216544a010557 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1881,6 +1881,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 @@ -1888,14 +1894,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. *