Merge "Fix ActivityEmbedding Shell transition animation with reparent" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-09-15 06:29:44 +00:00
committed by Android (Google) Code Review
4 changed files with 115 additions and 42 deletions

View File

@@ -170,7 +170,9 @@ class ActivityEmbeddingAnimationAdapter {
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) { void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
super.onAnimationEnd(t); super.onAnimationEnd(t);
// Remove the screenshot leash after animation is finished. // Remove the screenshot leash after animation is finished.
t.remove(mLeash); if (mLeash.isValid()) {
t.remove(mLeash);
}
} }
} }

View File

@@ -24,6 +24,7 @@ import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.IBinder; import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.animation.Animation; import android.view.animation.Animation;
@@ -39,6 +40,7 @@ import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
/** To run the ActivityEmbedding animations. */ /** To run the ActivityEmbedding animations. */
@@ -214,38 +216,65 @@ class ActivityEmbeddingAnimationRunner {
private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters( private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>(); final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();
// For the first iteration, we prepare the animation for the change type windows. This is
// needed because there may be window that is reparented while resizing. In such case, we
// will do the following:
// 1. Capture a screenshot from the Activity surface.
// 2. Attach the screenshot surface to the top of TaskFragment (Activity's parent) surface.
// 3. Animate the TaskFragment using Activity Change info (start/end bounds).
// This is because the TaskFragment surface/change won't contain the Activity's before its
// reparent.
for (TransitionInfo.Change change : info.getChanges()) { for (TransitionInfo.Change change : info.getChanges()) {
if (change.getMode() == TRANSIT_CHANGE if (change.getMode() != TRANSIT_CHANGE
&& !change.getStartAbsBounds().equals(change.getEndAbsBounds())) { || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
// This is the window with bounds change. continue;
final WindowContainerToken parentToken = change.getParent(); }
final Rect parentBounds;
if (parentToken != null) { // This is the window with bounds change.
TransitionInfo.Change parentChange = info.getChange(parentToken); handledChanges.add(change);
parentBounds = parentChange != null final WindowContainerToken parentToken = change.getParent();
? parentChange.getEndAbsBounds() TransitionInfo.Change boundsAnimationChange = change;
: change.getEndAbsBounds(); if (parentToken != null) {
} else { // When the parent window is also included in the transition as an opening window,
parentBounds = change.getEndAbsBounds(); // we would like to animate the parent window instead.
} final TransitionInfo.Change parentChange = info.getChange(parentToken);
final Animation[] animations = if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds); // We won't create a separate animation for the parent, but to animate the
// Adapter for the starting screenshot leash. // parent for the child resizing.
final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction); handledChanges.add(parentChange);
if (screenshotLeash != null) { boundsAnimationChange = parentChange;
// The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd }
adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter( }
animations[0], change, screenshotLeash));
} else { final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
Log.e(TAG, "Failed to take screenshot for change=" + change); boundsAnimationChange.getEndAbsBounds());
}
// Adapter for the ending bounds changed leash. // Create a screenshot based on change, but attach it to the top of the
adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter( // boundsAnimationChange.
animations[1], change)); final SurfaceControl screenshotLeash = getOrCreateScreenshot(change,
boundsAnimationChange, startTransaction);
if (screenshotLeash != null) {
// Adapter for the starting screenshot leash.
// The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
animations[0], change, screenshotLeash));
} else {
Log.e(TAG, "Failed to take screenshot for change=" + change);
}
// Adapter for the ending bounds changed leash.
adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
animations[1], boundsAnimationChange));
}
// Handle the other windows that don't have bounds change in the same transition.
for (TransitionInfo.Change change : info.getChanges()) {
if (handledChanges.contains(change)) {
// Skip windows that we have already handled in the previous iteration.
continue; continue;
} }
// These are the other windows that don't have bounds change in the same transition.
final Animation animation; final Animation animation;
if (!TransitionInfo.isIndependent(change, info)) { if (!TransitionInfo.isIndependent(change, info)) {
// No-op if it will be covered by the changing parent window. // No-op if it will be covered by the changing parent window.
@@ -260,13 +289,27 @@ class ActivityEmbeddingAnimationRunner {
return adapters; return adapters;
} }
/** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */ /**
* Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
* The screenshot leash should be attached to the {@code animationChange} surface which we will
* animate later.
*/
@Nullable @Nullable
private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change, private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange,
@NonNull SurfaceControl.Transaction startTransaction) { @NonNull TransitionInfo.Change animationChange,
final Rect cropBounds = new Rect(change.getStartAbsBounds()); @NonNull SurfaceControl.Transaction t) {
final SurfaceControl screenshotLeash = screenshotChange.getSnapshot();
if (screenshotLeash != null) {
// If WM Core has already taken a screenshot, make sure it is reparented to the
// animation leash.
t.reparent(screenshotLeash, animationChange.getLeash());
return screenshotLeash;
}
// If WM Core hasn't taken a screenshot, take a screenshot now.
final Rect cropBounds = new Rect(screenshotChange.getStartAbsBounds());
cropBounds.offsetTo(0, 0); cropBounds.offsetTo(0, 0);
return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds, return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
Integer.MAX_VALUE); animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
} }
} }

View File

@@ -28,7 +28,7 @@ import java.util.function.Consumer;
public class ScreenshotUtils { public class ScreenshotUtils {
/** /**
* Take a screenshot of the specified SurfaceControl. * Takes a screenshot of the specified SurfaceControl.
* *
* @param sc the SurfaceControl to take a screenshot of * @param sc the SurfaceControl to take a screenshot of
* @param crop the crop to use when capturing the screenshot * @param crop the crop to use when capturing the screenshot
@@ -49,11 +49,14 @@ public class ScreenshotUtils {
SurfaceControl mScreenshot = null; SurfaceControl mScreenshot = null;
SurfaceControl.Transaction mTransaction; SurfaceControl.Transaction mTransaction;
SurfaceControl mSurfaceControl; SurfaceControl mSurfaceControl;
SurfaceControl mParentSurfaceControl;
int mLayer; int mLayer;
BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) { BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc,
int layer) {
mTransaction = t; mTransaction = t;
mSurfaceControl = sc; mSurfaceControl = sc;
mParentSurfaceControl = parentSc;
mLayer = layer; mLayer = layer;
} }
@@ -72,7 +75,7 @@ public class ScreenshotUtils {
mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer()); mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace()); mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mSurfaceControl); mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer); mTransaction.setLayer(mScreenshot, mLayer);
mTransaction.show(mScreenshot); mTransaction.show(mScreenshot);
mTransaction.apply(); mTransaction.apply();
@@ -80,7 +83,7 @@ public class ScreenshotUtils {
} }
/** /**
* Take a screenshot of the specified SurfaceControl. * Takes a screenshot of the specified SurfaceControl.
* *
* @param t the transaction used to set changes on the resulting screenshot. * @param t the transaction used to set changes on the resulting screenshot.
* @param sc the SurfaceControl to take a screenshot of * @param sc the SurfaceControl to take a screenshot of
@@ -91,7 +94,23 @@ public class ScreenshotUtils {
*/ */
public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc, public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
Rect crop, int layer) { Rect crop, int layer) {
BufferConsumer consumer = new BufferConsumer(t, sc, layer); return takeScreenshot(t, sc, sc /* parentSc */, crop, layer);
}
/**
* Takes a screenshot of the specified SurfaceControl.
*
* @param t the transaction used to set changes on the resulting screenshot.
* @param sc the SurfaceControl to take a screenshot of
* @param parentSc the SurfaceControl to attach the screenshot to.
* @param crop the crop to use when capturing the screenshot
* @param layer the layer to place the screenshot
*
* @return A SurfaceControl where the screenshot will be attached, or null if failed.
*/
public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
SurfaceControl parentSc, Rect crop, int layer) {
BufferConsumer consumer = new BufferConsumer(t, sc, parentSc, layer);
captureLayer(sc, crop, consumer); captureLayer(sc, crop, consumer);
return consumer.mScreenshot; return consumer.mScreenshot;
} }

View File

@@ -1522,8 +1522,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
this.task = newTask; this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) { if (shouldStartChangeTransition(newParent, oldParent)) {
// Animate change transition on TaskFragment level to get the correct window crop. if (mTransitionController.isShellTransitionsEnabled()) {
newParent.initializeChangeTransition(getBounds(), getSurfaceControl()); // For Shell transition, call #initializeChangeTransition directly to take the
// screenshot at the Activity level. And Shell will be in charge of handling the
// surface reparent and crop.
initializeChangeTransition(getBounds());
} else {
// For legacy app transition, we want to take a screenshot of the Activity surface,
// but animate the change transition on TaskFragment level to get the correct window
// crop.
newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
}
} }
super.onParentChanged(newParent, oldParent); super.onParentChanged(newParent, oldParent);