Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5aedd125 authored by wilsonshih's avatar wilsonshih
Browse files

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
parent d491ea05
Loading
Loading
Loading
Loading
+5 −0
Original line number Original line Diff line number Diff line
@@ -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 {
  @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();
    ctor public Activity();
    method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
    method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
    method public void clearOverrideActivityTransition(int);
    method public void closeContextMenu();
    method public void closeContextMenu();
    method public void closeOptionsMenu();
    method public void closeOptionsMenu();
    method public android.app.PendingIntent createPendingResult(int, @NonNull android.content.Intent, int);
    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 @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
    method public void openContextMenu(android.view.View);
    method public void openContextMenu(android.view.View);
    method public void openOptionsMenu();
    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);
    method public void overridePendingTransition(int, int, int);
    method public void overridePendingTransition(int, int, int);
    method public void postponeEnterTransition();
    method public void postponeEnterTransition();
@@ -4529,6 +4532,8 @@ package android.app {
    field protected static final int[] FOCUSED_STATE_SET;
    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_ENTER = 1; // 0x1
    field public static final int FULLSCREEN_MODE_REQUEST_EXIT = 0; // 0x0
    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_CANCELED = 0; // 0x0
    field public static final int RESULT_FIRST_USER = 1; // 0x1
    field public static final int RESULT_FIRST_USER = 1; // 0x1
    field public static final int RESULT_OK = -1; // 0xffffffff
    field public static final int RESULT_OK = -1; // 0xffffffff
+140 −0
Original line number Original line Diff line number Diff line
@@ -26,8 +26,10 @@ import static android.os.Process.myUid;


import static java.lang.Character.MIN_VALUE;
import static java.lang.Character.MIN_VALUE;


import android.annotation.AnimRes;
import android.annotation.CallSuper;
import android.annotation.CallSuper;
import android.annotation.CallbackExecutor;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.IntDef;
@@ -108,6 +110,7 @@ import android.util.AttributeSet;
import android.util.Dumpable;
import android.util.Dumpable;
import android.util.EventLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;
@@ -1006,6 +1009,25 @@ public class Activity extends ContextThemeWrapper
     */
     */
    public static final int FULLSCREEN_MODE_REQUEST_ENTER = 1;
    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 boolean mShouldDockBigOverlays;


    private UiTranslationController mUiTranslationController;
    private UiTranslationController mUiTranslationController;
@@ -6461,6 +6483,124 @@ public class Activity extends ContextThemeWrapper
                flagsMask, flagsValues, options);
                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.
     *
     * <p> 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.</p>
     * <p> 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}. </p>
     *
     * <p> 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.</p>
     *
     * <p> 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.</p>
     *
     * @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.
     *
     * <p> 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.</p>
     * <p> 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}. </p>
     *
     * <p> 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.</p>
     *
     * <p> 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.</p>
     *
     * @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)}
     * Call immediately after one of the flavors of {@link #startActivity(Intent)}
     * or {@link #finish} to specify an explicit transition animation to
     * or {@link #finish} to specify an explicit transition animation to
+18 −0
Original line number Original line Diff line number Diff line
@@ -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,
    void overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim,
            int backgroundColor) {
            int backgroundColor) {
        try {
        try {
+3 −0
Original line number Original line Diff line number Diff line
@@ -121,6 +121,9 @@ interface IActivityClientController {
    oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
    oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
    oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
    oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
    oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
    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
     * 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,
     * the method is usually used after startActivity or Activity#finish. If this is non-blocking,
+103 −0
Original line number Original line Diff line number Diff line
@@ -845,6 +845,9 @@ public final class TransitionInfo implements Parcelable {
        private HardwareBuffer mThumbnail;
        private HardwareBuffer mThumbnail;
        private int mAnimations;
        private int mAnimations;
        private @ColorInt int mBackgroundColor;
        private @ColorInt int mBackgroundColor;
        // Customize activity transition animation
        private CustomActivityTransition mCustomActivityOpenTransition;
        private CustomActivityTransition mCustomActivityCloseTransition;


        private AnimationOptions(int type) {
        private AnimationOptions(int type) {
            mType = type;
            mType = type;
@@ -860,6 +863,15 @@ public final class TransitionInfo implements Parcelable {
            mTransitionBounds.readFromParcel(in);
            mTransitionBounds.readFromParcel(in);
            mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
            mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
            mAnimations = in.readInt();
            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(
        public static AnimationOptions makeAnimOptionsFromLayoutParameters(
@@ -870,6 +882,27 @@ public final class TransitionInfo implements Parcelable {
            return options;
            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,
        public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
                int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) {
                int exitResId, @ColorInt int backgroundColor, boolean overrideTaskTransition) {
            AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
            AnimationOptions options = new AnimationOptions(ANIM_CUSTOM);
@@ -945,6 +978,11 @@ public final class TransitionInfo implements Parcelable {
            return mAnimations;
            return mAnimations;
        }
        }


        /** Return customized activity transition if existed. */
        public CustomActivityTransition getCustomActivityTransition(boolean open) {
            return open ? mCustomActivityOpenTransition : mCustomActivityCloseTransition;
        }

        @Override
        @Override
        public void writeToParcel(Parcel dest, int flags) {
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mType);
            dest.writeInt(mType);
@@ -956,6 +994,8 @@ public final class TransitionInfo implements Parcelable {
            mTransitionBounds.writeToParcel(dest, flags);
            mTransitionBounds.writeToParcel(dest, flags);
            dest.writeTypedObject(mThumbnail, flags);
            dest.writeTypedObject(mThumbnail, flags);
            dest.writeInt(mAnimations);
            dest.writeInt(mAnimations);
            dest.writeTypedObject(mCustomActivityOpenTransition, flags);
            dest.writeTypedObject(mCustomActivityCloseTransition, flags);
        }
        }


        @NonNull
        @NonNull
@@ -996,5 +1036,68 @@ public final class TransitionInfo implements Parcelable {
            return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName
            return "{ AnimationOptions type= " + typeToString(mType) + " package=" + mPackageName
                    + " override=" + mOverrideTaskTransition + " b=" + mTransitionBounds + "}";
                    + " 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<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];
                        }
                    };
        }
    }
    }
}
}
Loading