Implement ActivityEmbedding Shell transition animation with showBackdrop

Refactor background color handling from DefaultTransitionHandler to a
helper class, so that we can reuse it for ActivityEmbedding as well.

Bug: 207070762
Test: manual
Change-Id: I6034ddd1928184a969848f0be496247032763b40
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 1c0e6f7..756d802 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -20,6 +20,9 @@
 import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
 
+import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
+
 import android.animation.Animator;
 import android.animation.ValueAnimator;
 import android.content.Context;
@@ -42,7 +45,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
-import java.util.function.BiFunction;
 
 /** To run the ActivityEmbedding animations. */
 class ActivityEmbeddingAnimationRunner {
@@ -85,7 +87,7 @@
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Runnable animationFinishCallback) {
         final List<ActivityEmbeddingAnimationAdapter> adapters =
-                createAnimationAdapters(info, startTransaction);
+                createAnimationAdapters(info, startTransaction, finishTransaction);
         long duration = 0;
         for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
             duration = Math.max(duration, adapter.getDurationHint());
@@ -129,7 +131,8 @@
      */
     @NonNull
     private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters(
-            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
         boolean isChangeTransition = false;
         for (TransitionInfo.Change change : info.getChanges()) {
             if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) {
@@ -145,23 +148,25 @@
             return createChangeAnimationAdapters(info, startTransaction);
         }
         if (Transitions.isClosingType(info.getType())) {
-            return createCloseAnimationAdapters(info);
+            return createCloseAnimationAdapters(info, startTransaction, finishTransaction);
         }
-        return createOpenAnimationAdapters(info);
+        return createOpenAnimationAdapters(info, startTransaction, finishTransaction);
     }
 
     @NonNull
     private List<ActivityEmbeddingAnimationAdapter> createOpenAnimationAdapters(
-            @NonNull TransitionInfo info) {
-        return createOpenCloseAnimationAdapters(info, true /* isOpening */,
-                mAnimationSpec::loadOpenAnimation);
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction,
+                true /* isOpening */, mAnimationSpec::loadOpenAnimation);
     }
 
     @NonNull
     private List<ActivityEmbeddingAnimationAdapter> createCloseAnimationAdapters(
-            @NonNull TransitionInfo info) {
-        return createOpenCloseAnimationAdapters(info, false /* isOpening */,
-                mAnimationSpec::loadCloseAnimation);
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction,
+                false /* isOpening */, mAnimationSpec::loadCloseAnimation);
     }
 
     /**
@@ -170,8 +175,9 @@
      */
     @NonNull
     private List<ActivityEmbeddingAnimationAdapter> createOpenCloseAnimationAdapters(
-            @NonNull TransitionInfo info, boolean isOpening,
-            @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider) {
+            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction, boolean isOpening,
+            @NonNull AnimationProvider animationProvider) {
         // We need to know if the change window is only a partial of the whole animation screen.
         // If so, we will need to adjust it to make the whole animation screen looks like one.
         final List<TransitionInfo.Change> openingChanges = new ArrayList<>();
@@ -194,7 +200,8 @@
         final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
         for (TransitionInfo.Change change : openingChanges) {
             final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
-                    change, animationProvider, openingWholeScreenBounds);
+                    info, change, startTransaction, finishTransaction, animationProvider,
+                    openingWholeScreenBounds);
             if (isOpening) {
                 adapter.overrideLayer(offsetLayer++);
             }
@@ -202,7 +209,8 @@
         }
         for (TransitionInfo.Change change : closingChanges) {
             final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter(
-                    change, animationProvider, closingWholeScreenBounds);
+                    info, change, startTransaction, finishTransaction, animationProvider,
+                    closingWholeScreenBounds);
             if (!isOpening) {
                 adapter.overrideLayer(offsetLayer++);
             }
@@ -213,10 +221,18 @@
 
     @NonNull
     private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter(
-            @NonNull TransitionInfo.Change change,
-            @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
-            @NonNull Rect wholeAnimationBounds) {
-        final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
+            @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction,
+            @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) {
+        final Animation animation = animationProvider.get(info, change, wholeAnimationBounds);
+        // We may want to show a background color for open/close transition.
+        final int backgroundColor = getTransitionBackgroundColorIfSet(info, change, animation,
+                0 /* defaultColor */);
+        if (backgroundColor != 0) {
+            addBackgroundToTransition(info.getRootLeash(), backgroundColor, startTransaction,
+                    finishTransaction);
+        }
         return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
                 wholeAnimationBounds);
     }
@@ -322,4 +338,10 @@
         return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
                 animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
     }
+
+    /** To provide an {@link Animation} based on the transition infos. */
+    private interface AnimationProvider {
+        Animation get(@NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
+                @NonNull Rect animationBounds);
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index ad0dddf..eb6ac76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -17,6 +17,9 @@
 package com.android.wm.shell.activityembedding;
 
 
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
+
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -33,7 +36,6 @@
 
 import androidx.annotation.NonNull;
 
-import com.android.internal.R;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.wm.shell.transition.Transitions;
 
@@ -175,16 +177,20 @@
     }
 
     @NonNull
-    Animation loadOpenAnimation(@NonNull TransitionInfo.Change change,
-            @NonNull Rect wholeAnimationBounds) {
+    Animation loadOpenAnimation(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = Transitions.isOpeningType(change.getMode());
         final Animation animation;
-        // TODO(b/207070762):
-        // 1. Implement clearTop version: R.anim.task_fragment_clear_top_close_enter/exit
-        // 2. Implement edgeExtension version
-        animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                ? R.anim.task_fragment_open_enter
-                : R.anim.task_fragment_open_exit);
+        // TODO(b/207070762): Implement edgeExtension version
+        if (shouldShowBackdrop(info, change)) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_open_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_open_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_open_enter
+                    : com.android.internal.R.anim.task_fragment_open_exit);
+        }
         // Use the whole animation bounds instead of the change bounds, so that when multiple change
         // targets are opening at the same time, the animation applied to each will be the same.
         // Otherwise, we may see gap between the activities that are launching together.
@@ -195,16 +201,20 @@
     }
 
     @NonNull
-    Animation loadCloseAnimation(@NonNull TransitionInfo.Change change,
-            @NonNull Rect wholeAnimationBounds) {
+    Animation loadCloseAnimation(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) {
         final boolean isEnter = Transitions.isOpeningType(change.getMode());
         final Animation animation;
-        // TODO(b/207070762):
-        // 1. Implement clearTop version: R.anim.task_fragment_clear_top_close_enter/exit
-        // 2. Implement edgeExtension version
-        animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
-                ? R.anim.task_fragment_close_enter
-                : R.anim.task_fragment_close_exit);
+        // TODO(b/207070762): Implement edgeExtension version
+        if (shouldShowBackdrop(info, change)) {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_clear_top_close_enter
+                    : com.android.internal.R.anim.task_fragment_clear_top_close_exit);
+        } else {
+            animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
+                    ? com.android.internal.R.anim.task_fragment_close_enter
+                    : com.android.internal.R.anim.task_fragment_close_exit);
+        }
         // Use the whole animation bounds instead of the change bounds, so that when multiple change
         // targets are closing at the same time, the animation applied to each will be the same.
         // Otherwise, we may see gap between the activities that are finishing together.
@@ -213,4 +223,11 @@
         animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
         return animation;
     }
+
+    private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change) {
+        final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
+                mTransitionAnimation);
+        return a != null && a.getShowBackdrop();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4c927b6..f157816 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -18,13 +18,11 @@
 
 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;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
 import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -43,7 +41,6 @@
 import static android.view.WindowManager.TRANSIT_RELAUNCH;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
-import static android.view.WindowManager.transitTypeToString;
 import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
 import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
 import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -59,6 +56,10 @@
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
+import static com.android.wm.shell.transition.TransitionAnimationHelper.sDisableCustomTaskAnimationProperty;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -74,7 +75,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -84,7 +84,6 @@
 import android.hardware.HardwareBuffer;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.view.Choreographer;
@@ -122,21 +121,6 @@
 public class DefaultTransitionHandler implements Transitions.TransitionHandler {
     private static final int MAX_ANIMATION_DURATION = 3000;
 
-    /**
-     * Restrict ability of activities overriding transition animation in a way such that
-     * an activity can do it only when the transition happens within a same task.
-     *
-     * @see android.app.Activity#overridePendingTransition(int, int)
-     */
-    private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
-            "persist.wm.disable_custom_task_animation";
-
-    /**
-     * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
-     */
-    static boolean sDisableCustomTaskAnimationProperty =
-            SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
-
     private final TransactionPool mTransactionPool;
     private final DisplayController mDisplayController;
     private final Context mContext;
@@ -431,22 +415,8 @@
                     cornerRadius = 0;
                 }
 
-                if (a.getShowBackdrop()) {
-                    if (info.getAnimationOptions().getBackgroundColor() != 0) {
-                        // If available use the background color provided through AnimationOptions
-                        backgroundColorForTransition =
-                                info.getAnimationOptions().getBackgroundColor();
-                    } else if (a.getBackdropColor() != 0) {
-                        // Otherwise fallback on the background color provided through the animation
-                        // definition.
-                        backgroundColorForTransition = a.getBackdropColor();
-                    } else if (change.getBackgroundColor() != 0) {
-                        // Otherwise default to the window's background color if provided through
-                        // the theme as the background color for the animation - the top most window
-                        // with a valid background color and showBackground set takes precedence.
-                        backgroundColorForTransition = change.getBackgroundColor();
-                    }
-                }
+                backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
+                        backgroundColorForTransition);
 
                 boolean delayedEdgeExtension = false;
                 if (!isTask && a.hasExtension()) {
@@ -668,29 +638,6 @@
         return edgeExtensionLayer;
     }
 
-    private void addBackgroundToTransition(
-            @NonNull SurfaceControl rootLeash,
-            @ColorInt int color,
-            @NonNull SurfaceControl.Transaction startTransaction,
-            @NonNull SurfaceControl.Transaction finishTransaction
-    ) {
-        final Color bgColor = Color.valueOf(color);
-        final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
-
-        final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
-                .setName("Animation Background")
-                .setParent(rootLeash)
-                .setColorLayer()
-                .setOpaque(true)
-                .build();
-
-        startTransaction
-                .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
-                .setColor(animationBackgroundSurface, colorArray)
-                .show(animationBackgroundSurface);
-        finishTransaction.remove(animationBackgroundSurface);
-    }
-
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
@@ -704,9 +651,9 @@
     }
 
     @Nullable
-    private Animation loadAnimation(TransitionInfo info, TransitionInfo.Change change,
-            int wallpaperTransit) {
-        Animation a = null;
+    private Animation loadAnimation(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change, int wallpaperTransit) {
+        Animation a;
 
         final int type = info.getType();
         final int flags = info.getFlags();
@@ -717,12 +664,10 @@
         final boolean isTask = change.getTaskInfo() != null;
         final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
         final int overrideType = options != null ? options.getType() : ANIM_NONE;
-        final boolean canCustomContainer = isTask ? !sDisableCustomTaskAnimationProperty : true;
+        final boolean canCustomContainer = !isTask || !sDisableCustomTaskAnimationProperty;
         final Rect endBounds = Transitions.isClosingType(changeMode)
                 ? mRotator.getEndBoundsInStartRotation(change)
                 : change.getEndAbsBounds();
-        final boolean isDream =
-                isTask && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM;
 
         if (info.isKeyguardGoingAway()) {
             a = mTransitionAnimation.loadKeyguardExitAnimation(flags,
@@ -763,87 +708,7 @@
             // This received a transferred starting window, so don't animate
             return null;
         } else {
-            int animAttr = 0;
-            boolean translucent = false;
-            if (isDream) {
-                if (type == TRANSIT_OPEN) {
-                    animAttr = enter
-                            ? R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation
-                            : R.styleable.WindowAnimation_dreamActivityOpenExitAnimation;
-                } else if (type == TRANSIT_CLOSE) {
-                    animAttr = enter
-                            ? 0
-                            : R.styleable.WindowAnimation_dreamActivityCloseExitAnimation;
-                }
-            } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
-                        : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
-            } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
-                        : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
-            } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
-                        : R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
-            } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
-                        : R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
-            } else if (type == TRANSIT_OPEN) {
-                // We will translucent open animation for translucent activities and tasks. Choose
-                // WindowAnimation_activityOpenEnterAnimation and set translucent here, then
-                // TransitionAnimation loads appropriate animation later.
-                if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
-                    translucent = true;
-                }
-                if (isTask && !translucent) {
-                    animAttr = enter
-                            ? R.styleable.WindowAnimation_taskOpenEnterAnimation
-                            : R.styleable.WindowAnimation_taskOpenExitAnimation;
-                } else {
-                    animAttr = enter
-                            ? R.styleable.WindowAnimation_activityOpenEnterAnimation
-                            : R.styleable.WindowAnimation_activityOpenExitAnimation;
-                }
-            } else if (type == TRANSIT_TO_FRONT) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
-                        : R.styleable.WindowAnimation_taskToFrontExitAnimation;
-            } else if (type == TRANSIT_CLOSE) {
-                if (isTask) {
-                    animAttr = enter
-                            ? R.styleable.WindowAnimation_taskCloseEnterAnimation
-                            : R.styleable.WindowAnimation_taskCloseExitAnimation;
-                } else {
-                    if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
-                        translucent = true;
-                    }
-                    animAttr = enter
-                            ? R.styleable.WindowAnimation_activityCloseEnterAnimation
-                            : R.styleable.WindowAnimation_activityCloseExitAnimation;
-                }
-            } else if (type == TRANSIT_TO_BACK) {
-                animAttr = enter
-                        ? R.styleable.WindowAnimation_taskToBackEnterAnimation
-                        : 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, translucent);
-                }
-            }
-
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
-                    "loadAnimation: anim=%s animAttr=0x%x type=%s isEntrance=%b", a, animAttr,
-                    transitTypeToString(type),
-                    enter);
+            a = loadAttributeAnimation(info, change, wallpaperTransit, mTransitionAnimation);
         }
 
         if (a != null) {
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
new file mode 100644
index 0000000..efee6f40
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.transition;
+
+import static android.app.ActivityOptions.ANIM_FROM_STYLE;
+import static android.app.ActivityOptions.ANIM_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OPEN;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
+
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
+import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Color;
+import android.os.SystemProperties;
+import android.view.SurfaceControl;
+import android.view.animation.Animation;
+import android.window.TransitionInfo;
+
+import com.android.internal.R;
+import com.android.internal.policy.TransitionAnimation;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+
+/** The helper class that provides methods for adding styles to transition animations. */
+public class TransitionAnimationHelper {
+
+    /**
+     * Restrict ability of activities overriding transition animation in a way such that
+     * an activity can do it only when the transition happens within a same task.
+     *
+     * @see android.app.Activity#overridePendingTransition(int, int)
+     */
+    private static final String DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY =
+            "persist.wm.disable_custom_task_animation";
+
+    /**
+     * @see #DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY
+     */
+    static final boolean sDisableCustomTaskAnimationProperty =
+            SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
+
+    /** Loads the animation that is defined through attribute id for the given transition. */
+    @Nullable
+    public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change, int wallpaperTransit,
+            @NonNull TransitionAnimation transitionAnimation) {
+        final int type = info.getType();
+        final int changeMode = change.getMode();
+        final int changeFlags = change.getFlags();
+        final boolean enter = Transitions.isOpeningType(changeMode);
+        final boolean isTask = change.getTaskInfo() != null;
+        final TransitionInfo.AnimationOptions options = info.getAnimationOptions();
+        final int overrideType = options != null ? options.getType() : ANIM_NONE;
+        final boolean canCustomContainer = !isTask || !sDisableCustomTaskAnimationProperty;
+        final boolean isDream =
+                isTask && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM;
+        int animAttr = 0;
+        boolean translucent = false;
+        if (isDream) {
+            if (type == TRANSIT_OPEN) {
+                animAttr = enter
+                        ? R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation
+                        : R.styleable.WindowAnimation_dreamActivityOpenExitAnimation;
+            } else if (type == TRANSIT_CLOSE) {
+                animAttr = enter
+                        ? 0
+                        : R.styleable.WindowAnimation_dreamActivityCloseExitAnimation;
+            }
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation;
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_CLOSE) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_wallpaperIntraCloseEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperIntraCloseExitAnimation;
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_OPEN) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_wallpaperOpenEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperOpenExitAnimation;
+        } else if (wallpaperTransit == WALLPAPER_TRANSITION_CLOSE) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
+                    : R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
+        } else if (type == TRANSIT_OPEN) {
+            // We will translucent open animation for translucent activities and tasks. Choose
+            // WindowAnimation_activityOpenEnterAnimation and set translucent here, then
+            // TransitionAnimation loads appropriate animation later.
+            if ((changeFlags & FLAG_TRANSLUCENT) != 0 && enter) {
+                translucent = true;
+            }
+            if (isTask && !translucent) {
+                animAttr = enter
+                        ? R.styleable.WindowAnimation_taskOpenEnterAnimation
+                        : R.styleable.WindowAnimation_taskOpenExitAnimation;
+            } else {
+                animAttr = enter
+                        ? R.styleable.WindowAnimation_activityOpenEnterAnimation
+                        : R.styleable.WindowAnimation_activityOpenExitAnimation;
+            }
+        } else if (type == TRANSIT_TO_FRONT) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_taskToFrontEnterAnimation
+                    : R.styleable.WindowAnimation_taskToFrontExitAnimation;
+        } else if (type == TRANSIT_CLOSE) {
+            if (isTask) {
+                animAttr = enter
+                        ? R.styleable.WindowAnimation_taskCloseEnterAnimation
+                        : R.styleable.WindowAnimation_taskCloseExitAnimation;
+            } else {
+                if ((changeFlags & FLAG_TRANSLUCENT) != 0 && !enter) {
+                    translucent = true;
+                }
+                animAttr = enter
+                        ? R.styleable.WindowAnimation_activityCloseEnterAnimation
+                        : R.styleable.WindowAnimation_activityCloseExitAnimation;
+            }
+        } else if (type == TRANSIT_TO_BACK) {
+            animAttr = enter
+                    ? R.styleable.WindowAnimation_taskToBackEnterAnimation
+                    : R.styleable.WindowAnimation_taskToBackExitAnimation;
+        }
+
+        Animation a = null;
+        if (animAttr != 0) {
+            if (overrideType == ANIM_FROM_STYLE && canCustomContainer) {
+                a = transitionAnimation
+                        .loadAnimationAttr(options.getPackageName(), options.getAnimations(),
+                                animAttr, translucent);
+            } else {
+                a = transitionAnimation.loadDefaultAnimationAttr(animAttr, translucent);
+            }
+        }
+
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                "loadAnimation: anim=%s animAttr=0x%x type=%s isEntrance=%b", a, animAttr,
+                transitTypeToString(type),
+                enter);
+        return a;
+    }
+
+    /**
+     * Gets the background {@link ColorInt} for the given transition animation if it is set.
+     *
+     * @param defaultColor  {@link ColorInt} to return if there is no background color specified by
+     *                      the given transition animation.
+     */
+    @ColorInt
+    public static int getTransitionBackgroundColorIfSet(@NonNull TransitionInfo info,
+            @NonNull TransitionInfo.Change change, @NonNull Animation a,
+            @ColorInt int defaultColor) {
+        if (!a.getShowBackdrop()) {
+            return defaultColor;
+        }
+        if (info.getAnimationOptions() != null
+                && info.getAnimationOptions().getBackgroundColor() != 0) {
+            // If available use the background color provided through AnimationOptions
+            return info.getAnimationOptions().getBackgroundColor();
+        } else if (a.getBackdropColor() != 0) {
+            // Otherwise fallback on the background color provided through the animation
+            // definition.
+            return a.getBackdropColor();
+        } else if (change.getBackgroundColor() != 0) {
+            // Otherwise default to the window's background color if provided through
+            // the theme as the background color for the animation - the top most window
+            // with a valid background color and showBackground set takes precedence.
+            return change.getBackgroundColor();
+        }
+        return defaultColor;
+    }
+
+    /**
+     * Adds the given {@code backgroundColor} as the background color to the transition animation.
+     */
+    public static void addBackgroundToTransition(@NonNull SurfaceControl rootLeash,
+            @ColorInt int backgroundColor, @NonNull SurfaceControl.Transaction startTransaction,
+            @NonNull SurfaceControl.Transaction finishTransaction) {
+        if (backgroundColor == 0) {
+            // No background color.
+            return;
+        }
+        final Color bgColor = Color.valueOf(backgroundColor);
+        final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
+        final SurfaceControl animationBackgroundSurface = new SurfaceControl.Builder()
+                .setName("Animation Background")
+                .setParent(rootLeash)
+                .setColorLayer()
+                .setOpaque(true)
+                .build();
+        startTransaction
+                .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
+                .setColor(animationBackgroundSurface, colorArray)
+                .show(animationBackgroundSurface);
+        finishTransaction.remove(animationBackgroundSurface);
+    }
+}