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

Commit a4843175 authored by Pablo Gamito's avatar Pablo Gamito
Browse files

Add support for WindowAnimationStyle theme attribute in shell

Test: Make sure apps with custom animations set using WindowAnimationStyle actually use the custom animation when shell is enabled

Bug: 206960607
Change-Id: I1d982870837d67c74f76dd0a90aa5260d9441aba
parent 57922443
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -380,6 +380,8 @@ public class ActivityOptions extends ComponentOptions {
    public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
    /** @hide */
    public static final int ANIM_REMOTE_ANIMATION = 13;
    /** @hide */
    public static final int ANIM_FROM_STYLE = 14;

    private String mPackageName;
    private Rect mLaunchBounds;
+17 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.window;

import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
@@ -46,6 +47,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;

import java.util.ArrayList;
import java.util.List;
@@ -581,6 +583,7 @@ public final class TransitionInfo implements Parcelable {
        private String mPackageName;
        private final Rect mTransitionBounds = new Rect();
        private HardwareBuffer mThumbnail;
        private int mAnimations;

        private AnimationOptions(int type) {
            mType = type;
@@ -594,6 +597,15 @@ public final class TransitionInfo implements Parcelable {
            mPackageName = in.readString();
            mTransitionBounds.readFromParcel(in);
            mThumbnail = in.readTypedObject(HardwareBuffer.CREATOR);
            mAnimations = in.readInt();
        }

        public static AnimationOptions makeAnimOptionsFromLayoutParameters(
                WindowManager.LayoutParams lp) {
            AnimationOptions options = new AnimationOptions(ANIM_FROM_STYLE);
            options.mPackageName = lp.packageName;
            options.mAnimations = lp.windowAnimations;
            return options;
        }

        public static AnimationOptions makeCustomAnimOptions(String packageName, int enterResId,
@@ -662,6 +674,10 @@ public final class TransitionInfo implements Parcelable {
            return mThumbnail;
        }

        public int getAnimations() {
            return mAnimations;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mType);
@@ -671,6 +687,7 @@ public final class TransitionInfo implements Parcelable {
            dest.writeString(mPackageName);
            mTransitionBounds.writeToParcel(dest, flags);
            dest.writeTypedObject(mThumbnail, flags);
            dest.writeInt(mAnimations);
        }

        @NonNull
+28 −4
Original line number Diff line number Diff line
@@ -260,25 +260,39 @@ public class TransitionAnimation {
        return null;
    }

    /** Load animation by attribute Id from android package. */
    /** Load animation by attribute Id from a specific AnimationStyle resource. */
    @Nullable
    public Animation loadDefaultAnimationAttr(int animAttr) {
    public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
            boolean translucent) {
        if (animStyleResId == 0) {
            return null;
        }
        int resId = Resources.ID_NULL;
        Context context = mContext;
        if (animAttr >= 0) {
            AttributeCache.Entry ent = getCachedAnimations(DEFAULT_PACKAGE,
                    mDefaultWindowAnimationStyleResId);
            packageName = packageName != null ? packageName : DEFAULT_PACKAGE;
            AttributeCache.Entry ent = getCachedAnimations(packageName, animStyleResId);
            if (ent != null) {
                context = ent.context;
                resId = ent.array.getResourceId(animAttr, 0);
            }
        }
        if (translucent) {
            resId = updateToTranslucentAnimIfNeeded(resId);
        }
        if (ResourceId.isValid(resId)) {
            return loadAnimationSafely(context, resId, mTag);
        }
        return null;
    }

    /** Load animation by attribute Id from android package. */
    @Nullable
    public Animation loadDefaultAnimationAttr(int animAttr) {
        return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr,
                false /* translucent */);
    }

    @Nullable
    private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
        if (mDebug) {
@@ -1024,6 +1038,16 @@ public class TransitionAnimation {
        return anim;
    }

    private static int updateToTranslucentAnimIfNeeded(int anim) {
        if (anim == R.anim.activity_open_enter) {
            return R.anim.activity_translucent_open_enter;
        }
        if (anim == R.anim.activity_close_exit) {
            return R.anim.activity_translucent_close_exit;
        }
        return anim;
    }

    private static @TransitionOldType int getTransitCompatType(@TransitionType int transit,
            int wallpaperTransit) {
        if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+58 −47
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.transition;

import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
import static android.app.ActivityOptions.ANIM_FROM_STYLE;
import static android.app.ActivityOptions.ANIM_NONE;
import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
@@ -508,60 +509,70 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
        } else if ((changeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0 && isOpeningType) {
            // This received a transferred starting window, so don't animate
            return null;
        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
        } else {
            int animAttr = 0;
            boolean translucent = false;
            if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
                animAttr = enter
                        ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
                    : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation);
                        : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
            } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                animAttr = enter
                        ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
                    : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation);
                        : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
            } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                animAttr = enter
                        ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
                    : R.styleable.WindowAnimation_wallpaperOpenExitAnimation);
                        : R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
            } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                animAttr = enter
                        ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
                    : R.styleable.WindowAnimation_wallpaperCloseExitAnimation);
                        : R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
            } else if (type == TRANSIT_OPEN) {
                if (isTask) {
                a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                    animAttr = enter
                            ? R.styleable.WindowAnimation_taskOpenEnterAnimation
                        : R.styleable.WindowAnimation_taskOpenExitAnimation);
                            : R.styleable.WindowAnimation_taskOpenExitAnimation;
                } else {
                    if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
                    a = mTransitionAnimation.loadDefaultAnimationRes(
                            R.anim.activity_translucent_open_enter);
                } else {
                    a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                            ? R.styleable.WindowAnimation_activityOpenEnterAnimation
                            : R.styleable.WindowAnimation_activityOpenExitAnimation);
                        translucent = true;
                    }
                    animAttr = enter
                            ? R.styleable.WindowAnimation_activityOpenEnterAnimation
                            : R.styleable.WindowAnimation_activityOpenExitAnimation;
                }
            } else if (type == TRANSIT_TO_FRONT) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                animAttr = enter
                        ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
                    : R.styleable.WindowAnimation_taskToFrontExitAnimation);
                        : R.styleable.WindowAnimation_taskToFrontExitAnimation;
            } else if (type == TRANSIT_CLOSE) {
                if (isTask) {
                a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                    animAttr = enter
                            ? R.styleable.WindowAnimation_taskCloseEnterAnimation
                        : R.styleable.WindowAnimation_taskCloseExitAnimation);
                            : R.styleable.WindowAnimation_taskCloseExitAnimation;
                } else {
                    if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
                    a = mTransitionAnimation.loadDefaultAnimationRes(
                            R.anim.activity_translucent_close_exit);
                } else {
                    a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                            ? R.styleable.WindowAnimation_activityCloseEnterAnimation
                            : R.styleable.WindowAnimation_activityCloseExitAnimation);
                        translucent = true;
                    }
                    animAttr = enter
                            ? R.styleable.WindowAnimation_activityCloseEnterAnimation
                            : R.styleable.WindowAnimation_activityCloseExitAnimation;
                }
            } else if (type == TRANSIT_TO_BACK) {
            a = mTransitionAnimation.loadDefaultAnimationAttr(enter
                animAttr = enter
                        ? R.styleable.WindowAnimation_taskToBackEnterAnimation
                    : R.styleable.WindowAnimation_taskToBackExitAnimation);
                        : R.styleable.WindowAnimation_taskToBackExitAnimation;
            }

            if (animAttr != 0) {
                if (overrideType == ANIM_FROM_STYLE && canCustomContainer) {
                    a = mTransitionAnimation
                            .loadAnimationAttr(options.getPackageName(), options.getAnimations(),
                                    animAttr, translucent);
                } else {
                    a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr);
                }
            }
        }

        if (a != null) {
+81 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
@@ -70,6 +71,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.window.RemoteTransition;
import android.window.TransitionInfo;
@@ -82,6 +84,8 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * Represents a logical transition.
@@ -90,6 +94,9 @@ import java.util.ArrayList;
class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListener {
    private static final String TAG = "Transition";

    /** The default package for resources */
    private static final String DEFAULT_PACKAGE = "android";

    /** The transition has been created and is collecting, but hasn't formally started. */
    private static final int STATE_COLLECTING = 0;

@@ -537,7 +544,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
        // Resolve the animating targets from the participants
        mTargets = calculateTargets(mParticipants, mChanges);
        final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges);
        if (mOverrideOptions != null) {
            info.setAnimationOptions(mOverrideOptions);
        }

        // TODO(b/188669821): Move to animation impl in shell.
        handleLegacyRecentsStartBehavior(dc, info);
@@ -1238,9 +1247,80 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
            out.addChange(change);
        }

        final WindowManager.LayoutParams animLp =
                getLayoutParamsForAnimationsStyle(type, sortedTargets);
        if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING
                && animLp.windowAnimations != 0) {
            // 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);
            out.setAnimationOptions(animOptions);
        }

        return out;
    }

    private static WindowManager.LayoutParams getLayoutParamsForAnimationsStyle(int type,
            ArrayList<WindowContainer> sortedTargets) {
        // Find the layout params of the top-most application window that is part of the
        // transition, which is what will control the animation theme.
        final ArraySet<Integer> activityTypes = new ArraySet<>();
        for (WindowContainer target : sortedTargets) {
            if (target.asActivityRecord() != null) {
                activityTypes.add(target.getActivityType());
            } else if (target.asWindowToken() == null && target.asWindowState() == null) {
                // We don't want app to customize animations that are not activity to activity.
                // Activity-level transitions can only include activities, wallpaper and subwindows.
                // Anything else is not a WindowToken nor a WindowState and is "higher" in the
                // hierarchy which means we are no longer in an activity transition.
                return null;
            }
        }
        if (activityTypes.isEmpty()) {
            // We don't want app to be able to customize transitions that are not activity to
            // activity through the layout parameter animation style.
            return null;
        }
        final ActivityRecord animLpActivity =
                findAnimLayoutParamsActivityRecord(sortedTargets, type, activityTypes);
        final WindowState mainWindow = animLpActivity != null
                ? animLpActivity.findMainWindow() : null;
        return mainWindow != null ? mainWindow.mAttrs : null;
    }

    private static ActivityRecord findAnimLayoutParamsActivityRecord(
            List<WindowContainer> sortedTargets,
            @TransitionType int transit, ArraySet<Integer> activityTypes) {
        // Remote animations always win, but fullscreen windows override non-fullscreen windows.
        ActivityRecord result = lookForTopWindowWithFilter(sortedTargets,
                w -> w.getRemoteAnimationDefinition() != null
                    && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
        if (result != null) {
            return result;
        }
        result = lookForTopWindowWithFilter(sortedTargets,
                w -> w.fillsParent() && w.findMainWindow() != null);
        if (result != null) {
            return result;
        }
        return lookForTopWindowWithFilter(sortedTargets, w -> w.findMainWindow() != null);
    }

    private static ActivityRecord lookForTopWindowWithFilter(List<WindowContainer> sortedTargets,
            Predicate<ActivityRecord> filter) {
        for (WindowContainer target : sortedTargets) {
            final ActivityRecord activityRecord = target.asTaskFragment() != null
                    ? target.asTaskFragment().getTopNonFinishingActivity()
                    : target.asActivityRecord();
            if (activityRecord != null && filter.test(activityRecord)) {
                return activityRecord;
            }
        }
        return null;
    }

    private static int getTaskRotationAnimation(@NonNull Task task) {
        final ActivityRecord top = task.getTopVisibleActivity();
        if (top == null) return ROTATION_ANIMATION_UNSPECIFIED;
Loading