diff options
4 files changed, 113 insertions, 40 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java index 064c304825da..591e3476ecd9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java @@ -170,7 +170,9 @@ class ActivityEmbeddingAnimationAdapter { void onAnimationEnd(@NonNull SurfaceControl.Transaction t) { super.onAnimationEnd(t); // Remove the screenshot leash after animation is finished. - t.remove(mLeash); + if (mLeash.isValid()) { + t.remove(mLeash); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java index 846d2c9b3396..d88cc007c7b5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java @@ -24,6 +24,7 @@ import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; +import android.util.ArraySet; import android.util.Log; import android.view.SurfaceControl; import android.view.animation.Animation; @@ -39,6 +40,7 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.function.BiFunction; /** To run the ActivityEmbedding animations. */ @@ -214,38 +216,65 @@ class ActivityEmbeddingAnimationRunner { private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters( @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { 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()) { - if (change.getMode() == TRANSIT_CHANGE - && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) { - // This is the window with bounds change. - final WindowContainerToken parentToken = change.getParent(); - final Rect parentBounds; - if (parentToken != null) { - TransitionInfo.Change parentChange = info.getChange(parentToken); - parentBounds = parentChange != null - ? parentChange.getEndAbsBounds() - : change.getEndAbsBounds(); - } else { - parentBounds = change.getEndAbsBounds(); + if (change.getMode() != TRANSIT_CHANGE + || change.getStartAbsBounds().equals(change.getEndAbsBounds())) { + continue; + } + + // This is the window with bounds change. + handledChanges.add(change); + final WindowContainerToken parentToken = change.getParent(); + TransitionInfo.Change boundsAnimationChange = change; + if (parentToken != null) { + // When the parent window is also included in the transition as an opening window, + // we would like to animate the parent window instead. + final TransitionInfo.Change parentChange = info.getChange(parentToken); + if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) { + // We won't create a separate animation for the parent, but to animate the + // parent for the child resizing. + handledChanges.add(parentChange); + boundsAnimationChange = parentChange; } - final Animation[] animations = - mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds); + } + + final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change, + boundsAnimationChange.getEndAbsBounds()); + + // Create a screenshot based on change, but attach it to the top of the + // boundsAnimationChange. + final SurfaceControl screenshotLeash = getOrCreateScreenshot(change, + boundsAnimationChange, startTransaction); + if (screenshotLeash != null) { // Adapter for the starting screenshot leash. - final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction); - if (screenshotLeash != null) { - // 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], change)); + // 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; } - // These are the other windows that don't have bounds change in the same transition. final Animation animation; if (!TransitionInfo.isIndependent(change, info)) { // No-op if it will be covered by the changing parent window. @@ -260,13 +289,27 @@ class ActivityEmbeddingAnimationRunner { 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 - private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change, - @NonNull SurfaceControl.Transaction startTransaction) { - final Rect cropBounds = new Rect(change.getStartAbsBounds()); + private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange, + @NonNull TransitionInfo.Change animationChange, + @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); - return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds, - Integer.MAX_VALUE); + return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(), + animationChange.getLeash(), cropBounds, Integer.MAX_VALUE); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java index c4bd73ba1b4a..2a1bf0ee42ba 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java @@ -28,7 +28,7 @@ import java.util.function.Consumer; 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 crop the crop to use when capturing the screenshot @@ -49,11 +49,14 @@ public class ScreenshotUtils { SurfaceControl mScreenshot = null; SurfaceControl.Transaction mTransaction; SurfaceControl mSurfaceControl; + SurfaceControl mParentSurfaceControl; int mLayer; - BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) { + BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc, + int layer) { mTransaction = t; mSurfaceControl = sc; + mParentSurfaceControl = parentSc; mLayer = layer; } @@ -72,7 +75,7 @@ public class ScreenshotUtils { mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer()); mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace()); - mTransaction.reparent(mScreenshot, mSurfaceControl); + mTransaction.reparent(mScreenshot, mParentSurfaceControl); mTransaction.setLayer(mScreenshot, mLayer); mTransaction.show(mScreenshot); 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 sc the SurfaceControl to take a screenshot of @@ -91,7 +94,23 @@ public class ScreenshotUtils { */ public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc, 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); return consumer.mScreenshot; } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 6538ee3c2182..4c0a0171024a 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -1522,8 +1522,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A this.task = newTask; if (shouldStartChangeTransition(newParent, oldParent)) { - // Animate change transition on TaskFragment level to get the correct window crop. - newParent.initializeChangeTransition(getBounds(), getSurfaceControl()); + if (mTransitionController.isShellTransitionsEnabled()) { + // 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); |