Merge "Update ActivityEmbedding split open/close animation adapter for Shell" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-09-15 06:27:44 +00:00
committed by Android (Google) Code Review
3 changed files with 49 additions and 89 deletions

View File

@@ -16,7 +16,6 @@
package com.android.wm.shell.activityembedding; package com.android.wm.shell.activityembedding;
import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MTRANS_X; import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y; import static android.graphics.Matrix.MTRANS_Y;
@@ -42,31 +41,45 @@ class ActivityEmbeddingAnimationAdapter {
*/ */
private static final int LAYER_NO_OVERRIDE = -1; private static final int LAYER_NO_OVERRIDE = -1;
@NonNull
final Animation mAnimation; final Animation mAnimation;
@NonNull
final TransitionInfo.Change mChange; final TransitionInfo.Change mChange;
@NonNull
final SurfaceControl mLeash; final SurfaceControl mLeash;
/** Area in absolute coordinate that the animation surface shouldn't go beyond. */
@NonNull
private final Rect mWholeAnimationBounds = new Rect();
@NonNull
final Transformation mTransformation = new Transformation(); final Transformation mTransformation = new Transformation();
@NonNull
final float[] mMatrix = new float[9]; final float[] mMatrix = new float[9];
@NonNull
final float[] mVecs = new float[4]; final float[] mVecs = new float[4];
@NonNull
final Rect mRect = new Rect(); final Rect mRect = new Rect();
private boolean mIsFirstFrame = true; private boolean mIsFirstFrame = true;
private int mOverrideLayer = LAYER_NO_OVERRIDE; private int mOverrideLayer = LAYER_NO_OVERRIDE;
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation, ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
@NonNull TransitionInfo.Change change) { @NonNull TransitionInfo.Change change) {
this(animation, change, change.getLeash()); this(animation, change, change.getLeash(), change.getEndAbsBounds());
} }
/** /**
* @param leash the surface to animate, which is not necessary the same as * @param leash the surface to animate, which is not necessary the same as
* {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example. * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
* @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
* go beyond.
*/ */
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation, ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
@NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash) { @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash,
@NonNull Rect wholeAnimationBounds) {
mAnimation = animation; mAnimation = animation;
mChange = change; mChange = change;
mLeash = leash; mLeash = leash;
mWholeAnimationBounds.set(wholeAnimationBounds);
} }
/** /**
@@ -96,23 +109,31 @@ class ActivityEmbeddingAnimationAdapter {
/** To be overridden by subclasses to adjust the animation surface change. */ /** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) { void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
// Update the surface position and alpha.
final Point offset = mChange.getEndRelOffset(); final Point offset = mChange.getEndRelOffset();
mTransformation.getMatrix().postTranslate(offset.x, offset.y); mTransformation.getMatrix().postTranslate(offset.x, offset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix); t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha()); t.setAlpha(mLeash, mTransformation.getAlpha());
// Get current animation position.
// Get current surface bounds in absolute coordinate.
// positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]); final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]); final int positionY = Math.round(mMatrix[MTRANS_Y]);
// The exiting surface starts at position: Change#getEndRelOffset() and moves with final Rect cropRect = new Rect(mChange.getEndAbsBounds());
// positionX varying. Offset our crop region by the amount we have slided so crop cropRect.offset(positionX - offset.x, positionY - offset.y);
// regions stays exactly on the original container in split.
final int cropOffsetX = offset.x - positionX; // Store the current offset of the surface top left from (0,0) in absolute coordinate.
final int cropOffsetY = offset.y - positionY; final int offsetX = cropRect.left;
final Rect cropRect = new Rect(); final int offsetY = cropRect.top;
cropRect.set(mChange.getEndAbsBounds());
// Because window crop uses absolute position. // Intersect to make sure the animation happens within the whole animation bounds.
cropRect.offsetTo(0, 0); if (!cropRect.intersect(mWholeAnimationBounds)) {
cropRect.offset(cropOffsetX, cropOffsetY); // Hide the surface when it is outside of the animation area.
t.setAlpha(mLeash, 0);
}
// cropRect is in absolute coordinate, so we need to translate it to surface top left.
cropRect.offset(-offsetX, -offsetY);
t.setCrop(mLeash, cropRect); t.setCrop(mLeash, cropRect);
} }
@@ -126,53 +147,6 @@ class ActivityEmbeddingAnimationAdapter {
return mAnimation.computeDurationHint(); return mAnimation.computeDurationHint();
} }
/**
* Should be used when the {@link TransitionInfo.Change} is in split with others, and wants to
* animate together as one. This adapter will offset the animation leash to make the animate of
* two windows look like a single window.
*/
static class SplitAdapter extends ActivityEmbeddingAnimationAdapter {
private final boolean mIsLeftHalf;
private final int mWholeAnimationWidth;
/**
* @param isLeftHalf whether this is the left half of the animation.
* @param wholeAnimationWidth the whole animation windows width.
*/
SplitAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
boolean isLeftHalf, int wholeAnimationWidth) {
super(animation, change);
mIsLeftHalf = isLeftHalf;
mWholeAnimationWidth = wholeAnimationWidth;
if (wholeAnimationWidth == 0) {
throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
}
}
@Override
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
final Point offset = mChange.getEndRelOffset();
float posX = offset.x;
final float posY = offset.y;
// This window is half of the whole animation window. Offset left/right to make it
// look as one with the other half.
mTransformation.getMatrix().getValues(mMatrix);
final int changeWidth = mChange.getEndAbsBounds().width();
final float scaleX = mMatrix[MSCALE_X];
final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
final float curOffset = changeWidth * (1 - scaleX) / 2;
final float offsetDiff = totalOffset - curOffset;
if (mIsLeftHalf) {
posX += offsetDiff;
} else {
posX -= offsetDiff;
}
mTransformation.getMatrix().postTranslate(posX, posY);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
}
}
/** /**
* Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has * Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has
* size change. * size change.
@@ -181,7 +155,7 @@ class ActivityEmbeddingAnimationAdapter {
SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change, SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
@NonNull SurfaceControl snapshotLeash) { @NonNull SurfaceControl snapshotLeash) {
super(animation, change, snapshotLeash); super(animation, change, snapshotLeash, change.getEndAbsBounds());
} }
@Override @Override

View File

@@ -22,7 +22,6 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import android.animation.Animator; import android.animation.Animator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
@@ -169,15 +168,12 @@ class ActivityEmbeddingAnimationRunner {
final Rect openingWholeScreenBounds = new Rect(); final Rect openingWholeScreenBounds = new Rect();
final Rect closingWholeScreenBounds = new Rect(); final Rect closingWholeScreenBounds = new Rect();
for (TransitionInfo.Change change : info.getChanges()) { for (TransitionInfo.Change change : info.getChanges()) {
final Rect bounds = new Rect(change.getEndAbsBounds());
final Point offset = change.getEndRelOffset();
bounds.offsetTo(offset.x, offset.y);
if (Transitions.isOpeningType(change.getMode())) { if (Transitions.isOpeningType(change.getMode())) {
openingChanges.add(change); openingChanges.add(change);
openingWholeScreenBounds.union(bounds); openingWholeScreenBounds.union(change.getEndAbsBounds());
} else { } else {
closingChanges.add(change); closingChanges.add(change);
closingWholeScreenBounds.union(bounds); closingWholeScreenBounds.union(change.getEndAbsBounds());
} }
} }
@@ -210,22 +206,8 @@ class ActivityEmbeddingAnimationRunner {
@NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider, @NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) { @NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(change, wholeAnimationBounds); final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
final Rect bounds = new Rect(change.getEndAbsBounds()); return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
final Point offset = change.getEndRelOffset(); wholeAnimationBounds);
bounds.offsetTo(offset.x, offset.y);
if (bounds.left == wholeAnimationBounds.left
&& bounds.right != wholeAnimationBounds.right) {
// This is the left split of the whole animation window.
return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
true /* isLeftHalf */, wholeAnimationBounds.width());
} else if (bounds.left != wholeAnimationBounds.left
&& bounds.right == wholeAnimationBounds.right) {
// This is the right split of the whole animation window.
return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
false /* isLeftHalf */, wholeAnimationBounds.width());
}
// Open/close window that fills the whole animation.
return new ActivityEmbeddingAnimationAdapter(animation, change);
} }
@NonNull @NonNull

View File

@@ -185,8 +185,10 @@ class ActivityEmbeddingAnimationSpec {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_open_enter ? R.anim.task_fragment_open_enter
: R.anim.task_fragment_open_exit); : R.anim.task_fragment_open_exit);
final Rect bounds = change.getEndAbsBounds(); // Use the whole animation bounds instead of the change bounds, so that when multiple change
animation.initialize(bounds.width(), bounds.height(), // targets are opening at the same time, the animation applied to each will be the same.
// Otherwise, we may see gap between the activities that are launching together.
animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height()); wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation; return animation;
@@ -203,8 +205,10 @@ class ActivityEmbeddingAnimationSpec {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_close_enter ? R.anim.task_fragment_close_enter
: R.anim.task_fragment_close_exit); : R.anim.task_fragment_close_exit);
final Rect bounds = change.getEndAbsBounds(); // Use the whole animation bounds instead of the change bounds, so that when multiple change
animation.initialize(bounds.width(), bounds.height(), // targets are closing at the same time, the animation applied to each will be the same.
// Otherwise, we may see gap between the activities that are finishing together.
animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height()); wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting); animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation; return animation;