Merge "Play seekable animation for customize activity transition API.(2/N)" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
d55c38e435
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
/**
|
||||
* 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 Animation close = loadAnimation(animationInfo, false);
|
||||
if (close == null) {
|
||||
return null;
|
||||
}
|
||||
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) {
|
||||
final String packageName = animationInfo.getPackageName();
|
||||
if (packageName.isEmpty()) {
|
||||
return null;
|
||||
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 */);
|
||||
}
|
||||
final int windowAnimations = animationInfo.getWindowAnimations();
|
||||
if (windowAnimations == 0) {
|
||||
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 */);
|
||||
// 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 */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user