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 1c0e6f726fbfb..756d802048330 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.WindowManager.TRANSIT_CHANGE; 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 com.android.wm.shell.transition.Transitions; 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 @@ class ActivityEmbeddingAnimationRunner { @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Runnable animationFinishCallback) { final List 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 @@ class ActivityEmbeddingAnimationRunner { */ @NonNull private List 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 @@ class ActivityEmbeddingAnimationRunner { 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 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 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 @@ class ActivityEmbeddingAnimationRunner { */ @NonNull private List createOpenCloseAnimationAdapters( - @NonNull TransitionInfo info, boolean isOpening, - @NonNull BiFunction 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 openingChanges = new ArrayList<>(); @@ -194,7 +200,8 @@ class ActivityEmbeddingAnimationRunner { final List 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 @@ class ActivityEmbeddingAnimationRunner { } 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 @@ class ActivityEmbeddingAnimationRunner { @NonNull private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter( - @NonNull TransitionInfo.Change change, - @NonNull BiFunction 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 @@ class ActivityEmbeddingAnimationRunner { 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 ad0dddf77002d..eb6ac76152660 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 android.window.TransitionInfo; 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 @@ class ActivityEmbeddingAnimationSpec { } @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 @@ class ActivityEmbeddingAnimationSpec { } @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 @@ class ActivityEmbeddingAnimationSpec { 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 4c927b6e84b8f..f1578164b0e9c 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 @@ 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; 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_OPEN; 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_TRANSITI 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.Context; 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.graphics.drawable.Drawable; 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 @@ import java.util.function.Consumer; 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 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } @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 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { 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 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // 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 0000000000000..efee6f40b53ef --- /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); + } +}