summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author wilsonshih <wilsonshih@google.com> 2023-02-23 17:34:20 +0800
committer wilsonshih <wilsonshih@google.com> 2023-03-20 10:55:07 +0800
commit850d69d3c8ef759e17d1c69fe602bb53ac92055a (patch)
treeeef8930ec63d285c9d340710c94a2169a5454095
parent25af92cdeb524b895f5de14e86e2d0a9e321ea71 (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
-rw-r--r--core/java/android/window/BackNavigationInfo.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomizeActivityAnimation.java98
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomizeActivityAnimationTest.java87
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java21
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();