Fix enter/exit horizontal ActivityEmbedding split with Shell transition
1. For animation spec, before, we only consider animation to/from left/right. Now, we also check top/bottom. 2. For animation adapter, before, we always use the window end bounds, which may be empty when closing a window that is also resizing during the transition. Now, for closing window, we use the start bounds instead. Bug: 241043533 Test: Verify with launching activity into horizontal split. Test: Verify with switching between split and stack. Change-Id: I4fddcb2327d05e90b0c12605490fa7d7aa987fa0
This commit is contained in:
@@ -30,6 +30,8 @@ import android.window.TransitionInfo;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
/**
|
||||
* Wrapper to handle the ActivityEmbedding animation update in one
|
||||
* {@link SurfaceControl.Transaction}.
|
||||
@@ -50,6 +52,16 @@ class ActivityEmbeddingAnimationAdapter {
|
||||
/** Area in absolute coordinate that the animation surface shouldn't go beyond. */
|
||||
@NonNull
|
||||
private final Rect mWholeAnimationBounds = new Rect();
|
||||
/**
|
||||
* Area in absolute coordinate that should represent all the content to show for this window.
|
||||
* This should be the end bounds for opening window, and start bounds for closing window in case
|
||||
* the window is resizing during the open/close transition.
|
||||
*/
|
||||
@NonNull
|
||||
private final Rect mContentBounds = new Rect();
|
||||
/** Offset relative to the window parent surface for {@link #mContentBounds}. */
|
||||
@NonNull
|
||||
private final Point mContentRelOffset = new Point();
|
||||
|
||||
@NonNull
|
||||
final Transformation mTransformation = new Transformation();
|
||||
@@ -80,6 +92,21 @@ class ActivityEmbeddingAnimationAdapter {
|
||||
mChange = change;
|
||||
mLeash = leash;
|
||||
mWholeAnimationBounds.set(wholeAnimationBounds);
|
||||
if (Transitions.isClosingType(change.getMode())) {
|
||||
// When it is closing, we want to show the content at the start position in case the
|
||||
// window is resizing as well. For example, when the activities is changing from split
|
||||
// to stack, the bottom TaskFragment will be resized to fullscreen when hiding.
|
||||
final Rect startBounds = change.getStartAbsBounds();
|
||||
final Rect endBounds = change.getEndAbsBounds();
|
||||
mContentBounds.set(startBounds);
|
||||
mContentRelOffset.set(change.getEndRelOffset());
|
||||
mContentRelOffset.offset(
|
||||
startBounds.left - endBounds.left,
|
||||
startBounds.top - endBounds.top);
|
||||
} else {
|
||||
mContentBounds.set(change.getEndAbsBounds());
|
||||
mContentRelOffset.set(change.getEndRelOffset());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,8 +137,7 @@ class ActivityEmbeddingAnimationAdapter {
|
||||
/** To be overridden by subclasses to adjust the animation surface change. */
|
||||
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
|
||||
// Update the surface position and alpha.
|
||||
final Point offset = mChange.getEndRelOffset();
|
||||
mTransformation.getMatrix().postTranslate(offset.x, offset.y);
|
||||
mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
|
||||
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
|
||||
t.setAlpha(mLeash, mTransformation.getAlpha());
|
||||
|
||||
@@ -119,8 +145,8 @@ class ActivityEmbeddingAnimationAdapter {
|
||||
// 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 positionY = Math.round(mMatrix[MTRANS_Y]);
|
||||
final Rect cropRect = new Rect(mChange.getEndAbsBounds());
|
||||
cropRect.offset(positionX - offset.x, positionY - offset.y);
|
||||
final Rect cropRect = new Rect(mContentBounds);
|
||||
cropRect.offset(positionX - mContentRelOffset.x, positionY - mContentRelOffset.y);
|
||||
|
||||
// Store the current offset of the surface top left from (0,0) in absolute coordinate.
|
||||
final int offsetX = cropRect.left;
|
||||
@@ -133,7 +159,7 @@ class ActivityEmbeddingAnimationAdapter {
|
||||
} else if (mAnimation.hasExtension()) {
|
||||
// Allow the surface to be shown in its original bounds in case we want to use edge
|
||||
// extensions.
|
||||
cropRect.union(mChange.getEndAbsBounds());
|
||||
cropRect.union(mContentBounds);
|
||||
}
|
||||
|
||||
// cropRect is in absolute coordinate, so we need to translate it to surface top left.
|
||||
|
||||
@@ -304,6 +304,7 @@ class ActivityEmbeddingAnimationRunner {
|
||||
// This is because the TaskFragment surface/change won't contain the Activity's before its
|
||||
// reparent.
|
||||
Animation changeAnimation = null;
|
||||
Rect parentBounds = new Rect();
|
||||
for (TransitionInfo.Change change : info.getChanges()) {
|
||||
if (change.getMode() != TRANSIT_CHANGE
|
||||
|| change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
|
||||
@@ -326,10 +327,15 @@ class ActivityEmbeddingAnimationRunner {
|
||||
}
|
||||
}
|
||||
|
||||
// The TaskFragment may be enter/exit split, so we take the union of both as the parent
|
||||
// size.
|
||||
parentBounds.union(boundsAnimationChange.getStartAbsBounds());
|
||||
parentBounds.union(boundsAnimationChange.getEndAbsBounds());
|
||||
|
||||
// There are two animations in the array. The first one is for the start leash
|
||||
// (snapshot), and the second one is for the end leash (TaskFragment).
|
||||
final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
|
||||
boundsAnimationChange.getEndAbsBounds());
|
||||
parentBounds);
|
||||
// Keep track as we might need to add background color for the animation.
|
||||
// Although there may be multiple change animation, record one of them is sufficient
|
||||
// because the background color will be added to the root leash for the whole animation.
|
||||
@@ -352,6 +358,11 @@ class ActivityEmbeddingAnimationRunner {
|
||||
animations[1], boundsAnimationChange));
|
||||
}
|
||||
|
||||
if (parentBounds.isEmpty()) {
|
||||
throw new IllegalStateException(
|
||||
"There should be at least one changing window to play the change animation");
|
||||
}
|
||||
|
||||
// If there is no corresponding open/close window with the change, we should show background
|
||||
// color to cover the empty part of the screen.
|
||||
boolean shouldShouldBackgroundColor = true;
|
||||
@@ -368,10 +379,10 @@ class ActivityEmbeddingAnimationRunner {
|
||||
// No-op if it will be covered by the changing parent window.
|
||||
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
|
||||
} else if (Transitions.isClosingType(change.getMode())) {
|
||||
animation = mAnimationSpec.createChangeBoundsCloseAnimation(change);
|
||||
animation = mAnimationSpec.createChangeBoundsCloseAnimation(change, parentBounds);
|
||||
shouldShouldBackgroundColor = false;
|
||||
} else {
|
||||
animation = mAnimationSpec.createChangeBoundsOpenAnimation(change);
|
||||
animation = mAnimationSpec.createChangeBoundsOpenAnimation(change, parentBounds);
|
||||
shouldShouldBackgroundColor = false;
|
||||
}
|
||||
adapters.add(new ActivityEmbeddingAnimationAdapter(animation, change));
|
||||
|
||||
@@ -21,7 +21,6 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
|
||||
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.Animation;
|
||||
@@ -80,15 +79,25 @@ class ActivityEmbeddingAnimationSpec {
|
||||
|
||||
/** Animation for window that is opening in a change transition. */
|
||||
@NonNull
|
||||
Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change) {
|
||||
Animation createChangeBoundsOpenAnimation(@NonNull TransitionInfo.Change change,
|
||||
@NonNull Rect parentBounds) {
|
||||
// Use end bounds for opening.
|
||||
final Rect bounds = change.getEndAbsBounds();
|
||||
final Point offset = change.getEndRelOffset();
|
||||
// The window will be animated in from left or right depends on its position.
|
||||
final int startLeft = offset.x == 0 ? -bounds.width() : bounds.width();
|
||||
final int startLeft;
|
||||
final int startTop;
|
||||
if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
|
||||
// The window will be animated in from left or right depends on its position.
|
||||
startTop = 0;
|
||||
startLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
|
||||
} else {
|
||||
// The window will be animated in from top or bottom depends on its position.
|
||||
startTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
|
||||
startLeft = 0;
|
||||
}
|
||||
|
||||
// The position should be 0-based as we will post translate in
|
||||
// ActivityEmbeddingAnimationAdapter#onAnimationUpdate
|
||||
final Animation animation = new TranslateAnimation(startLeft, 0, 0, 0);
|
||||
final Animation animation = new TranslateAnimation(startLeft, 0, startTop, 0);
|
||||
animation.setInterpolator(mFastOutExtraSlowInInterpolator);
|
||||
animation.setDuration(CHANGE_ANIMATION_DURATION);
|
||||
animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
|
||||
@@ -98,15 +107,25 @@ class ActivityEmbeddingAnimationSpec {
|
||||
|
||||
/** Animation for window that is closing in a change transition. */
|
||||
@NonNull
|
||||
Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change) {
|
||||
final Rect bounds = change.getEndAbsBounds();
|
||||
final Point offset = change.getEndRelOffset();
|
||||
// The window will be animated out to left or right depends on its position.
|
||||
final int endLeft = offset.x == 0 ? -bounds.width() : bounds.width();
|
||||
Animation createChangeBoundsCloseAnimation(@NonNull TransitionInfo.Change change,
|
||||
@NonNull Rect parentBounds) {
|
||||
// Use start bounds for closing.
|
||||
final Rect bounds = change.getStartAbsBounds();
|
||||
final int endTop;
|
||||
final int endLeft;
|
||||
if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {
|
||||
// The window will be animated out to left or right depends on its position.
|
||||
endTop = 0;
|
||||
endLeft = parentBounds.left == bounds.left ? -bounds.width() : bounds.width();
|
||||
} else {
|
||||
// The window will be animated out to top or bottom depends on its position.
|
||||
endTop = parentBounds.top == bounds.top ? -bounds.height() : bounds.height();
|
||||
endLeft = 0;
|
||||
}
|
||||
|
||||
// The position should be 0-based as we will post translate in
|
||||
// ActivityEmbeddingAnimationAdapter#onAnimationUpdate
|
||||
final Animation animation = new TranslateAnimation(0, endLeft, 0, 0);
|
||||
final Animation animation = new TranslateAnimation(0, endLeft, 0, endTop);
|
||||
animation.setInterpolator(mFastOutExtraSlowInInterpolator);
|
||||
animation.setDuration(CHANGE_ANIMATION_DURATION);
|
||||
animation.initialize(bounds.width(), bounds.height(), bounds.width(), bounds.height());
|
||||
|
||||
Reference in New Issue
Block a user