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:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user