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);
+ }
+}