diff options
| author | 2023-02-23 17:34:20 +0800 | |
|---|---|---|
| committer | 2023-03-20 10:55:07 +0800 | |
| commit | 850d69d3c8ef759e17d1c69fe602bb53ac92055a (patch) | |
| tree | eef8930ec63d285c9d340710c94a2169a5454095 /libs | |
| parent | 25af92cdeb524b895f5de14e86e2d0a9e321ea71 (diff) | |
Play seekable animation for customize activity transition API.(2/N)
Add supporting for Activity#overrideActivityTransition.
The CustomizeActivityAnimation will load both enter and exit animation
for the close activity transition. And it is only valid if the exit
animation has set and loaded success. If the entering animation has not
set(i.e. 0), there will load the default entering animation for it.
Also note that if both overrideActivityTransition and windowAnimations
has set, system should prior to load the animation set from
overrideActivityTransition.
Bug: 259427810
Test: atest ActivityTransitionTests BackNavigationControllerTests\
CustomizeActivityAnimationTest
Change-Id: Id182dc8f93f55a256de68c1fb6cc91fd08450e3e
Diffstat (limited to 'libs')
2 files changed, 160 insertions, 25 deletions
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); + } + } } |