Merge "Treat two TaskFragment leashes as one for TASK_FRAGMENT_OPEN/CLOSE" into sc-v2-dev am: df04e22915

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15959427

Change-Id: I3ace731ebd2e44e52cee7d10713b19cdd2085cef
This commit is contained in:
Chris Li
2021-10-10 11:29:29 +00:00
committed by Automerger Merge Worker
4 changed files with 198 additions and 59 deletions

View File

@@ -16,7 +16,8 @@
package androidx.window.extensions.embedding;
import android.graphics.Point;
import static android.graphics.Matrix.MSCALE_X;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -25,58 +26,151 @@ import android.view.animation.Animation;
import android.view.animation.Transformation;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
*
* The base adapter can be used for {@link RemoteAnimationTarget} that is simple open/close.
*/
class TaskFragmentAnimationAdapter {
private final Animation mAnimation;
private final RemoteAnimationTarget mTarget;
private final SurfaceControl mLeash;
private final boolean mSizeChanged;
private final Point mPosition;
private final Transformation mTransformation = new Transformation();
private final float[] mMatrix = new float[9];
private final float[] mVecs = new float[4];
private final Rect mRect = new Rect();
final Animation mAnimation;
final RemoteAnimationTarget mTarget;
final SurfaceControl mLeash;
final Transformation mTransformation = new Transformation();
final float[] mMatrix = new float[9];
private boolean mIsFirstFrame = true;
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target) {
this(animation, target, target.leash, false /* sizeChanged */, null /* position */);
this(animation, target, target.leash);
}
/**
* @param sizeChanged whether the surface size needs to be changed.
* @param leash the surface to animate.
*/
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
boolean sizeChanged, @Nullable Point position) {
@NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
mAnimation = animation;
mTarget = target;
mLeash = leash;
mSizeChanged = sizeChanged;
mPosition = position != null
? position
: new Point(target.localBounds.left, target.localBounds.top);
}
/** Called on frame update. */
void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
if (mIsFirstFrame) {
t.show(mLeash);
mIsFirstFrame = false;
}
currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
mAnimation.getTransformation(currentPlayTime, mTransformation);
mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
// Extract the transformation to the current time.
mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
mTransformation);
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
onAnimationUpdateInner(t);
}
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
mTransformation.getMatrix().postTranslate(
mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
}
/** Called after animation finished. */
final void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
onAnimationUpdate(t, mAnimation.getDuration());
}
final long getDurationHint() {
return mAnimation.computeDurationHint();
}
/**
* Should be used when the {@link RemoteAnimationTarget} is in split with others, and want 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 TaskFragmentAnimationAdapter {
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 RemoteAnimationTarget target,
boolean isLeftHalf, int wholeAnimationWidth) {
super(animation, target);
mIsLeftHalf = isLeftHalf;
mWholeAnimationWidth = wholeAnimationWidth;
if (wholeAnimationWidth == 0) {
throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
}
}
@Override
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
float posX = mTarget.localBounds.left;
final float posY = mTarget.localBounds.top;
// 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 targetWidth = mTarget.localBounds.width();
final float scaleX = mMatrix[MSCALE_X];
final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
final float curOffset = targetWidth * (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 RemoteAnimationTarget} that has
* size change.
*/
static class SnapshotAdapter extends TaskFragmentAnimationAdapter {
SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
// Start leash is the snapshot of the starting surface.
super(animation, target, target.startLeash);
}
@Override
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
// Snapshot should always be placed at the top left of the animation leash.
mTransformation.getMatrix().postTranslate(0, 0);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
}
}
/**
* Should be used for the animation of the {@link RemoteAnimationTarget} that has size change.
*/
static class BoundsChangeAdapter extends TaskFragmentAnimationAdapter {
private final float[] mVecs = new float[4];
private final Rect mRect = new Rect();
BoundsChangeAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
super(animation, target);
}
@Override
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
mTransformation.getMatrix().postTranslate(
mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
if (mSizeChanged) {
// The following applies an inverse scale to the clip-rect so that it crops "after" the
// scale instead of before.
mVecs[1] = mVecs[2] = 0;
@@ -92,13 +186,4 @@ class TaskFragmentAnimationAdapter {
t.setWindowCrop(mLeash, mRect);
}
}
/** Called after animation finished. */
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
onAnimationUpdate(t, mAnimation.getDuration());
}
long getDurationHint() {
return mAnimation.computeDurationHint();
}
}

View File

@@ -29,7 +29,6 @@ import android.window.TaskFragmentOrganizer;
class TaskFragmentAnimationController {
private static final String TAG = "TaskFragAnimationCtrl";
// TODO(b/196173550) turn off when finalize
static final boolean DEBUG = false;
private final TaskFragmentOrganizer mOrganizer;

View File

@@ -23,7 +23,7 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
/** To run the TaskFragment animations. */
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@@ -167,42 +168,86 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : targets) {
final Animation animation =
mAnimationSpec.loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
adapters.add(new TaskFragmentAnimationAdapter(animation, target));
}
return adapters;
return createOpenCloseAnimationAdapters(targets,
mAnimationSpec::loadOpenAnimation);
}
private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
return createOpenCloseAnimationAdapters(targets,
mAnimationSpec::loadCloseAnimation);
}
private List<TaskFragmentAnimationAdapter> createOpenCloseAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets,
@NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider) {
// We need to know if the target window is only a partial of the whole animation screen.
// If so, we will need to adjust it to make the whole animation screen looks like one.
final List<RemoteAnimationTarget> openingTargets = new ArrayList<>();
final List<RemoteAnimationTarget> closingTargets = new ArrayList<>();
final Rect openingWholeScreenBounds = new Rect();
final Rect closingWholeScreenBounds = new Rect();
for (RemoteAnimationTarget target : targets) {
final Animation animation =
mAnimationSpec.loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
adapters.add(new TaskFragmentAnimationAdapter(animation, target));
if (target.mode != MODE_CLOSING) {
openingTargets.add(target);
openingWholeScreenBounds.union(target.localBounds);
} else {
closingTargets.add(target);
closingWholeScreenBounds.union(target.localBounds);
}
}
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : openingTargets) {
adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
openingWholeScreenBounds));
}
for (RemoteAnimationTarget target : closingTargets) {
adapters.add(createOpenCloseAnimationAdapter(target, animationProvider,
closingWholeScreenBounds));
}
return adapters;
}
private TaskFragmentAnimationAdapter createOpenCloseAnimationAdapter(
@NonNull RemoteAnimationTarget target,
@NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
final Rect targetBounds = target.localBounds;
if (targetBounds.left == wholeAnimationBounds.left
&& targetBounds.right != wholeAnimationBounds.right) {
// This is the left split of the whole animation window.
return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
true /* isLeftHalf */, wholeAnimationBounds.width());
} else if (targetBounds.left != wholeAnimationBounds.left
&& targetBounds.right == wholeAnimationBounds.right) {
// This is the right split of the whole animation window.
return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
false /* isLeftHalf */, wholeAnimationBounds.width());
}
// Open/close window that fills the whole animation.
return new TaskFragmentAnimationAdapter(animation, target);
}
private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : targets) {
if (target.startBounds != null) {
// This is the target with bounds change.
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(target);
// The snapshot surface will always be at (0, 0) of its parent.
adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
target.startLeash, false /* sizeChanged */, new Point(0, 0)));
// The end surface will have size change for scaling.
adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
target.leash, true /* sizeChanged */, null /* position */));
// Adapter for the starting snapshot leash.
adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
animations[0], target));
// Adapter for the ending bounds changed leash.
adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
animations[1], target));
continue;
}
// These are the other targets that don't have bounds change in the same transition.
final Animation animation;
if (target.hasAnimatingParent) {
// No-op if it will be covered by the changing parent window.

View File

@@ -176,18 +176,28 @@ class TaskFragmentAnimationSpec {
return new Animation[]{startSet, endSet};
}
Animation loadOpenAnimation(boolean isEnter) {
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
@NonNull Rect wholeAnimationBounds) {
final boolean isEnter = target.mode != MODE_CLOSING;
final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
? R.styleable.WindowAnimation_activityOpenEnterAnimation
: R.styleable.WindowAnimation_activityOpenExitAnimation);
animation.initialize(target.localBounds.width(), target.localBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
}
Animation loadCloseAnimation(boolean isEnter) {
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
@NonNull Rect wholeAnimationBounds) {
final boolean isEnter = target.mode != MODE_CLOSING;
final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
? R.styleable.WindowAnimation_activityCloseEnterAnimation
: R.styleable.WindowAnimation_activityCloseExitAnimation);
animation.initialize(target.localBounds.width(), target.localBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
}
private class SettingsObserver extends ContentObserver {