Merge "Provide startBounds for closing container with legacy transition" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-12-03 02:26:52 +00:00
committed by Android (Google) Code Review
9 changed files with 173 additions and 39 deletions

View File

@@ -35,6 +35,7 @@ import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import android.annotation.ColorInt;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
@@ -175,10 +176,16 @@ public class RemoteAnimationTarget implements Parcelable {
public final Rect screenSpaceBounds;
/**
* The starting bounds of the source container in screen space coordinates. This is {@code null}
* if the animation target isn't MODE_CHANGING. Since this is the starting bounds, it's size
* should be equivalent to the size of the starting thumbnail. Note that sourceContainerBounds
* is the end bounds of a change transition.
* The starting bounds of the source container in screen space coordinates.
* For {@link #MODE_OPENING}, this will be equivalent to {@link #screenSpaceBounds}.
* For {@link #MODE_CLOSING}, this will be equivalent to {@link #screenSpaceBounds} unless the
* closing container is also resizing. For example, when ActivityEmbedding split pair becomes
* stacked, the container on the back will be resized to fullscreen, but will also be covered
* (closing) by the container in the front.
* For {@link #MODE_CHANGING}, since this is the starting bounds, its size should be equivalent
* to the bounds of the starting thumbnail.
*
* Note that {@link #screenSpaceBounds} is the end bounds of a transition.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public final Rect startBounds;
@@ -247,7 +254,8 @@ public class RemoteAnimationTarget implements Parcelable {
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
SurfaceControl startLeash, Rect startBounds, ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl startLeash, @Nullable Rect startBounds,
ActivityManager.RunningTaskInfo taskInfo,
boolean allowEnterPip) {
this(taskId, mode, leash, isTranslucent, clipRect, contentInsets, prefixOrderIndex,
position, localBounds, screenSpaceBounds, windowConfig, isNotInRecents, startLeash,
@@ -258,7 +266,7 @@ public class RemoteAnimationTarget implements Parcelable {
Rect clipRect, Rect contentInsets, int prefixOrderIndex, Point position,
Rect localBounds, Rect screenSpaceBounds,
WindowConfiguration windowConfig, boolean isNotInRecents,
SurfaceControl startLeash, Rect startBounds,
SurfaceControl startLeash, @Nullable Rect startBounds,
ActivityManager.RunningTaskInfo taskInfo, boolean allowEnterPip,
@WindowManager.LayoutParams.WindowType int windowType) {
this.mode = mode;
@@ -275,10 +283,13 @@ public class RemoteAnimationTarget implements Parcelable {
this.windowConfiguration = windowConfig;
this.isNotInRecents = isNotInRecents;
this.startLeash = startLeash;
this.startBounds = startBounds == null ? null : new Rect(startBounds);
this.taskInfo = taskInfo;
this.allowEnterPip = allowEnterPip;
this.windowType = windowType;
// Same as screenSpaceBounds if the window is not resizing.
this.startBounds = startBounds == null
? new Rect(screenSpaceBounds)
: new Rect(startBounds);
}
public RemoteAnimationTarget(Parcel in) {
@@ -399,9 +410,7 @@ public class RemoteAnimationTarget implements Parcelable {
if (startLeash != null) {
startLeash.dumpDebug(proto, START_LEASH);
}
if (startBounds != null) {
startBounds.dumpDebug(proto, START_BOUNDS);
}
startBounds.dumpDebug(proto, START_BOUNDS);
proto.end(token);
}

View File

@@ -18,7 +18,9 @@ package androidx.window.extensions.embedding;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.Choreographer;
import android.view.RemoteAnimationTarget;
@@ -49,6 +51,16 @@ class TaskFragmentAnimationAdapter {
/** 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();
@@ -78,6 +90,21 @@ class TaskFragmentAnimationAdapter {
mTarget = target;
mLeash = leash;
mWholeAnimationBounds.set(wholeAnimationBounds);
if (target.mode == MODE_CLOSING) {
// 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 = target.startBounds;
final Rect endBounds = target.screenSpaceBounds;
mContentBounds.set(startBounds);
mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
mContentRelOffset.offset(
startBounds.left - endBounds.left,
startBounds.top - endBounds.top);
} else {
mContentBounds.set(target.screenSpaceBounds);
mContentRelOffset.set(target.localBounds.left, target.localBounds.top);
}
}
/**
@@ -108,8 +135,7 @@ class TaskFragmentAnimationAdapter {
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
// Update the surface position and alpha.
mTransformation.getMatrix().postTranslate(
mTarget.localBounds.left, mTarget.localBounds.top);
mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
@@ -117,9 +143,8 @@ class TaskFragmentAnimationAdapter {
// 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(mTarget.screenSpaceBounds);
final Rect localBounds = mTarget.localBounds;
cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
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;

View File

@@ -17,6 +17,7 @@
package androidx.window.extensions.embedding;
import static android.os.Process.THREAD_PRIORITY_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
@@ -254,7 +255,7 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@NonNull RemoteAnimationTarget[] targets) {
final List<TaskFragmentAnimationAdapter> adapters = new ArrayList<>();
for (RemoteAnimationTarget target : targets) {
if (target.startBounds != null) {
if (target.mode == MODE_CHANGING) {
// This is the target with bounds change.
final Animation[] animations =
mAnimationSpec.createChangeBoundsChangeAnimations(target);

View File

@@ -114,8 +114,8 @@ class TaskFragmentAnimationSpec {
@NonNull
Animation createChangeBoundsCloseAnimation(@NonNull RemoteAnimationTarget target) {
final Rect parentBounds = target.taskInfo.configuration.windowConfiguration.getBounds();
// TODO(b/258126915): we want to keep track of the closing start bounds
final Rect bounds = target.screenSpaceBounds;
// Use startBounds if the window is closing in case it may also resize.
final Rect bounds = target.startBounds;
final int endTop;
final int endLeft;
if (parentBounds.top == bounds.top && parentBounds.bottom == bounds.bottom) {

View File

@@ -268,6 +268,7 @@ public class AppTransitionController {
handleClosingApps();
handleOpeningApps();
handleChangingApps(transit);
handleClosingChangingContainers();
appTransition.setLastAppTransition(transit, topOpeningApp,
topClosingApp, topChangingApp);
@@ -287,6 +288,7 @@ public class AppTransitionController {
mDisplayContent.mClosingApps.clear();
mDisplayContent.mChangingContainers.clear();
mDisplayContent.mUnknownAppVisibilityController.clear();
mDisplayContent.mClosingChangingContainers.clear();
// This has changed the visibility of windows, so perform
// a new layout to get them all up-to-date.
@@ -1171,6 +1173,24 @@ public class AppTransitionController {
}
}
private void handleClosingChangingContainers() {
final ArrayMap<WindowContainer, Rect> containers =
mDisplayContent.mClosingChangingContainers;
while (!containers.isEmpty()) {
final WindowContainer container = containers.keyAt(0);
containers.remove(container);
// For closing changing windows that are part of the transition, they should have been
// removed from mClosingChangingContainers in WindowContainer#getAnimationAdapter()
// If the closing changing TaskFragment is not part of the transition, update its
// surface after removing it from mClosingChangingContainers.
final TaskFragment taskFragment = container.asTaskFragment();
if (taskFragment != null) {
taskFragment.updateOrganizedTaskFragmentSurface();
}
}
}
private void handleChangingApps(@TransitionOldType int transit) {
final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
final int appsCount = apps.size();

View File

@@ -192,6 +192,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.DisplayUtils;
@@ -348,6 +349,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
final ArraySet<ActivityRecord> mClosingApps = new ArraySet<>();
final ArraySet<WindowContainer> mChangingContainers = new ArraySet<>();
final UnknownAppVisibilityController mUnknownAppVisibilityController;
/**
* If a container is closing when resizing, keeps track of its starting bounds when it is
* removed from {@link #mChangingContainers}.
*/
final ArrayMap<WindowContainer, Rect> mClosingChangingContainers = new ArrayMap<>();
private MetricsLogger mMetricsLogger;

View File

@@ -103,10 +103,29 @@ class RemoteAnimationController implements DeathRecipient {
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect endBounds, Rect startBounds,
boolean showBackdrop) {
return createRemoteAnimationRecord(windowContainer, position, localBounds, endBounds,
startBounds, showBackdrop, startBounds != null /* shouldCreateSnapshot */);
}
/**
* Creates an animation record for each individual {@link WindowContainer}.
*
* @param windowContainer The windows to animate.
* @param position The position app bounds relative to its parent.
* @param localBounds The bounds of the app relative to its parent.
* @param endBounds The end bounds after the transition, in screen coordinates.
* @param startBounds The start bounds before the transition, in screen coordinates.
* @param showBackdrop To show background behind a window during animation.
* @param shouldCreateSnapshot Whether this target should create a snapshot animation.
* @return The record representing animation(s) to run on the app.
*/
RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
Point position, Rect localBounds, Rect endBounds, Rect startBounds,
boolean showBackdrop, boolean shouldCreateSnapshot) {
ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
windowContainer);
final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
localBounds, endBounds, startBounds, showBackdrop);
localBounds, endBounds, startBounds, showBackdrop, shouldCreateSnapshot);
mPendingAnimations.add(adapters);
return adapters;
}
@@ -438,14 +457,15 @@ class RemoteAnimationController implements DeathRecipient {
private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
Rect endBounds, Rect startBounds, boolean showBackdrop) {
Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
boolean shouldCreateSnapshot) {
mWindowContainer = windowContainer;
mShowBackdrop = showBackdrop;
if (startBounds != null) {
mStartBounds = new Rect(startBounds);
mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
mStartBounds, mShowBackdrop);
if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
if (shouldCreateSnapshot && mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
final Rect thumbnailLocalBounds = new Rect(startBounds);
thumbnailLocalBounds.offsetTo(0, 0);
// Snapshot is located at (0,0) of the animation leash. It doesn't have size

View File

@@ -2345,11 +2345,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
if (mTaskFragmentOrganizer != null) {
updateOrganizedTaskFragmentSurface();
}
updateOrganizedTaskFragmentSurface();
sendTaskFragmentInfoChanged();
}
@@ -2362,8 +2358,13 @@ class TaskFragment extends WindowContainer<WindowContainer> {
updateOrganizedTaskFragmentSurface();
}
private void updateOrganizedTaskFragmentSurface() {
if (mDelayOrganizedTaskFragmentSurfaceUpdate) {
/**
* TaskFragmentOrganizer doesn't have access to the surface for security reasons, so we need to
* update its surface on the server side if it is not collected for Shell or in pending
* animation.
*/
void updateOrganizedTaskFragmentSurface() {
if (mDelayOrganizedTaskFragmentSurfaceUpdate || mTaskFragmentOrganizer == null) {
return;
}
if (mTransitionController.isShellTransitionsEnabled()
@@ -2395,7 +2396,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return;
}
final Rect bounds = getBounds();
// If this TaskFragment is closing while resizing, crop to the starting bounds instead.
final Rect bounds = isClosingWhenResizing()
? mDisplayContent.mClosingChangingContainers.get(this)
: getBounds();
final int width = bounds.width();
final int height = bounds.height();
if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
@@ -2443,6 +2447,15 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|| endBounds.height() != startBounds.height();
}
/** Records the starting bounds of the closing organized TaskFragment. */
void setClosingChangingStartBoundsIfNeeded() {
if (isOrganizedTaskFragment() && mDisplayContent != null
&& mDisplayContent.mChangingContainers.remove(this)) {
mDisplayContent.mClosingChangingContainers.put(
this, new Rect(mSurfaceFreezer.mFreezeBounds));
}
}
@Override
boolean isSyncFinished() {
return super.isSyncFinished() && isReadyToTransit();

View File

@@ -810,6 +810,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
void removeImmediately() {
final DisplayContent dc = getDisplayContent();
if (dc != null) {
dc.mClosingChangingContainers.remove(this);
mSurfaceFreezer.unfreeze(getSyncTransaction());
}
while (!mChildren.isEmpty()) {
@@ -1019,9 +1020,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param dc The display this container is on after changes.
*/
void onDisplayChanged(DisplayContent dc) {
if (mDisplayContent != null && mDisplayContent.mChangingContainers.remove(this)) {
// Cancel any change transition queued-up for this container on the old display.
mSurfaceFreezer.unfreeze(getSyncTransaction());
if (mDisplayContent != null) {
mDisplayContent.mClosingChangingContainers.remove(this);
if (mDisplayContent.mChangingContainers.remove(this)) {
// Cancel any change transition queued-up for this container on the old display.
mSurfaceFreezer.unfreeze(getSyncTransaction());
}
}
mDisplayContent = dc;
if (dc != null && dc != this) {
@@ -1298,6 +1302,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
// If we are losing visibility, then a snapshot isn't necessary and we are no-longer
// part of a change transition.
if (!visible) {
if (asTaskFragment() != null) {
// If the organized TaskFragment is closing while resizing, we want to keep track of
// its starting bounds to make sure the animation starts at the correct position.
// This should be called before unfreeze() because we record the starting bounds
// in SurfaceFreezer.
asTaskFragment().setClosingChangingStartBoundsIfNeeded();
}
mSurfaceFreezer.unfreeze(getSyncTransaction());
}
WindowContainer parent = getParent();
@@ -1306,6 +1317,12 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
}
/** Whether this window is closing while resizing. */
boolean isClosingWhenResizing() {
return mDisplayContent != null
&& mDisplayContent.mClosingChangingContainers.containsKey(this);
}
void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
proto.write(HASH_CODE, System.identityHashCode(this));
@@ -3004,10 +3021,22 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
final Rect localBounds = new Rect(mTmpRect);
localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
final RemoteAnimationController.RemoteAnimationRecord adapters =
controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds,
(isChanging ? mSurfaceFreezer.mFreezeBounds : null), showBackdrop);
final RemoteAnimationController.RemoteAnimationRecord adapters;
if (!isChanging && !enter && isClosingWhenResizing()) {
// Container that is closing while resizing. Pass in the closing start bounds, so
// the animation can start with the correct bounds, there won't be a snapshot.
// Cleanup the mClosingChangingContainers so that when the animation is finished, it
// will reset the surface.
final Rect closingStartBounds = getDisplayContent().mClosingChangingContainers
.remove(this);
adapters = controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds, closingStartBounds,
showBackdrop, false /* shouldCreateSnapshot */);
} else {
final Rect startBounds = isChanging ? mSurfaceFreezer.mFreezeBounds : null;
adapters = controller.createRemoteAnimationRecord(
this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);
}
if (backdropColor != 0) {
adapters.setBackDropColor(backdropColor);
}
@@ -3464,7 +3493,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return;
}
getRelativePosition(mTmpPos);
if (isClosingWhenResizing()) {
// This container is closing while resizing, keep its surface at the starting position
// to prevent animation flicker.
getRelativePosition(mDisplayContent.mClosingChangingContainers.get(this), mTmpPos);
} else {
getRelativePosition(mTmpPos);
}
final int deltaRotation = getRelativeDisplayRotation();
if (mTmpPos.equals(mLastSurfacePosition) && deltaRotation == mLastDeltaRotation) {
return;
@@ -3529,9 +3564,14 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
outSurfaceInsets.setEmpty();
}
/** Gets the position of this container in its parent's coordinate. */
void getRelativePosition(Point outPos) {
final Rect dispBounds = getBounds();
outPos.set(dispBounds.left, dispBounds.top);
getRelativePosition(getBounds(), outPos);
}
/** Gets the position of {@code curBounds} in this container's parent's coordinate. */
void getRelativePosition(Rect curBounds, Point outPos) {
outPos.set(curBounds.left, curBounds.top);
final WindowContainer parent = getParent();
if (parent != null) {
final Rect parentBounds = parent.getBounds();