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