Edge extension effect: extend surfaces

Instead of attaching new windows to extend the existing ones, we
enlarge the bounds and fill the surfaces with a shader

Bug: 322036393
Test: TransitionAnimationTests
Flag: com.android.graphics.libgui.flags.edge_extension_shader

Change-Id: I594088d5c605e267b3a02f53ed6e32b12074ab8d
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index af3981c..24a2a9c 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -99,6 +99,7 @@
         "framework_graphics_flags_java_lib",
         "hwui_flags_java_lib",
         "libcore_exported_aconfig_flags_lib",
+        "libgui_flags_java_lib",
         "power_flags_lib",
         "sdk_sandbox_flags_lib",
         "surfaceflinger_flags_java_lib",
@@ -1212,6 +1213,12 @@
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
+java_aconfig_library {
+    name: "libgui_flags_java_lib",
+    aconfig_declarations: "libgui_flags",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
 // Content Capture
 aconfig_declarations {
     name: "android.view.contentcapture.flags-aconfig",
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 634469d..cf329d3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -164,6 +164,9 @@
             float width, float height, float vecX, float vecY,
             float maxStretchAmountX, float maxStretchAmountY, float childRelativeLeft,
             float childRelativeTop, float childRelativeRight, float childRelativeBottom);
+    private static native void nativeSetEdgeExtensionEffect(long transactionObj, long nativeObj,
+                                                            boolean leftEdge, boolean rightEdge,
+                                                            boolean topEdge, boolean bottomEdge);
     private static native void nativeSetTrustedOverlay(long transactionObj, long nativeObject,
             int isTrustedOverlay);
     private static native void nativeSetDropInputMode(
@@ -3513,6 +3516,19 @@
         /**
          * @hide
          */
+        public Transaction setEdgeExtensionEffect(SurfaceControl sc, int edge) {
+            checkPreconditions(sc);
+
+            nativeSetEdgeExtensionEffect(
+                    mNativeObject, sc.mNativeObject,
+                    (edge & WindowInsets.Side.LEFT) != 0, (edge & WindowInsets.Side.RIGHT) != 0,
+                    (edge & WindowInsets.Side.TOP) != 0, (edge & WindowInsets.Side.BOTTOM) != 0);
+            return this;
+        }
+
+        /**
+         * @hide
+         */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
         public Transaction setLayerStack(SurfaceControl sc, int layerStack) {
             checkPreconditions(sc);
@@ -4882,4 +4898,5 @@
     public static void notifyShutdown() {
         nativeNotifyShutdown();
     }
+
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 09306c7..2af935d 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -28,6 +28,7 @@
 import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.WindowInsets;
 
 import dalvik.system.CloseGuard;
 
@@ -881,12 +882,13 @@
     }
 
     /**
-     * @return if a window animation has outsets applied to it.
+     * @return the edges to which outsets can be applied to
      *
      * @hide
      */
-    public boolean hasExtension() {
-        return false;
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        return 0x0;
     }
 
     /**
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 5aaa994..bbdc9d0 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -21,6 +21,7 @@
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -540,12 +541,12 @@
 
     /** @hide */
     @Override
-    public boolean hasExtension() {
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        int edge = 0x0;
         for (Animation animation : mAnimations) {
-            if (animation.hasExtension()) {
-                return true;
-            }
+            edge |= animation.getExtensionEdges();
         }
-        return false;
+        return edge;
     }
 }
diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java
index 210eb8a..ed047c7 100644
--- a/core/java/android/view/animation/ExtendAnimation.java
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Insets;
 import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 /**
  * An animation that controls the outset of an object.
@@ -50,6 +51,8 @@
     private float mToRightValue;
     private float mToBottomValue;
 
+    private int mExtensionEdges = 0x0;
+
     /**
      * Constructor used when an ExtendAnimation is loaded from a resource.
      *
@@ -151,9 +154,22 @@
 
     /** @hide */
     @Override
-    public boolean hasExtension() {
-        return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
-                || mFromInsets.bottom < 0;
+    @WindowInsets.Side.InsetsSide
+    public int getExtensionEdges() {
+        mExtensionEdges = 0x0;
+        if (mFromLeftValue > 0 || mToLeftValue > 0) {
+            mExtensionEdges |= WindowInsets.Side.LEFT;
+        }
+        if (mFromRightValue > 0 || mToRightValue > 0) {
+            mExtensionEdges |= WindowInsets.Side.RIGHT;
+        }
+        if (mFromTopValue > 0 || mToTopValue > 0) {
+            mExtensionEdges |= WindowInsets.Side.TOP;
+        }
+        if (mFromBottomValue > 0 || mToBottomValue > 0) {
+            mExtensionEdges |= WindowInsets.Side.BOTTOM;
+        }
+        return mExtensionEdges;
     }
 
     @Override
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 9ce7658..0f53164 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -22,6 +22,7 @@
 #include <android/graphics/properties.h>
 #include <android/graphics/region.h>
 #include <android/gui/BnWindowInfosReportedListener.h>
+#include <android/gui/EdgeExtensionParameters.h>
 #include <android/gui/JankData.h>
 #include <android/hardware/display/IDeviceProductInfoConstants.h>
 #include <android/os/IInputConstants.h>
@@ -799,6 +800,20 @@
     transaction->setStretchEffect(ctrl, stretch);
 }
 
+static void nativeSetEdgeExtensionEffect(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                         jlong nativeObj, jboolean leftEdge, jboolean rightEdge,
+                                         jboolean topEdge, jboolean bottomEdge) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+    auto* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObj);
+
+    auto effect = gui::EdgeExtensionParameters();
+    effect.extendLeft = leftEdge;
+    effect.extendRight = rightEdge;
+    effect.extendTop = topEdge;
+    effect.extendBottom = bottomEdge;
+    transaction->setEdgeExtensionEffect(ctrl, effect);
+}
+
 static void nativeSetFlags(JNIEnv* env, jclass clazz, jlong transactionObj,
         jlong nativeObject, jint flags, jint mask) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2340,6 +2355,8 @@
             (void*)nativeSetBlurRegions },
     {"nativeSetStretchEffect", "(JJFFFFFFFFFF)V",
             (void*) nativeSetStretchEffect },
+    {"nativeSetEdgeExtensionEffect", "(JJZZZZ)V",
+            (void*) nativeSetEdgeExtensionEffect },
     {"nativeSetShadowRadius", "(JJF)V",
             (void*)nativeSetShadowRadius },
     {"nativeSetFrameRate", "(JJFII)V",
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 8d30db6..86e0f14 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
@@ -146,6 +146,11 @@
     /** To be overridden by subclasses to adjust the animation surface change. */
     void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
         // Update the surface position and alpha.
+        if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()
+                && mAnimation.getExtensionEdges() != 0) {
+            t.setEdgeExtensionEffect(mLeash, mAnimation.getExtensionEdges());
+        }
+
         mTransformation.getMatrix().postTranslate(mContentRelOffset.x, mContentRelOffset.y);
         t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
         t.setAlpha(mLeash, mTransformation.getAlpha());
@@ -165,7 +170,7 @@
         if (!cropRect.intersect(mWholeAnimationBounds)) {
             // Hide the surface when it is outside of the animation area.
             t.setAlpha(mLeash, 0);
-        } else if (mAnimation.hasExtension()) {
+        } else if (mAnimation.getExtensionEdges() != 0) {
             // Allow the surface to be shown in its original bounds in case we want to use edge
             // extensions.
             cropRect.union(mContentBounds);
@@ -180,6 +185,7 @@
     @CallSuper
     void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
         onAnimationUpdate(t, mAnimation.getDuration());
+        t.setEdgeExtensionEffect(mLeash, /* edge */ 0);
     }
 
     final long getDurationHint() {
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 5696a54..d2cef4b 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
@@ -144,8 +144,10 @@
             // ending states.
             prepareForJumpCut(info, startTransaction);
         } else {
-            addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
-                    postStartTransactionCallbacks, adapters);
+            if (!com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+                addEdgeExtensionIfNeeded(startTransaction, finishTransaction,
+                        postStartTransactionCallbacks, adapters);
+            }
             addBackgroundColorIfNeeded(info, startTransaction, finishTransaction, adapters);
             for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
                 duration = Math.max(duration, adapter.getDurationHint());
@@ -341,7 +343,7 @@
             @NonNull List<ActivityEmbeddingAnimationAdapter> adapters) {
         for (ActivityEmbeddingAnimationAdapter adapter : adapters) {
             final Animation animation = adapter.mAnimation;
-            if (!animation.hasExtension()) {
+            if (animation.getExtensionEdges() == 0) {
                 continue;
             }
             if (adapter.mChange.hasFlags(FLAG_TRANSLUCENT)
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 7784784..d8c8c60 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
@@ -502,7 +502,8 @@
                 backgroundColorForTransition = getTransitionBackgroundColorIfSet(info, change, a,
                         backgroundColorForTransition);
 
-                if (!isTask && a.hasExtension()) {
+                if (!com.android.graphics.libgui.flags.Flags.edgeExtensionShader() && !isTask
+                        && a.getExtensionEdges() != 0) {
                     if (!TransitionUtil.isOpeningType(mode)) {
                         // Can screenshot now (before startTransaction is applied)
                         edgeExtendWindow(change, a, startTransaction, finishTransaction);
@@ -512,6 +513,8 @@
                         postStartTransactionCallbacks
                                 .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
                     }
+                } else if (com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+                    finishTransaction.setEdgeExtensionEffect(change.getLeash(), /* edge */ 0);
                 }
 
                 final Rect clipRect = TransitionUtil.isClosingType(mode)
@@ -1008,6 +1011,10 @@
             Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
         tmpTransformation.clear();
         anim.getTransformation(time, tmpTransformation);
+        if (anim.getExtensionEdges() != 0
+                && com.android.graphics.libgui.flags.Flags.edgeExtensionShader()) {
+            t.setEdgeExtensionEffect(leash, anim.getExtensionEdges());
+        }
         if (position != null) {
             tmpTransformation.getMatrix().postTranslate(position.x, position.y);
         }
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 34b9913..2c58c61 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -97,10 +97,10 @@
 
     /**
      * @return If a window animation has outsets applied to it.
-     * @see Animation#hasExtension()
+     * @see Animation#getExtensionEdges()
      */
     public boolean hasExtension() {
-        return mAnimation.hasExtension();
+        return mAnimation.getExtensionEdges() != 0;
     }
 
     @Override