diff options
8 files changed, 238 insertions, 166 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 591e3476ecd9..215308d9e96e 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 @@ -130,6 +130,10 @@ class ActivityEmbeddingAnimationAdapter { if (!cropRect.intersect(mWholeAnimationBounds)) { // Hide the surface when it is outside of the animation area. t.setAlpha(mLeash, 0); + } 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 is in absolute coordinate, so we need to translate it to surface top left. 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 756d80204833..490975cce956 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 @@ -21,6 +21,7 @@ import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET; import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW; import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition; +import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet; import android.animation.Animator; @@ -45,6 +46,7 @@ import com.android.wm.shell.transition.Transitions; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.function.Consumer; /** To run the ActivityEmbedding animations. */ class ActivityEmbeddingAnimationRunner { @@ -65,10 +67,31 @@ class ActivityEmbeddingAnimationRunner { void startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { + // There may be some surface change that we want to apply after the start transaction is + // applied to make sure the surface is ready. + final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks = + new ArrayList<>(); final Animator animator = createAnimator(info, startTransaction, finishTransaction, - () -> mController.onAnimationFinished(transition)); - startTransaction.apply(); - animator.start(); + () -> mController.onAnimationFinished(transition), postStartTransactionCallbacks); + + // Start the animation. + if (!postStartTransactionCallbacks.isEmpty()) { + // postStartTransactionCallbacks require that the start transaction is already + // applied to run otherwise they may result in flickers and UI inconsistencies. + startTransaction.apply(true /* sync */); + + // Run tasks that require startTransaction to already be applied + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback : + postStartTransactionCallbacks) { + postStartTransactionCallback.accept(t); + } + t.apply(); + animator.start(); + } else { + startTransaction.apply(); + animator.start(); + } } /** @@ -85,9 +108,13 @@ class ActivityEmbeddingAnimationRunner { Animator createAnimator(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Runnable animationFinishCallback) { - final List<ActivityEmbeddingAnimationAdapter> adapters = - createAnimationAdapters(info, startTransaction, finishTransaction); + @NonNull Runnable animationFinishCallback, + @NonNull List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks) { + final List<ActivityEmbeddingAnimationAdapter> adapters = createAnimationAdapters(info, + startTransaction); + addEdgeExtensionIfNeeded(startTransaction, finishTransaction, postStartTransactionCallbacks, + adapters); + addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters); long duration = 0; for (ActivityEmbeddingAnimationAdapter adapter : adapters) { duration = Math.max(duration, adapter.getDurationHint()); @@ -131,8 +158,7 @@ class ActivityEmbeddingAnimationRunner { */ @NonNull private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters( - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction) { + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) { boolean isChangeTransition = false; for (TransitionInfo.Change change : info.getChanges()) { if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) { @@ -148,25 +174,23 @@ class ActivityEmbeddingAnimationRunner { return createChangeAnimationAdapters(info, startTransaction); } if (Transitions.isClosingType(info.getType())) { - return createCloseAnimationAdapters(info, startTransaction, finishTransaction); + return createCloseAnimationAdapters(info); } - return createOpenAnimationAdapters(info, startTransaction, finishTransaction); + return createOpenAnimationAdapters(info); } @NonNull private List<ActivityEmbeddingAnimationAdapter> createOpenAnimationAdapters( - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction) { - return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction, - true /* isOpening */, mAnimationSpec::loadOpenAnimation); + @NonNull TransitionInfo info) { + return createOpenCloseAnimationAdapters(info, true /* isOpening */, + mAnimationSpec::loadOpenAnimation); } @NonNull private List<ActivityEmbeddingAnimationAdapter> createCloseAnimationAdapters( - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction) { - return createOpenCloseAnimationAdapters(info, startTransaction, finishTransaction, - false /* isOpening */, mAnimationSpec::loadCloseAnimation); + @NonNull TransitionInfo info) { + return createOpenCloseAnimationAdapters(info, false /* isOpening */, + mAnimationSpec::loadCloseAnimation); } /** @@ -175,8 +199,7 @@ class ActivityEmbeddingAnimationRunner { */ @NonNull private List<ActivityEmbeddingAnimationAdapter> createOpenCloseAnimationAdapters( - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, boolean isOpening, + @NonNull TransitionInfo info, boolean isOpening, @NonNull AnimationProvider animationProvider) { // We need to know if the change 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. @@ -200,8 +223,7 @@ class ActivityEmbeddingAnimationRunner { final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>(); for (TransitionInfo.Change change : openingChanges) { final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( - info, change, startTransaction, finishTransaction, animationProvider, - openingWholeScreenBounds); + info, change, animationProvider, openingWholeScreenBounds); if (isOpening) { adapter.overrideLayer(offsetLayer++); } @@ -209,8 +231,7 @@ class ActivityEmbeddingAnimationRunner { } for (TransitionInfo.Change change : closingChanges) { final ActivityEmbeddingAnimationAdapter adapter = createOpenCloseAnimationAdapter( - info, change, startTransaction, finishTransaction, animationProvider, - closingWholeScreenBounds); + info, change, animationProvider, closingWholeScreenBounds); if (!isOpening) { adapter.overrideLayer(offsetLayer++); } @@ -219,20 +240,51 @@ class ActivityEmbeddingAnimationRunner { return adapters; } + /** Adds edge extension to the surfaces that have such an animation property. */ + private void addEdgeExtensionIfNeeded(@NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks, + @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) { + for (ActivityEmbeddingAnimationAdapter adapter : adapters) { + final Animation animation = adapter.mAnimation; + if (!animation.hasExtension()) { + continue; + } + final TransitionInfo.Change change = adapter.mChange; + if (Transitions.isOpeningType(adapter.mChange.getMode())) { + // Need to screenshot after startTransaction is applied otherwise activity + // may not be visible or ready yet. + postStartTransactionCallbacks.add( + t -> edgeExtendWindow(change, animation, t, finishTransaction)); + } else { + // Can screenshot now (before startTransaction is applied) + edgeExtendWindow(change, animation, startTransaction, finishTransaction); + } + } + } + + /** Adds background color to the transition if any animation has such a property. */ + private void addBackgroundColorIfNeeded(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) { + for (ActivityEmbeddingAnimationAdapter adapter : adapters) { + final int backgroundColor = getTransitionBackgroundColorIfSet(info, adapter.mChange, + adapter.mAnimation, 0 /* defaultColor */); + if (backgroundColor != 0) { + // We only need to show one color. + addBackgroundToTransition(info.getRootLeash(), backgroundColor, startTransaction, + finishTransaction); + return; + } + } + } + @NonNull private ActivityEmbeddingAnimationAdapter createOpenCloseAnimationAdapter( @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, @NonNull AnimationProvider animationProvider, @NonNull Rect wholeAnimationBounds) { final Animation animation = animationProvider.get(info, change, wholeAnimationBounds); - // We may want to show a background color for open/close transition. - final int backgroundColor = getTransitionBackgroundColorIfSet(info, change, animation, - 0 /* defaultColor */); - if (backgroundColor != 0) { - addBackgroundToTransition(info.getRootLeash(), backgroundColor, startTransaction, - finishTransaction); - } return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(), wholeAnimationBounds); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java index eb6ac7615266..58b23667dc18 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java @@ -181,15 +181,15 @@ class ActivityEmbeddingAnimationSpec { @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = Transitions.isOpeningType(change.getMode()); final Animation animation; - // TODO(b/207070762): Implement edgeExtension version if (shouldShowBackdrop(info, change)) { animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter ? com.android.internal.R.anim.task_fragment_clear_top_open_enter : com.android.internal.R.anim.task_fragment_clear_top_open_exit); } else { + // Use the same edge extension animation as regular activity open. animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter - ? com.android.internal.R.anim.task_fragment_open_enter - : com.android.internal.R.anim.task_fragment_open_exit); + ? com.android.internal.R.anim.activity_open_enter + : com.android.internal.R.anim.activity_open_exit); } // Use the whole animation bounds instead of the change bounds, so that when multiple change // targets are opening at the same time, the animation applied to each will be the same. @@ -205,15 +205,15 @@ class ActivityEmbeddingAnimationSpec { @NonNull TransitionInfo.Change change, @NonNull Rect wholeAnimationBounds) { final boolean isEnter = Transitions.isOpeningType(change.getMode()); final Animation animation; - // TODO(b/207070762): Implement edgeExtension version if (shouldShowBackdrop(info, change)) { animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter ? com.android.internal.R.anim.task_fragment_clear_top_close_enter : com.android.internal.R.anim.task_fragment_clear_top_close_exit); } else { + // Use the same edge extension animation as regular activity close. animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter - ? com.android.internal.R.anim.task_fragment_close_enter - : com.android.internal.R.anim.task_fragment_close_exit); + ? com.android.internal.R.anim.activity_close_enter + : com.android.internal.R.anim.activity_close_exit); } // Use the whole animation bounds instead of the change bounds, so that when multiple change // targets are closing at the same time, the animation applied to each will be the same. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index dbb2948de5db..9c2c2fa8598a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -59,6 +59,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN; import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition; +import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow; import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet; import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation; import static com.android.wm.shell.transition.TransitionAnimationHelper.sDisableCustomTaskAnimationProperty; @@ -76,10 +77,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.graphics.Canvas; import android.graphics.Insets; -import android.graphics.Paint; -import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -89,7 +87,6 @@ import android.os.IBinder; import android.os.UserHandle; import android.util.ArrayMap; import android.view.Choreographer; -import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; @@ -525,123 +522,6 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } } - private void edgeExtendWindow(TransitionInfo.Change change, - Animation a, SurfaceControl.Transaction startTransaction, - SurfaceControl.Transaction finishTransaction) { - // Do not create edge extension surface for transfer starting window change. - // The app surface could be empty thus nothing can draw on the hardware renderer, which will - // block this thread when calling Surface#unlockCanvasAndPost. - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - return; - } - final Transformation transformationAtStart = new Transformation(); - a.getTransformationAt(0, transformationAtStart); - final Transformation transformationAtEnd = new Transformation(); - a.getTransformationAt(1, transformationAtEnd); - - // We want to create an extension surface that is the maximal size and the animation will - // take care of cropping any part that overflows. - final Insets maxExtensionInsets = Insets.min( - transformationAtStart.getInsets(), transformationAtEnd.getInsets()); - - final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(), - change.getEndAbsBounds().height()); - final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(), - change.getEndAbsBounds().width()); - if (maxExtensionInsets.left < 0) { - final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight); - final Rect extensionRect = new Rect(0, 0, - -maxExtensionInsets.left, targetSurfaceHeight); - final int xPos = maxExtensionInsets.left; - final int yPos = 0; - createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, - "Left Edge Extension", startTransaction, finishTransaction); - } - - if (maxExtensionInsets.top < 0) { - final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1); - final Rect extensionRect = new Rect(0, 0, - targetSurfaceWidth, -maxExtensionInsets.top); - final int xPos = 0; - final int yPos = maxExtensionInsets.top; - createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, - "Top Edge Extension", startTransaction, finishTransaction); - } - - if (maxExtensionInsets.right < 0) { - final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0, - targetSurfaceWidth, targetSurfaceHeight); - final Rect extensionRect = new Rect(0, 0, - -maxExtensionInsets.right, targetSurfaceHeight); - final int xPos = targetSurfaceWidth; - final int yPos = 0; - createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, - "Right Edge Extension", startTransaction, finishTransaction); - } - - if (maxExtensionInsets.bottom < 0) { - final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1, - targetSurfaceWidth, targetSurfaceHeight); - final Rect extensionRect = new Rect(0, 0, - targetSurfaceWidth, -maxExtensionInsets.bottom); - final int xPos = maxExtensionInsets.left; - final int yPos = targetSurfaceHeight; - createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, - "Bottom Edge Extension", startTransaction, finishTransaction); - } - } - - private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds, - Rect extensionRect, int xPos, int yPos, String layerName, - SurfaceControl.Transaction startTransaction, - SurfaceControl.Transaction finishTransaction) { - final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() - .setName(layerName) - .setParent(surfaceToExtend) - .setHidden(true) - .setCallsite("DefaultTransitionHandler#startAnimation") - .setOpaque(true) - .setBufferSize(extensionRect.width(), extensionRect.height()) - .build(); - - SurfaceControl.LayerCaptureArgs captureArgs = - new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend) - .setSourceCrop(edgeBounds) - .setFrameScale(1) - .setPixelFormat(PixelFormat.RGBA_8888) - .setChildrenOnly(true) - .setAllowProtected(true) - .build(); - final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer = - SurfaceControl.captureLayers(captureArgs); - - if (edgeBuffer == null) { - ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, - "Failed to capture edge of window."); - return null; - } - - android.graphics.BitmapShader shader = - new android.graphics.BitmapShader(edgeBuffer.asBitmap(), - android.graphics.Shader.TileMode.CLAMP, - android.graphics.Shader.TileMode.CLAMP); - final Paint paint = new Paint(); - paint.setShader(shader); - - final Surface surface = new Surface(edgeExtensionLayer); - Canvas c = surface.lockHardwareCanvas(); - c.drawRect(extensionRect, paint); - surface.unlockCanvasAndPost(c); - surface.release(); - - startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); - startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); - startTransaction.setVisibility(edgeExtensionLayer, true); - finishTransaction.remove(edgeExtensionLayer); - - return edgeExtensionLayer; - } - @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java index efee6f40b53e..b75c55274cff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE; @@ -34,10 +35,19 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; +import android.graphics.BitmapShader; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Insets; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.Shader; import android.os.SystemProperties; +import android.view.Surface; import android.view.SurfaceControl; import android.view.animation.Animation; +import android.view.animation.Transformation; import android.window.TransitionInfo; import com.android.internal.R; @@ -217,4 +227,126 @@ public class TransitionAnimationHelper { .show(animationBackgroundSurface); finishTransaction.remove(animationBackgroundSurface); } + + /** + * Adds edge extension surface to the given {@code change} for edge extension animation. + */ + public static void edgeExtendWindow(@NonNull TransitionInfo.Change change, + @NonNull Animation a, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + // Do not create edge extension surface for transfer starting window change. + // The app surface could be empty thus nothing can draw on the hardware renderer, which will + // block this thread when calling Surface#unlockCanvasAndPost. + if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { + return; + } + final Transformation transformationAtStart = new Transformation(); + a.getTransformationAt(0, transformationAtStart); + final Transformation transformationAtEnd = new Transformation(); + a.getTransformationAt(1, transformationAtEnd); + + // We want to create an extension surface that is the maximal size and the animation will + // take care of cropping any part that overflows. + final Insets maxExtensionInsets = Insets.min( + transformationAtStart.getInsets(), transformationAtEnd.getInsets()); + + final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(), + change.getEndAbsBounds().height()); + final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(), + change.getEndAbsBounds().width()); + if (maxExtensionInsets.left < 0) { + final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + -maxExtensionInsets.left, targetSurfaceHeight); + final int xPos = maxExtensionInsets.left; + final int yPos = 0; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Left Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.top < 0) { + final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1); + final Rect extensionRect = new Rect(0, 0, + targetSurfaceWidth, -maxExtensionInsets.top); + final int xPos = 0; + final int yPos = maxExtensionInsets.top; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Top Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.right < 0) { + final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0, + targetSurfaceWidth, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + -maxExtensionInsets.right, targetSurfaceHeight); + final int xPos = targetSurfaceWidth; + final int yPos = 0; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Right Edge Extension", startTransaction, finishTransaction); + } + + if (maxExtensionInsets.bottom < 0) { + final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1, + targetSurfaceWidth, targetSurfaceHeight); + final Rect extensionRect = new Rect(0, 0, + targetSurfaceWidth, -maxExtensionInsets.bottom); + final int xPos = maxExtensionInsets.left; + final int yPos = targetSurfaceHeight; + createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos, + "Bottom Edge Extension", startTransaction, finishTransaction); + } + } + + /** + * Takes a screenshot of {@code surfaceToExtend}'s edge and extends it for edge extension + * animation. + */ + private static SurfaceControl createExtensionSurface(@NonNull SurfaceControl surfaceToExtend, + @NonNull Rect edgeBounds, @NonNull Rect extensionRect, int xPos, int yPos, + @NonNull String layerName, @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction) { + final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() + .setName(layerName) + .setParent(surfaceToExtend) + .setHidden(true) + .setCallsite("TransitionAnimationHelper#createExtensionSurface") + .setOpaque(true) + .setBufferSize(extensionRect.width(), extensionRect.height()) + .build(); + + final SurfaceControl.LayerCaptureArgs captureArgs = + new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend) + .setSourceCrop(edgeBounds) + .setFrameScale(1) + .setPixelFormat(PixelFormat.RGBA_8888) + .setChildrenOnly(true) + .setAllowProtected(true) + .build(); + final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer = + SurfaceControl.captureLayers(captureArgs); + + if (edgeBuffer == null) { + ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "Failed to capture edge of window."); + return null; + } + + final BitmapShader shader = new BitmapShader(edgeBuffer.asBitmap(), + Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); + final Paint paint = new Paint(); + paint.setShader(shader); + + final Surface surface = new Surface(edgeExtensionLayer); + final Canvas c = surface.lockHardwareCanvas(); + c.drawRect(extensionRect, paint); + surface.unlockCanvasAndPost(c); + surface.release(); + + startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); + startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); + startTransaction.setVisibility(edgeExtensionLayer, true); + finishTransaction.remove(edgeExtensionLayer); + + return edgeExtensionLayer; + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java index 98b59126227c..79070b1469be 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java @@ -40,6 +40,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import java.util.ArrayList; + /** * Tests for {@link ActivityEmbeddingAnimationRunner}. * @@ -62,13 +64,13 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim final TransitionInfo.Change embeddingChange = createChange(); embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY); info.addChange(embeddingChange); - doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any()); + doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any()); mAnimRunner.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction); final ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class); verify(mAnimRunner).createAnimator(eq(info), eq(mStartTransaction), eq(mFinishTransaction), - finishCallback.capture()); + finishCallback.capture(), any()); verify(mStartTransaction).apply(); verify(mAnimator).start(); verifyNoMoreInteractions(mFinishTransaction); @@ -88,7 +90,8 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim info.addChange(embeddingChange); final Animator animator = mAnimRunner.createAnimator( info, mStartTransaction, mFinishTransaction, - () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */)); + () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */), + new ArrayList()); // The animation should be empty when it is behind starting window. assertEquals(0, animator.getDuration()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java index 3792e8361284..54a12ab999c5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java @@ -56,13 +56,12 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase { @Mock SurfaceControl.Transaction mFinishTransaction; @Mock - Transitions.TransitionFinishCallback mFinishCallback; - @Mock Animator mAnimator; ActivityEmbeddingController mController; ActivityEmbeddingAnimationRunner mAnimRunner; ActivityEmbeddingAnimationSpec mAnimSpec; + Transitions.TransitionFinishCallback mFinishCallback; @CallSuper @Before @@ -75,9 +74,11 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase { assertNotNull(mAnimRunner); mAnimSpec = mAnimRunner.mAnimationSpec; assertNotNull(mAnimSpec); + mFinishCallback = (wct, wctCB) -> {}; spyOn(mController); spyOn(mAnimRunner); spyOn(mAnimSpec); + spyOn(mFinishCallback); } /** Creates a mock {@link TransitionInfo.Change}. */ diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java index baecf6fe6673..4d98b6ba4f7a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java @@ -55,7 +55,7 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation @Before public void setup() { super.setUp(); - doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any()); + doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any()); } @Test |