Merge "Treat two TaskFragment leashes as one for TASK_FRAGMENT_OPEN/CLOSE" into sc-v2-dev
This commit is contained in:
@@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
package androidx.window.extensions.embedding;
|
package androidx.window.extensions.embedding;
|
||||||
|
|
||||||
import android.graphics.Point;
|
import static android.graphics.Matrix.MSCALE_X;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.view.Choreographer;
|
import android.view.Choreographer;
|
||||||
import android.view.RemoteAnimationTarget;
|
import android.view.RemoteAnimationTarget;
|
||||||
@@ -25,58 +26,151 @@ import android.view.animation.Animation;
|
|||||||
import android.view.animation.Transformation;
|
import android.view.animation.Transformation;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper to handle the TaskFragment animation update in one {@link SurfaceControl.Transaction}.
|
* 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 {
|
class TaskFragmentAnimationAdapter {
|
||||||
private final Animation mAnimation;
|
final Animation mAnimation;
|
||||||
private final RemoteAnimationTarget mTarget;
|
final RemoteAnimationTarget mTarget;
|
||||||
private final SurfaceControl mLeash;
|
final SurfaceControl mLeash;
|
||||||
private final boolean mSizeChanged;
|
|
||||||
private final Point mPosition;
|
final Transformation mTransformation = new Transformation();
|
||||||
private final Transformation mTransformation = new Transformation();
|
final float[] mMatrix = new float[9];
|
||||||
private final float[] mMatrix = new float[9];
|
|
||||||
private final float[] mVecs = new float[4];
|
|
||||||
private final Rect mRect = new Rect();
|
|
||||||
private boolean mIsFirstFrame = true;
|
private boolean mIsFirstFrame = true;
|
||||||
|
|
||||||
TaskFragmentAnimationAdapter(@NonNull Animation animation,
|
TaskFragmentAnimationAdapter(@NonNull Animation animation,
|
||||||
@NonNull RemoteAnimationTarget target) {
|
@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,
|
TaskFragmentAnimationAdapter(@NonNull Animation animation,
|
||||||
@NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
|
@NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
|
||||||
boolean sizeChanged, @Nullable Point position) {
|
|
||||||
mAnimation = animation;
|
mAnimation = animation;
|
||||||
mTarget = target;
|
mTarget = target;
|
||||||
mLeash = leash;
|
mLeash = leash;
|
||||||
mSizeChanged = sizeChanged;
|
|
||||||
mPosition = position != null
|
|
||||||
? position
|
|
||||||
: new Point(target.localBounds.left, target.localBounds.top);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called on frame update. */
|
/** Called on frame update. */
|
||||||
void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
|
final void onAnimationUpdate(@NonNull SurfaceControl.Transaction t, long currentPlayTime) {
|
||||||
if (mIsFirstFrame) {
|
if (mIsFirstFrame) {
|
||||||
t.show(mLeash);
|
t.show(mLeash);
|
||||||
mIsFirstFrame = false;
|
mIsFirstFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayTime = Math.min(currentPlayTime, mAnimation.getDuration());
|
// Extract the transformation to the current time.
|
||||||
mAnimation.getTransformation(currentPlayTime, mTransformation);
|
mAnimation.getTransformation(Math.min(currentPlayTime, mAnimation.getDuration()),
|
||||||
mTransformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
|
||||||
t.setAlpha(mLeash, mTransformation.getAlpha());
|
t.setAlpha(mLeash, mTransformation.getAlpha());
|
||||||
t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId());
|
|
||||||
|
|
||||||
if (mSizeChanged) {
|
|
||||||
// The following applies an inverse scale to the clip-rect so that it crops "after" the
|
// The following applies an inverse scale to the clip-rect so that it crops "after" the
|
||||||
// scale instead of before.
|
// scale instead of before.
|
||||||
mVecs[1] = mVecs[2] = 0;
|
mVecs[1] = mVecs[2] = 0;
|
||||||
@@ -92,13 +186,4 @@ class TaskFragmentAnimationAdapter {
|
|||||||
t.setWindowCrop(mLeash, mRect);
|
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 {
|
class TaskFragmentAnimationController {
|
||||||
|
|
||||||
private static final String TAG = "TaskFragAnimationCtrl";
|
private static final String TAG = "TaskFragAnimationCtrl";
|
||||||
// TODO(b/196173550) turn off when finalize
|
|
||||||
static final boolean DEBUG = false;
|
static final boolean DEBUG = false;
|
||||||
|
|
||||||
private final TaskFragmentOrganizer mOrganizer;
|
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.Animator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.graphics.Point;
|
import android.graphics.Rect;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
/** To run the TaskFragment animations. */
|
/** To run the TaskFragment animations. */
|
||||||
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
||||||
@@ -167,42 +168,86 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
|
|||||||
|
|
||||||
private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
|
private List<TaskFragmentAnimationAdapter> createOpenAnimationAdapters(
|
||||||
@NonNull RemoteAnimationTarget[] targets) {
|
@NonNull RemoteAnimationTarget[] targets) {
|
||||||
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
|
return createOpenCloseAnimationAdapters(targets,
|
||||||
for (RemoteAnimationTarget target : targets) {
|
mAnimationSpec::loadOpenAnimation);
|
||||||
final Animation animation =
|
|
||||||
mAnimationSpec.loadOpenAnimation(target.mode != MODE_CLOSING /* isEnter */);
|
|
||||||
adapters.add(new TaskFragmentAnimationAdapter(animation, target));
|
|
||||||
}
|
|
||||||
return adapters;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
|
private List<TaskFragmentAnimationAdapter> createCloseAnimationAdapters(
|
||||||
@NonNull RemoteAnimationTarget[] targets) {
|
@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) {
|
for (RemoteAnimationTarget target : targets) {
|
||||||
final Animation animation =
|
if (target.mode != MODE_CLOSING) {
|
||||||
mAnimationSpec.loadCloseAnimation(target.mode != MODE_CLOSING /* isEnter */);
|
openingTargets.add(target);
|
||||||
adapters.add(new TaskFragmentAnimationAdapter(animation, 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;
|
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(
|
private List<TaskFragmentAnimationAdapter> createChangeAnimationAdapters(
|
||||||
@NonNull RemoteAnimationTarget[] targets) {
|
@NonNull RemoteAnimationTarget[] targets) {
|
||||||
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
|
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
|
||||||
for (RemoteAnimationTarget target : targets) {
|
for (RemoteAnimationTarget target : targets) {
|
||||||
if (target.startBounds != null) {
|
if (target.startBounds != null) {
|
||||||
|
// This is the target with bounds change.
|
||||||
final Animation[] animations =
|
final Animation[] animations =
|
||||||
mAnimationSpec.createChangeBoundsChangeAnimations(target);
|
mAnimationSpec.createChangeBoundsChangeAnimations(target);
|
||||||
// The snapshot surface will always be at (0, 0) of its parent.
|
// Adapter for the starting snapshot leash.
|
||||||
adapters.add(new TaskFragmentAnimationAdapter(animations[0], target,
|
adapters.add(new TaskFragmentAnimationAdapter.SnapshotAdapter(
|
||||||
target.startLeash, false /* sizeChanged */, new Point(0, 0)));
|
animations[0], target));
|
||||||
// The end surface will have size change for scaling.
|
// Adapter for the ending bounds changed leash.
|
||||||
adapters.add(new TaskFragmentAnimationAdapter(animations[1], target,
|
adapters.add(new TaskFragmentAnimationAdapter.BoundsChangeAdapter(
|
||||||
target.leash, true /* sizeChanged */, null /* position */));
|
animations[1], target));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These are the other targets that don't have bounds change in the same transition.
|
||||||
final Animation animation;
|
final Animation animation;
|
||||||
if (target.hasAnimatingParent) {
|
if (target.hasAnimatingParent) {
|
||||||
// No-op if it will be covered by the changing parent window.
|
// No-op if it will be covered by the changing parent window.
|
||||||
|
|||||||
@@ -176,18 +176,28 @@ class TaskFragmentAnimationSpec {
|
|||||||
return new Animation[]{startSet, endSet};
|
return new Animation[]{startSet, endSet};
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation loadOpenAnimation(boolean isEnter) {
|
Animation loadOpenAnimation(@NonNull RemoteAnimationTarget target,
|
||||||
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
|
@NonNull Rect wholeAnimationBounds) {
|
||||||
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
final boolean isEnter = target.mode != MODE_CLOSING;
|
||||||
|
final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
||||||
? R.styleable.WindowAnimation_activityOpenEnterAnimation
|
? R.styleable.WindowAnimation_activityOpenEnterAnimation
|
||||||
: R.styleable.WindowAnimation_activityOpenExitAnimation);
|
: 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) {
|
Animation loadCloseAnimation(@NonNull RemoteAnimationTarget target,
|
||||||
// TODO(b/196173550) We need to customize the animation to handle two open window as one.
|
@NonNull Rect wholeAnimationBounds) {
|
||||||
return mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
final boolean isEnter = target.mode != MODE_CLOSING;
|
||||||
|
final Animation animation = mTransitionAnimation.loadDefaultAnimationAttr(isEnter
|
||||||
? R.styleable.WindowAnimation_activityCloseEnterAnimation
|
? R.styleable.WindowAnimation_activityCloseEnterAnimation
|
||||||
: R.styleable.WindowAnimation_activityCloseExitAnimation);
|
: 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 {
|
private class SettingsObserver extends ContentObserver {
|
||||||
|
|||||||
Reference in New Issue
Block a user