Loading core/java/android/window/BackNavigationInfo.java +48 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.window; import android.annotation.AnimRes; import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -245,6 +247,9 @@ public final class BackNavigationInfo implements Parcelable { public static final class CustomAnimationInfo implements Parcelable { private final String mPackageName; private int mWindowAnimations; @AnimRes private int mCustomExitAnim; @AnimRes private int mCustomEnterAnim; @ColorInt private int mCustomBackground; /** * The package name of the windowAnimations. Loading @@ -261,6 +266,27 @@ public final class BackNavigationInfo implements Parcelable { return mWindowAnimations; } /** * The exit animation resource Id of customize activity transition. */ public int getCustomExitAnim() { return mCustomExitAnim; } /** * The entering animation resource Id of customize activity transition. */ public int getCustomEnterAnim() { return mCustomEnterAnim; } /** * The background color of customize activity transition. */ public int getCustomBackground() { return mCustomBackground; } public CustomAnimationInfo(@NonNull String packageName) { this.mPackageName = packageName; } Loading @@ -274,11 +300,17 @@ public final class BackNavigationInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString8(mPackageName); dest.writeInt(mWindowAnimations); dest.writeInt(mCustomEnterAnim); dest.writeInt(mCustomExitAnim); dest.writeInt(mCustomBackground); } private CustomAnimationInfo(@NonNull Parcel in) { mPackageName = in.readString8(); mWindowAnimations = in.readInt(); mCustomEnterAnim = in.readInt(); mCustomExitAnim = in.readInt(); mCustomBackground = in.readInt(); } @Override Loading Loading @@ -349,10 +381,25 @@ public final class BackNavigationInfo implements Parcelable { * Set windowAnimations for customize animation. */ public Builder setWindowAnimations(String packageName, int windowAnimations) { if (mCustomAnimationInfo == null) { mCustomAnimationInfo = new CustomAnimationInfo(packageName); } mCustomAnimationInfo.mWindowAnimations = windowAnimations; return this; } /** * Set resources ids for customize activity animation. */ public Builder setCustomAnimation(String packageName, @AnimRes int enterResId, @AnimRes int exitResId, @ColorInt int backgroundColor) { if (mCustomAnimationInfo == null) { mCustomAnimationInfo = new CustomAnimationInfo(packageName); } mCustomAnimationInfo.mCustomExitAnim = exitResId; mCustomAnimationInfo.mCustomEnterAnim = enterResId; mCustomAnimationInfo.mCustomBackground = backgroundColor; return this; } /** * Builds and returns an instance of {@link BackNavigationInfo} Loading libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +78 −20 Original line number Diff line number Diff line Loading @@ -25,7 +25,10 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.os.RemoteException; import android.util.FloatProperty; Loading @@ -34,6 +37,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.WindowManager.LayoutParams; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; Loading @@ -43,6 +47,7 @@ import android.window.BackNavigationInfo; import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; import com.android.internal.R; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; import com.android.internal.policy.ScreenDecorationsUtils; Loading Loading @@ -78,6 +83,7 @@ class CustomizeActivityAnimation { final CustomAnimationLoader mCustomAnimationLoader; private Animation mEnterAnimation; private Animation mCloseAnimation; private int mNextBackgroundColor; final Transformation mTransformation = new Transformation(); private final Choreographer mChoreographer; Loading Loading @@ -144,8 +150,9 @@ class CustomizeActivityAnimation { // Draw background with task background color. if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) { mBackground.ensureBackground( mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction); mBackground.ensureBackground(mNextBackgroundColor == Color.TRANSPARENT ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor() : mNextBackgroundColor, mTransaction); } } Loading Loading @@ -191,6 +198,7 @@ class CustomizeActivityAnimation { mTransaction.apply(); mTransformation.clear(); mLatestProgress = 0; mNextBackgroundColor = Color.TRANSPARENT; if (mFinishCallback != null) { try { mFinishCallback.onAnimationFinished(); Loading Loading @@ -252,11 +260,11 @@ class CustomizeActivityAnimation { * Load customize animation before animation start. */ boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) { mCloseAnimation = mCustomAnimationLoader.load( animationInfo, false /* enterAnimation */); if (mCloseAnimation != null) { mEnterAnimation = mCustomAnimationLoader.load( animationInfo, true /* enterAnimation */); final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo); if (result != null) { mCloseAnimation = result.mCloseAnimation; mEnterAnimation = result.mEnterAnimation; mNextBackgroundColor = result.mBackgroundColor; return true; } return false; Loading Loading @@ -318,35 +326,79 @@ class CustomizeActivityAnimation { } } static final class AnimationLoadResult { Animation mCloseAnimation; Animation mEnterAnimation; int mBackgroundColor; } /** * Helper class to load custom animation. */ static class CustomAnimationLoader { private final TransitionAnimation mTransitionAnimation; final TransitionAnimation mTransitionAnimation; CustomAnimationLoader(Context context) { mTransitionAnimation = new TransitionAnimation( context, false /* debug */, "CustomizeBackAnimation"); } Animation load(BackNavigationInfo.CustomAnimationInfo animationInfo, boolean enterAnimation) { final String packageName = animationInfo.getPackageName(); if (packageName.isEmpty()) { /** * Load both enter and exit animation for the close activity transition. * Note that the result is only valid if the exit animation has set and loaded success. * If the entering animation has not set(i.e. 0), here will load the default entering * animation for it. * * @param animationInfo The information of customize animation, which can be set from * {@link Activity#overrideActivityTransition} and/or * {@link LayoutParams#windowAnimations} */ AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) { if (animationInfo.getPackageName().isEmpty()) { return null; } final int windowAnimations = animationInfo.getWindowAnimations(); if (windowAnimations == 0) { final Animation close = loadAnimation(animationInfo, false); if (close == null) { return null; } final int attrs = enterAnimation ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; Animation a = mTransitionAnimation.loadAnimationAttr(packageName, windowAnimations, attrs, false /* translucent */); final Animation open = loadAnimation(animationInfo, true); AnimationLoadResult result = new AnimationLoadResult(); result.mCloseAnimation = close; result.mEnterAnimation = open; result.mBackgroundColor = animationInfo.getCustomBackground(); return result; } /** * Load enter or exit animation from CustomAnimationInfo * @param animationInfo The information for customize animation. * @param enterAnimation true when load for enter animation, false for exit animation. * @return Loaded animation. */ @Nullable Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo, boolean enterAnimation) { Animation a = null; // Activity#overrideActivityTransition has higher priority than windowAnimations // Try to get animation from Activity#overrideActivityTransition if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0) || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) { a = mTransitionAnimation.loadAppTransitionAnimation( animationInfo.getPackageName(), enterAnimation ? animationInfo.getCustomEnterAnim() : animationInfo.getCustomExitAnim()); } else if (animationInfo.getWindowAnimations() != 0) { // try to get animation from LayoutParams#windowAnimations a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(), animationInfo.getWindowAnimations(), enterAnimation ? R.styleable.WindowAnimation_activityCloseEnterAnimation : R.styleable.WindowAnimation_activityCloseExitAnimation, false /* translucent */); } // Only allow to load default animation for opening target. if (a == null && enterAnimation) { a = mTransitionAnimation.loadDefaultAnimationAttr(attrs, false /* translucent */); a = loadDefaultOpenAnimation(); } if (a != null) { ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a); Loading @@ -355,5 +407,11 @@ class CustomizeActivityAnimation { } return a; } private Animation loadDefaultOpenAnimation() { return mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_activityCloseEnterAnimation, false /* translucent */); } } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java +82 −5 Original line number Diff line number Diff line Loading @@ -18,14 +18,20 @@ package com.android.wm.shell.back; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.WindowConfiguration; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; Loading Loading @@ -69,11 +75,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { mBackAnimationBackground, mock(SurfaceControl.Transaction.class), mock(Choreographer.class)); spyOn(mCustomizeActivityAnimation); spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .load(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .load(any(), eq(true)); spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation); } RemoteAnimationTarget createAnimationTarget(boolean open) { Loading @@ -87,6 +89,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { @Test public void receiveFinishAfterInvoke() throws InterruptedException { spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(true)); mCustomizeActivityAnimation.prepareNextAnimation( new BackNavigationInfo.CustomAnimationInfo("TestPackage")); final RemoteAnimationTarget close = createAnimationTarget(false); Loading @@ -112,6 +120,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { @Test public void receiveFinishAfterCancel() throws InterruptedException { spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(true)); mCustomizeActivityAnimation.prepareNextAnimation( new BackNavigationInfo.CustomAnimationInfo("TestPackage")); final RemoteAnimationTarget close = createAnimationTarget(false); Loading Loading @@ -152,4 +166,67 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { verify(mCustomizeActivityAnimation).onGestureCommitted(); finishCalled.await(1, TimeUnit.SECONDS); } @Test public void testLoadCustomAnimation() { testLoadCustomAnimation(10, 20, 0); } @Test public void testLoadCustomAnimationNoEnter() { testLoadCustomAnimation(0, 10, 0); } @Test public void testLoadWindowAnimations() { testLoadCustomAnimation(0, 0, 30); } @Test public void testCustomAnimationHigherThanWindowAnimations() { testLoadCustomAnimation(10, 20, 30); } private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) { final String testPackage = "TestPackage"; BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN) .setWindowAnimations(testPackage, windowAnimations); final BackNavigationInfo.CustomAnimationInfo info = builder.build() .getCustomAnimationInfo(); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAppTransitionAnimation(eq(testPackage), eq(exitResId)); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean()); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean()); CustomizeActivityAnimation.AnimationLoadResult result = mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info); if (exitResId != 0) { if (enterResId == 0) { verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation) .loadDefaultAnimationAttr(anyInt(), anyBoolean()); } else { assertEquals(result.mEnterAnimation, mMockOpenAnimation); } assertEquals(result.mBackgroundColor, Color.GREEN); assertEquals(result.mCloseAnimation, mMockCloseAnimation); verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never()) .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); } else if (windowAnimations != 0) { verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); assertEquals(result.mCloseAnimation, mMockCloseAnimation); } } } services/core/java/com/android/server/wm/BackNavigationController.java +15 −6 Original line number Diff line number Diff line Loading @@ -262,15 +262,24 @@ class BackNavigationController { if (!isOccluded || prevActivity.canShowWhenLocked()) { // We have another Activity in the same currentTask to go to final WindowContainer parent = currentActivity.getParent(); final boolean isCustomize = parent != null final boolean canCustomize = parent != null && (parent.asTask() != null || (parent.asTaskFragment() != null && parent.canCustomizeAppTransition())) && isCustomizeExitAnimation(window); if (isCustomize) { && parent.canCustomizeAppTransition())); if (canCustomize) { if (isCustomizeExitAnimation(window)) { infoBuilder.setWindowAnimations( window.mAttrs.packageName, window.mAttrs.windowAnimations); } final ActivityRecord.CustomAppTransition customAppTransition = currentActivity.getCustomAnimation(false/* open */); if (customAppTransition != null) { infoBuilder.setCustomAnimation(currentActivity.packageName, customAppTransition.mExitAnim, customAppTransition.mEnterAnim, customAppTransition.mBackgroundColor); } } removedWindowContainer = currentActivity; prevTask = prevActivity.getTask(); backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; Loading Loading
core/java/android/window/BackNavigationInfo.java +48 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.window; import android.annotation.AnimRes; import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; Loading Loading @@ -245,6 +247,9 @@ public final class BackNavigationInfo implements Parcelable { public static final class CustomAnimationInfo implements Parcelable { private final String mPackageName; private int mWindowAnimations; @AnimRes private int mCustomExitAnim; @AnimRes private int mCustomEnterAnim; @ColorInt private int mCustomBackground; /** * The package name of the windowAnimations. Loading @@ -261,6 +266,27 @@ public final class BackNavigationInfo implements Parcelable { return mWindowAnimations; } /** * The exit animation resource Id of customize activity transition. */ public int getCustomExitAnim() { return mCustomExitAnim; } /** * The entering animation resource Id of customize activity transition. */ public int getCustomEnterAnim() { return mCustomEnterAnim; } /** * The background color of customize activity transition. */ public int getCustomBackground() { return mCustomBackground; } public CustomAnimationInfo(@NonNull String packageName) { this.mPackageName = packageName; } Loading @@ -274,11 +300,17 @@ public final class BackNavigationInfo implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString8(mPackageName); dest.writeInt(mWindowAnimations); dest.writeInt(mCustomEnterAnim); dest.writeInt(mCustomExitAnim); dest.writeInt(mCustomBackground); } private CustomAnimationInfo(@NonNull Parcel in) { mPackageName = in.readString8(); mWindowAnimations = in.readInt(); mCustomEnterAnim = in.readInt(); mCustomExitAnim = in.readInt(); mCustomBackground = in.readInt(); } @Override Loading Loading @@ -349,10 +381,25 @@ public final class BackNavigationInfo implements Parcelable { * Set windowAnimations for customize animation. */ public Builder setWindowAnimations(String packageName, int windowAnimations) { if (mCustomAnimationInfo == null) { mCustomAnimationInfo = new CustomAnimationInfo(packageName); } mCustomAnimationInfo.mWindowAnimations = windowAnimations; return this; } /** * Set resources ids for customize activity animation. */ public Builder setCustomAnimation(String packageName, @AnimRes int enterResId, @AnimRes int exitResId, @ColorInt int backgroundColor) { if (mCustomAnimationInfo == null) { mCustomAnimationInfo = new CustomAnimationInfo(packageName); } mCustomAnimationInfo.mCustomExitAnim = exitResId; mCustomAnimationInfo.mCustomEnterAnim = enterResId; mCustomAnimationInfo.mCustomBackground = backgroundColor; return this; } /** * Builds and returns an instance of {@link BackNavigationInfo} Loading
libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +78 −20 Original line number Diff line number Diff line Loading @@ -25,7 +25,10 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.os.RemoteException; import android.util.FloatProperty; Loading @@ -34,6 +37,7 @@ import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.WindowManager.LayoutParams; import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; Loading @@ -43,6 +47,7 @@ import android.window.BackNavigationInfo; import android.window.BackProgressAnimator; import android.window.IOnBackInvokedCallback; import com.android.internal.R; import com.android.internal.dynamicanimation.animation.SpringAnimation; import com.android.internal.dynamicanimation.animation.SpringForce; import com.android.internal.policy.ScreenDecorationsUtils; Loading Loading @@ -78,6 +83,7 @@ class CustomizeActivityAnimation { final CustomAnimationLoader mCustomAnimationLoader; private Animation mEnterAnimation; private Animation mCloseAnimation; private int mNextBackgroundColor; final Transformation mTransformation = new Transformation(); private final Choreographer mChoreographer; Loading Loading @@ -144,8 +150,9 @@ class CustomizeActivityAnimation { // Draw background with task background color. if (mEnteringTarget.taskInfo != null && mEnteringTarget.taskInfo.taskDescription != null) { mBackground.ensureBackground( mEnteringTarget.taskInfo.taskDescription.getBackgroundColor(), mTransaction); mBackground.ensureBackground(mNextBackgroundColor == Color.TRANSPARENT ? mEnteringTarget.taskInfo.taskDescription.getBackgroundColor() : mNextBackgroundColor, mTransaction); } } Loading Loading @@ -191,6 +198,7 @@ class CustomizeActivityAnimation { mTransaction.apply(); mTransformation.clear(); mLatestProgress = 0; mNextBackgroundColor = Color.TRANSPARENT; if (mFinishCallback != null) { try { mFinishCallback.onAnimationFinished(); Loading Loading @@ -252,11 +260,11 @@ class CustomizeActivityAnimation { * Load customize animation before animation start. */ boolean prepareNextAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo) { mCloseAnimation = mCustomAnimationLoader.load( animationInfo, false /* enterAnimation */); if (mCloseAnimation != null) { mEnterAnimation = mCustomAnimationLoader.load( animationInfo, true /* enterAnimation */); final AnimationLoadResult result = mCustomAnimationLoader.loadAll(animationInfo); if (result != null) { mCloseAnimation = result.mCloseAnimation; mEnterAnimation = result.mEnterAnimation; mNextBackgroundColor = result.mBackgroundColor; return true; } return false; Loading Loading @@ -318,35 +326,79 @@ class CustomizeActivityAnimation { } } static final class AnimationLoadResult { Animation mCloseAnimation; Animation mEnterAnimation; int mBackgroundColor; } /** * Helper class to load custom animation. */ static class CustomAnimationLoader { private final TransitionAnimation mTransitionAnimation; final TransitionAnimation mTransitionAnimation; CustomAnimationLoader(Context context) { mTransitionAnimation = new TransitionAnimation( context, false /* debug */, "CustomizeBackAnimation"); } Animation load(BackNavigationInfo.CustomAnimationInfo animationInfo, boolean enterAnimation) { final String packageName = animationInfo.getPackageName(); if (packageName.isEmpty()) { /** * Load both enter and exit animation for the close activity transition. * Note that the result is only valid if the exit animation has set and loaded success. * If the entering animation has not set(i.e. 0), here will load the default entering * animation for it. * * @param animationInfo The information of customize animation, which can be set from * {@link Activity#overrideActivityTransition} and/or * {@link LayoutParams#windowAnimations} */ AnimationLoadResult loadAll(BackNavigationInfo.CustomAnimationInfo animationInfo) { if (animationInfo.getPackageName().isEmpty()) { return null; } final int windowAnimations = animationInfo.getWindowAnimations(); if (windowAnimations == 0) { final Animation close = loadAnimation(animationInfo, false); if (close == null) { return null; } final int attrs = enterAnimation ? com.android.internal.R.styleable.WindowAnimation_activityCloseEnterAnimation : com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; Animation a = mTransitionAnimation.loadAnimationAttr(packageName, windowAnimations, attrs, false /* translucent */); final Animation open = loadAnimation(animationInfo, true); AnimationLoadResult result = new AnimationLoadResult(); result.mCloseAnimation = close; result.mEnterAnimation = open; result.mBackgroundColor = animationInfo.getCustomBackground(); return result; } /** * Load enter or exit animation from CustomAnimationInfo * @param animationInfo The information for customize animation. * @param enterAnimation true when load for enter animation, false for exit animation. * @return Loaded animation. */ @Nullable Animation loadAnimation(BackNavigationInfo.CustomAnimationInfo animationInfo, boolean enterAnimation) { Animation a = null; // Activity#overrideActivityTransition has higher priority than windowAnimations // Try to get animation from Activity#overrideActivityTransition if ((enterAnimation && animationInfo.getCustomEnterAnim() != 0) || (!enterAnimation && animationInfo.getCustomExitAnim() != 0)) { a = mTransitionAnimation.loadAppTransitionAnimation( animationInfo.getPackageName(), enterAnimation ? animationInfo.getCustomEnterAnim() : animationInfo.getCustomExitAnim()); } else if (animationInfo.getWindowAnimations() != 0) { // try to get animation from LayoutParams#windowAnimations a = mTransitionAnimation.loadAnimationAttr(animationInfo.getPackageName(), animationInfo.getWindowAnimations(), enterAnimation ? R.styleable.WindowAnimation_activityCloseEnterAnimation : R.styleable.WindowAnimation_activityCloseExitAnimation, false /* translucent */); } // Only allow to load default animation for opening target. if (a == null && enterAnimation) { a = mTransitionAnimation.loadDefaultAnimationAttr(attrs, false /* translucent */); a = loadDefaultOpenAnimation(); } if (a != null) { ProtoLog.d(WM_SHELL_BACK_PREVIEW, "custom animation loaded %s", a); Loading @@ -355,5 +407,11 @@ class CustomizeActivityAnimation { } return a; } private Animation loadDefaultOpenAnimation() { return mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_activityCloseEnterAnimation, false /* translucent */); } } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java +82 −5 Original line number Diff line number Diff line Loading @@ -18,14 +18,20 @@ package com.android.wm.shell.back; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.WindowConfiguration; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.RemoteException; Loading Loading @@ -69,11 +75,7 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { mBackAnimationBackground, mock(SurfaceControl.Transaction.class), mock(Choreographer.class)); spyOn(mCustomizeActivityAnimation); spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .load(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .load(any(), eq(true)); spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation); } RemoteAnimationTarget createAnimationTarget(boolean open) { Loading @@ -87,6 +89,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { @Test public void receiveFinishAfterInvoke() throws InterruptedException { spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(true)); mCustomizeActivityAnimation.prepareNextAnimation( new BackNavigationInfo.CustomAnimationInfo("TestPackage")); final RemoteAnimationTarget close = createAnimationTarget(false); Loading @@ -112,6 +120,12 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { @Test public void receiveFinishAfterCancel() throws InterruptedException { spyOn(mCustomizeActivityAnimation.mCustomAnimationLoader); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(false)); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader) .loadAnimation(any(), eq(true)); mCustomizeActivityAnimation.prepareNextAnimation( new BackNavigationInfo.CustomAnimationInfo("TestPackage")); final RemoteAnimationTarget close = createAnimationTarget(false); Loading Loading @@ -152,4 +166,67 @@ public class CustomizeActivityAnimationTest extends ShellTestCase { verify(mCustomizeActivityAnimation).onGestureCommitted(); finishCalled.await(1, TimeUnit.SECONDS); } @Test public void testLoadCustomAnimation() { testLoadCustomAnimation(10, 20, 0); } @Test public void testLoadCustomAnimationNoEnter() { testLoadCustomAnimation(0, 10, 0); } @Test public void testLoadWindowAnimations() { testLoadCustomAnimation(0, 0, 30); } @Test public void testCustomAnimationHigherThanWindowAnimations() { testLoadCustomAnimation(10, 20, 30); } private void testLoadCustomAnimation(int enterResId, int exitResId, int windowAnimations) { final String testPackage = "TestPackage"; BackNavigationInfo.Builder builder = new BackNavigationInfo.Builder() .setCustomAnimation(testPackage, enterResId, exitResId, Color.GREEN) .setWindowAnimations(testPackage, windowAnimations); final BackNavigationInfo.CustomAnimationInfo info = builder.build() .getCustomAnimationInfo(); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAppTransitionAnimation(eq(testPackage), eq(exitResId)); doReturn(mMockCloseAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation) .loadAnimationAttr(eq(testPackage), eq(windowAnimations), anyInt(), anyBoolean()); doReturn(mMockOpenAnimation).when(mCustomizeActivityAnimation.mCustomAnimationLoader .mTransitionAnimation).loadDefaultAnimationAttr(anyInt(), anyBoolean()); CustomizeActivityAnimation.AnimationLoadResult result = mCustomizeActivityAnimation.mCustomAnimationLoader.loadAll(info); if (exitResId != 0) { if (enterResId == 0) { verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never()).loadAppTransitionAnimation(eq(testPackage), eq(enterResId)); verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation) .loadDefaultAnimationAttr(anyInt(), anyBoolean()); } else { assertEquals(result.mEnterAnimation, mMockOpenAnimation); } assertEquals(result.mBackgroundColor, Color.GREEN); assertEquals(result.mCloseAnimation, mMockCloseAnimation); verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, never()) .loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); } else if (windowAnimations != 0) { verify(mCustomizeActivityAnimation.mCustomAnimationLoader.mTransitionAnimation, times(2)).loadAnimationAttr(eq(testPackage), anyInt(), anyInt(), anyBoolean()); assertEquals(result.mCloseAnimation, mMockCloseAnimation); } } }
services/core/java/com/android/server/wm/BackNavigationController.java +15 −6 Original line number Diff line number Diff line Loading @@ -262,15 +262,24 @@ class BackNavigationController { if (!isOccluded || prevActivity.canShowWhenLocked()) { // We have another Activity in the same currentTask to go to final WindowContainer parent = currentActivity.getParent(); final boolean isCustomize = parent != null final boolean canCustomize = parent != null && (parent.asTask() != null || (parent.asTaskFragment() != null && parent.canCustomizeAppTransition())) && isCustomizeExitAnimation(window); if (isCustomize) { && parent.canCustomizeAppTransition())); if (canCustomize) { if (isCustomizeExitAnimation(window)) { infoBuilder.setWindowAnimations( window.mAttrs.packageName, window.mAttrs.windowAnimations); } final ActivityRecord.CustomAppTransition customAppTransition = currentActivity.getCustomAnimation(false/* open */); if (customAppTransition != null) { infoBuilder.setCustomAnimation(currentActivity.packageName, customAppTransition.mExitAnim, customAppTransition.mEnterAnim, customAppTransition.mBackgroundColor); } } removedWindowContainer = currentActivity; prevTask = prevActivity.getTask(); backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; Loading