summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();