diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 74749cc9bf0b8..377dfd0fb1372 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -98,10 +98,6 @@ public class TransitionAnimation {
private static final String DEFAULT_PACKAGE = "android";
- // TODO (b/215515255): remove once we full migrate to shell transitions
- private static final boolean SHELL_TRANSITIONS_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
-
private final Context mContext;
private final String mTag;
@@ -256,9 +252,6 @@ public class TransitionAnimation {
resId = ent.array.getResourceId(animAttr, 0);
}
}
- if (!SHELL_TRANSITIONS_ENABLED) {
- resId = updateToLegacyIfNeeded(resId);
- }
resId = updateToTranslucentAnimIfNeeded(resId, transit);
if (ResourceId.isValid(resId)) {
return loadAnimationSafely(context, resId, mTag);
@@ -266,24 +259,6 @@ public class TransitionAnimation {
return null;
}
- /**
- * Replace animations that are not compatible with the legacy transition system with ones that
- * are compatible with it.
- * TODO (b/215515255): remove once we full migrate to shell transitions
- */
- private int updateToLegacyIfNeeded(int anim) {
- if (anim == R.anim.activity_open_enter) {
- return R.anim.activity_open_enter_legacy;
- } else if (anim == R.anim.activity_open_exit) {
- return R.anim.activity_open_exit_legacy;
- } else if (anim == R.anim.activity_close_enter) {
- return R.anim.activity_close_enter_legacy;
- } else if (anim == R.anim.activity_close_exit) {
- return R.anim.activity_close_exit_legacy;
- }
- return anim;
- }
-
/** Load animation by attribute Id from a specific AnimationStyle resource. */
@Nullable
public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
diff --git a/core/res/res/anim/activity_close_enter_legacy.xml b/core/res/res/anim/activity_close_enter_legacy.xml
deleted file mode 100644
index 9fa7c5498ea6c..0000000000000
--- a/core/res/res/anim/activity_close_enter_legacy.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit_legacy.xml b/core/res/res/anim/activity_close_exit_legacy.xml
deleted file mode 100644
index 1599ae8cb19fc..0000000000000
--- a/core/res/res/anim/activity_close_exit_legacy.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
diff --git a/core/res/res/anim/activity_open_enter_legacy.xml b/core/res/res/anim/activity_open_enter_legacy.xml
deleted file mode 100644
index 38d3e8ed06ce9..0000000000000
--- a/core/res/res/anim/activity_open_enter_legacy.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
diff --git a/core/res/res/anim/activity_open_exit_legacy.xml b/core/res/res/anim/activity_open_exit_legacy.xml
deleted file mode 100644
index 3865d2149f42f..0000000000000
--- a/core/res/res/anim/activity_open_exit_legacy.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1edd46451a890..845de8a3492e2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1713,10 +1713,6 @@
-
-
-
-
diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
index 61f9fe29b2f88..d7e725b5e679f 100644
--- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
+++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java
@@ -174,5 +174,9 @@ class LocalAnimationAdapter implements AnimationAdapter {
}
void dumpDebugInner(ProtoOutputStream proto);
+
+ default WindowAnimationSpec asWindowAnimationSpec() {
+ return null;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 92e2ee6f8af96..b57670914c118 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -26,13 +26,22 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.hardware.power.Boost;
import android.os.Handler;
import android.os.PowerManagerInternal;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.animation.Animation;
+import android.view.animation.Transformation;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -40,6 +49,8 @@ import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.server.AnimationThread;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Supplier;
/**
@@ -71,6 +82,10 @@ class SurfaceAnimationRunner {
@VisibleForTesting
final ArrayMap mPendingAnimations = new ArrayMap<>();
+ @GuardedBy("mLock")
+ @VisibleForTesting
+ final ArrayMap mPreProcessingAnimations = new ArrayMap<>();
+
@GuardedBy("mLock")
@VisibleForTesting
final ArrayMap mRunningAnimations = new ArrayMap<>();
@@ -136,23 +151,64 @@ class SurfaceAnimationRunner {
synchronized (mLock) {
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
- mPendingAnimations.put(animationLeash, runningAnim);
- if (!mAnimationStartDeferred) {
- mChoreographer.postFrameCallback(this::startAnimations);
+ boolean requiresEdgeExtension = requiresEdgeExtension(a);
+
+ if (requiresEdgeExtension) {
+ mPreProcessingAnimations.put(animationLeash, runningAnim);
+
+ // We must wait for t to be committed since otherwise the leash doesn't have the
+ // windows we want to screenshot and extend as children.
+ t.addTransactionCommittedListener(Runnable::run, () -> {
+ final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec();
+ final Runnable cleanUpEdgeExtension = edgeExtendWindow(animationLeash,
+ animationSpec.getRootTaskBounds(), animationSpec.getAnimation(),
+ mFrameTransaction);
+
+ runningAnim.mFinishCallback = () -> {
+ cleanUpEdgeExtension.run();
+ finishCallback.run();
+ };
+
+ synchronized (mLock) {
+ // only run if animation is not yet canceled by this point
+ if (mPreProcessingAnimations.get(animationLeash) == runningAnim) {
+ mPreProcessingAnimations.remove(animationLeash);
+ mPendingAnimations.put(animationLeash, runningAnim);
+ if (!mAnimationStartDeferred) {
+ mChoreographer.postFrameCallback(this::startAnimations);
+ }
+ }
+ }
+ });
}
- // Some animations (e.g. move animations) require the initial transform to be applied
- // immediately.
- applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
+ if (!requiresEdgeExtension) {
+ mPendingAnimations.put(animationLeash, runningAnim);
+ if (!mAnimationStartDeferred) {
+ mChoreographer.postFrameCallback(this::startAnimations);
+ }
+
+ // Some animations (e.g. move animations) require the initial transform to be
+ // applied immediately.
+ applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
+ }
}
}
+ private boolean requiresEdgeExtension(AnimationSpec a) {
+ return a.asWindowAnimationSpec() != null && a.asWindowAnimationSpec().hasExtension();
+ }
+
void onAnimationCancelled(SurfaceControl leash) {
synchronized (mLock) {
if (mPendingAnimations.containsKey(leash)) {
mPendingAnimations.remove(leash);
return;
}
+ if (mPreProcessingAnimations.containsKey(leash)) {
+ mPreProcessingAnimations.remove(leash);
+ return;
+ }
final RunningAnimation anim = mRunningAnimations.get(leash);
if (anim != null) {
mRunningAnimations.remove(leash);
@@ -264,10 +320,133 @@ class SurfaceAnimationRunner {
mApplyScheduled = false;
}
+ private Runnable edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a,
+ Transaction transaction) {
+ 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 = bounds.height();
+ final int targetSurfaceWidth = bounds.width();
+
+ final List extensionSurfaces = new ArrayList<>();
+
+ 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;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Left Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ 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;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Top Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ 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;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Right Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ 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;
+ final SurfaceControl extensionSurface = createExtensionSurface(leash, edgeBounds,
+ extensionRect, xPos, yPos, "Bottom Edge Extension", transaction);
+ extensionSurfaces.add(extensionSurface);
+ }
+
+ Runnable cleanUp = () -> {
+ for (final SurfaceControl extensionSurface : extensionSurfaces) {
+ if (extensionSurface != null) {
+ transaction.remove(extensionSurface);
+ }
+ }
+ };
+
+ return cleanUp;
+ }
+
+ private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
+ Rect extensionRect, int xPos, int yPos, String layerName,
+ Transaction startTransaction) {
+ 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) {
+ Log.e("SurfaceAnimationRunner", "Failed to create edge extension - "
+ + "edge buffer is null");
+ 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);
+
+ return edgeExtensionLayer;
+ }
+
private static final class RunningAnimation {
final AnimationSpec mAnimSpec;
final SurfaceControl mLeash;
- final Runnable mFinishCallback;
+ Runnable mFinishCallback;
ValueAnimator mAnim;
@GuardedBy("mCancelLock")
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 5bfd546f76203..b6d668dde9d8d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -21,6 +21,7 @@ import static com.android.server.wm.AnimationSpecProto.WINDOW;
import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION;
import static com.android.server.wm.WindowStateAnimator.ROOT_TASK_CLIP_NONE;
+import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.SystemClock;
@@ -74,6 +75,11 @@ public class WindowAnimationSpec implements AnimationSpec {
}
}
+ @Override
+ public WindowAnimationSpec asWindowAnimationSpec() {
+ return this;
+ }
+
@Override
public boolean getShowWallpaper() {
return mAnimation.getShowWallpaper();
@@ -89,11 +95,27 @@ public class WindowAnimationSpec implements AnimationSpec {
return mAnimation.getBackgroundColor();
}
+ /**
+ * @return If a window animation has outsets applied to it.
+ * @see Animation#hasExtension()
+ */
+ public boolean hasExtension() {
+ return mAnimation.hasExtension();
+ }
+
@Override
public long getDuration() {
return mAnimation.computeDurationHint();
}
+ public Rect getRootTaskBounds() {
+ return mRootTaskBounds;
+ }
+
+ public Animation getAnimation() {
+ return mAnimation;
+ }
+
@Override
public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
final TmpValues tmp = mThreadLocalTmps.get();
@@ -106,7 +128,9 @@ public class WindowAnimationSpec implements AnimationSpec {
boolean cropSet = false;
if (mRootTaskClipMode == ROOT_TASK_CLIP_NONE) {
if (tmp.transformation.hasClipRect()) {
- t.setWindowCrop(leash, tmp.transformation.getClipRect());
+ final Rect clipRect = tmp.transformation.getClipRect();
+ accountForExtension(tmp.transformation, clipRect);
+ t.setWindowCrop(leash, clipRect);
cropSet = true;
}
} else {
@@ -114,6 +138,7 @@ public class WindowAnimationSpec implements AnimationSpec {
if (tmp.transformation.hasClipRect()) {
mTmpRect.intersect(tmp.transformation.getClipRect());
}
+ accountForExtension(tmp.transformation, mTmpRect);
t.setWindowCrop(leash, mTmpRect);
cropSet = true;
}
@@ -125,6 +150,14 @@ public class WindowAnimationSpec implements AnimationSpec {
}
}
+ private void accountForExtension(Transformation transformation, Rect clipRect) {
+ Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ if (!extensionInsets.equals(Insets.NONE)) {
+ // Extend the surface to allow for the edge extension to be visible
+ clipRect.inset(extensionInsets);
+ }
+ }
+
@Override
public long calculateStatusBarTransitionStartTime() {
TranslateAnimation openTranslateAnimation = findTranslateAnimation(mAnimation);