diff options
4 files changed, 223 insertions, 32 deletions
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java index 514059456279..e0ee68337061 100644 --- a/core/java/android/window/BackNavigationInfo.java +++ b/core/java/android/window/BackNavigationInfo.java @@ -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; @@ -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. @@ -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; } @@ -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 @@ -349,10 +381,25 @@ public final class BackNavigationInfo implements Parcelable { * Set windowAnimations for customize animation. */ public Builder setWindowAnimations(String packageName, int windowAnimations) { - mCustomAnimationInfo = new CustomAnimationInfo(packageName); + 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} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java index ae33b9445acd..4eaedd3136f1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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); } } @@ -191,6 +198,7 @@ class CustomizeActivityAnimation { mTransaction.apply(); mTransformation.clear(); mLatestProgress = 0; + mNextBackgroundColor = Color.TRANSPARENT; if (mFinishCallback != null) { try { mFinishCallback.onAnimationFinished(); @@ -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; @@ -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); @@ -355,5 +407,11 @@ class CustomizeActivityAnimation { } return a; } + + private Animation loadDefaultOpenAnimation() { + return mTransitionAnimation.loadDefaultAnimationAttr( + R.styleable.WindowAnimation_activityCloseEnterAnimation, + false /* translucent */); + } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java index 2814ef9e26cc..e7d459893ce8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java @@ -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; @@ -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) { @@ -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); @@ -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); @@ -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); + } + } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index b67bc62e52f1..fb79c0677a2f 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -262,14 +262,23 @@ 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) { - infoBuilder.setWindowAnimations( - window.mAttrs.packageName, window.mAttrs.windowAnimations); + && 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(); |