Updated EdgeEffect parameters for overscroll stretch

Added dampening logic to caluclate the distance to
overscroll.
Updated StretchEffect to support independent
maximum stretch amounts for the x/y axis

Removed debugging parameters to configure
stretch distance since they are no longer
used. Removed hidden API calls to configure
stretch distance in ScrollView/HorizontalScrollView

Bug: 179047472
Test: re-ran EdgeEffectTests
Change-Id: I4698669273d364695a21c2cce00ec2cfec41b2cc
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 3c41112..5f55887 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -130,7 +130,11 @@
     public @interface EdgeEffectType {
     }
 
-    private static final float DEFAULT_MAX_STRETCH_INTENSITY = 0.08f;
+    private static final float LINEAR_STRETCH_INTENSITY = 0.03f;
+
+    private static final float EXP_STRETCH_INTENSITY = 0.02f;
+
+    private static final float SCROLL_DIST_AFFECTED_BY_EXP_STRETCH = 0.4f;
 
     @SuppressWarnings("UnusedDeclaration")
     private static final String TAG = "EdgeEffect";
@@ -176,9 +180,6 @@
 
     private long mStartTime;
     private float mDuration;
-    private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
-    private float mStretchDistanceFraction = 1f;
-    private float mStretchDistance = -1f;
 
     private final Interpolator mInterpolator = new DecelerateInterpolator();
 
@@ -269,16 +270,6 @@
     }
 
     /**
-     * Configure the distance in pixels to stretch the content. This is only consumed as part
-     * if {@link #setType(int)} is set to {@link #TYPE_STRETCH}
-     * @param stretchDistance Stretch distance in pixels when the target View is overscrolled
-     * @hide
-     */
-    public void setStretchDistance(float stretchDistance) {
-        mStretchDistance = stretchDistance;
-    }
-
-    /**
      * Reports if this EdgeEffect's animation is finished. If this method returns false
      * after a call to {@link #draw(Canvas)} the host widget should schedule another
      * drawing pass to continue the animation.
@@ -520,13 +511,6 @@
     }
 
     /**
-     * @hide
-     */
-    public void setMaxStretchIntensity(float stretchIntensity) {
-        mStretchIntensity = stretchIntensity;
-    }
-
-    /**
      * Set or clear the blend mode. A blend mode defines how source pixels
      * (generated by a drawing command) are composited with the destination pixels
      * (content of the render target).
@@ -642,22 +626,19 @@
             // assume rotations of increments of 90 degrees
             float x = mTmpPoints[10] - mTmpPoints[8];
             float width = right - left;
-            float vecX = Math.max(-1f, Math.min(1f, x / width));
+            float vecX = dampStretchVector(Math.max(-1f, Math.min(1f, x / width)));
             float y = mTmpPoints[11] - mTmpPoints[9];
             float height = bottom - top;
-            float vecY = Math.max(-1f, Math.min(1f, y / height));
+            float vecY = dampStretchVector(Math.max(-1f, Math.min(1f, y / height)));
             renderNode.stretch(
                     left,
                     top,
                     right,
                     bottom,
-                    vecX * mStretchIntensity,
-                    vecY * mStretchIntensity,
-                    // TODO (njawad/mount) figure out proper stretch distance from UX
-                    //  for now leverage placeholder logic if no stretch distance is provided to
-                    //  consume the displacement ratio times the minimum of the width or height
-                    mStretchDistance > 0 ? mStretchDistance :
-                            (mStretchDistanceFraction * Math.max(mWidth, mHeight))
+                    vecX,
+                    vecY,
+                    mWidth,
+                    mHeight
             );
         }
 
@@ -794,4 +775,13 @@
         return Math.abs(mVelocity) < VELOCITY_THRESHOLD
                 && Math.abs(displacement) < VALUE_THRESHOLD;
     }
+
+    private float dampStretchVector(float normalizedVec) {
+        float sign = normalizedVec > 0 ? 1f : -1f;
+        float overscroll = Math.abs(normalizedVec);
+        float linearIntensity = LINEAR_STRETCH_INTENSITY * overscroll;
+        double scalar = Math.E / SCROLL_DIST_AFFECTED_BY_EXP_STRETCH;
+        double expIntensity = EXP_STRETCH_INTENSITY * (1 - Math.exp(-overscroll * scalar));
+        return sign * (float) (linearIntensity + expIntensity);
+    }
 }
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index bf552e2..23915e0 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -249,26 +249,6 @@
     }
 
     /**
-     * API used for prototyping stretch effect parameters in framework sample apps
-     * @hide
-     */
-    public void setEdgeEffectIntensity(float intensity) {
-        mEdgeGlowLeft.setMaxStretchIntensity(intensity);
-        mEdgeGlowRight.setMaxStretchIntensity(intensity);
-        invalidate();
-    }
-
-    /**
-     * API used for prototyping stretch effect parameters in the framework sample apps
-     * @hide
-     */
-    public void setStretchDistance(float distance) {
-        mEdgeGlowLeft.setStretchDistance(distance);
-        mEdgeGlowRight.setStretchDistance(distance);
-        invalidate();
-    }
-
-    /**
      * Sets the right edge effect color.
      *
      * @param color The color for the right edge effect.
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3006729..65f3da7 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -281,26 +281,6 @@
     }
 
     /**
-     * API used for prototyping stretch effect parameters in framework sample apps
-     * @hide
-     */
-    public void setEdgeEffectIntensity(float intensity) {
-        mEdgeGlowTop.setMaxStretchIntensity(intensity);
-        mEdgeGlowBottom.setMaxStretchIntensity(intensity);
-        invalidate();
-    }
-
-    /**
-     * API used for prototyping stretch effect parameters in the framework sample apps
-     * @hide
-     */
-    public void setStretchDistance(float distance) {
-        mEdgeGlowTop.setStretchDistance(distance);
-        mEdgeGlowBottom.setStretchDistance(distance);
-        invalidate();
-    }
-
-    /**
      * Sets the bottom edge effect color.
      *
      * @param color The color for the bottom edge effect.
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index da5162b..6fcd8d0 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -275,6 +275,8 @@
          * Call to apply a stretch effect to any child SurfaceControl layers
          *
          * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls?
+         *   (njawad) update to consume different stretch parameters for horizontal/vertical stretch
+         *   to ensure SkiaGLRenderEngine can also apply the same stretch to a surface
          *
          * @hide
          */
@@ -718,7 +720,7 @@
 
     /** @hide */
     public boolean stretch(float left, float top, float right, float bottom,
-            float vecX, float vecY, float maxStretchAmount) {
+            float vecX, float vecY, float maxStretchAmountX, float maxStretchAmountY) {
         if (Float.isInfinite(vecX) || Float.isNaN(vecX)) {
             throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX);
         }
@@ -730,9 +732,13 @@
                     "Stretch region must not be empty, got "
                             + new RectF(left, top, right, bottom).toString());
         }
-        if (maxStretchAmount <= 0.0f) {
+        if (maxStretchAmountX <= 0.0f) {
             throw new IllegalArgumentException(
-                    "The max stretch amount must be >0, got " + maxStretchAmount);
+                    "The max horizontal stretch amount must be >0, got " + maxStretchAmountX);
+        }
+        if (maxStretchAmountY <= 0.0f) {
+            throw new IllegalArgumentException(
+                    "The max vertical stretch amount must be >0, got " + maxStretchAmountY);
         }
         return nStretch(
                 mNativeRenderNode,
@@ -742,7 +748,8 @@
                 bottom,
                 vecX,
                 vecY,
-                maxStretchAmount
+                maxStretchAmountX,
+                maxStretchAmountY
         );
     }
 
@@ -1695,7 +1702,7 @@
 
     @CriticalNative
     private static native boolean nStretch(long renderNode, float left, float top, float right,
-            float bottom, float vecX, float vecY, float maxStretch);
+            float bottom, float vecX, float vecY, float maxStretchX, float maxStretchY);
 
     @CriticalNative
     private static native boolean nHasShadow(long renderNode);
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
index d4fd105..9e4fb8f 100644
--- a/libs/hwui/effects/StretchEffect.cpp
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -33,7 +33,8 @@
     uniform float uMaxStretchIntensity;
 
     // Maximum percentage to stretch beyond bounds  of target
-    uniform float uStretchAffectedDist;
+    uniform float uStretchAffectedDistX;
+    uniform float uStretchAffectedDistY;
 
     // Distance stretched as a function of the normalized overscroll times
     // scale intensity
@@ -138,7 +139,7 @@
             outU,
             inU,
             uOverscrollX,
-            uStretchAffectedDist,
+            uStretchAffectedDistX,
             uDistanceStretchedX,
             uDistDiffX
         );
@@ -146,7 +147,7 @@
             outV,
             inV,
             uOverscrollY,
-            uStretchAffectedDist,
+            uStretchAffectedDistY,
             uDistanceStretchedY,
             uDistDiffY
         );
@@ -166,16 +167,14 @@
         return mStretchFilter;
     }
 
-    float distanceNotStretchedX = maxStretchAmount / stretchArea.width();
-    float distanceNotStretchedY = maxStretchAmount / stretchArea.height();
-    float normOverScrollDistX = mStretchDirection.x();
-    float normOverScrollDistY = mStretchDirection.y();
-    float distanceStretchedX = maxStretchAmount / (1 + abs(normOverScrollDistX));
-    float distanceStretchedY = maxStretchAmount / (1 + abs(normOverScrollDistY));
-    float diffX = distanceStretchedX - distanceNotStretchedX;
-    float diffY = distanceStretchedY - distanceNotStretchedY;
     float viewportWidth = stretchArea.width();
     float viewportHeight = stretchArea.height();
+    float normOverScrollDistX = mStretchDirection.x();
+    float normOverScrollDistY = mStretchDirection.y();
+    float distanceStretchedX = maxStretchAmountX / (1 + abs(normOverScrollDistX));
+    float distanceStretchedY = maxStretchAmountY / (1 + abs(normOverScrollDistY));
+    float diffX = distanceStretchedX;
+    float diffY = distanceStretchedY;
 
     if (mBuilder == nullptr) {
         mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
@@ -183,7 +182,8 @@
 
     mBuilder->child("uContentTexture") = snapshotImage->makeShader(
             SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
-    mBuilder->uniform("uStretchAffectedDist").set(&maxStretchAmount, 1);
+    mBuilder->uniform("uStretchAffectedDistX").set(&maxStretchAmountX, 1);
+    mBuilder->uniform("uStretchAffectedDistY").set(&maxStretchAmountY, 1);
     mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
     mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
     mBuilder->uniform("uDistDiffX").set(&diffX, 1);
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
index d2da06b..8221b41 100644
--- a/libs/hwui/effects/StretchEffect.h
+++ b/libs/hwui/effects/StretchEffect.h
@@ -33,8 +33,12 @@
         SmoothStep,
     };
 
-    StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmount)
-            : stretchArea(area), maxStretchAmount(maxStretchAmount), mStretchDirection(direction) {}
+    StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmountX,
+                  float maxStretchAmountY)
+            : stretchArea(area)
+            , maxStretchAmountX(maxStretchAmountX)
+            , maxStretchAmountY(maxStretchAmountY)
+            , mStretchDirection(direction) {}
 
     StretchEffect() {}
 
@@ -50,7 +54,8 @@
         this->stretchArea = other.stretchArea;
         this->mStretchDirection = other.mStretchDirection;
         this->mStretchFilter = nullptr;
-        this->maxStretchAmount = other.maxStretchAmount;
+        this->maxStretchAmountX = other.maxStretchAmountX;
+        this->maxStretchAmountY = other.maxStretchAmountY;
         return *this;
     }
 
@@ -67,13 +72,15 @@
             return setEmpty();
         }
         stretchArea.join(other.stretchArea);
-        maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
+        maxStretchAmountX = std::max(maxStretchAmountX, other.maxStretchAmountX);
+        maxStretchAmountY = std::max(maxStretchAmountY, other.maxStretchAmountY);
     }
 
     sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
 
     SkRect stretchArea {0, 0, 0, 0};
-    float maxStretchAmount = 0;
+    float maxStretchAmountX = 0;
+    float maxStretchAmountY = 0;
 
     void setStretchDirection(const SkVector& direction) {
         mStretchFilter = nullptr;
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index fc7d0d1..fffa806 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -181,9 +181,10 @@
 
 static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
                                                 jfloat left, jfloat top, jfloat right,
-                                                jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
-    StretchEffect effect =
-            StretchEffect(SkRect::MakeLTRB(left, top, right, bottom), {.fX = vX, .fY = vY}, max);
+                                                jfloat bottom, jfloat vX, jfloat vY, jfloat maxX,
+                                                jfloat maxY) {
+    StretchEffect effect = StretchEffect(SkRect::MakeLTRB(left, top, right, bottom),
+                                         {.fX = vX, .fY = vY}, maxX, maxY);
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
             effect);
@@ -662,7 +663,7 @@
             env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
                                 info.canvasContext.getFrameNumber(), area.left, area.top,
                                 area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
-                                effect->maxStretchAmount);
+                                effect->maxStretchAmountX, effect->maxStretchAmountY);
 #endif
             env->DeleteLocalRef(localref);
         }
@@ -738,7 +739,7 @@
         {"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
         {"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
         {"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
-        {"nStretch", "(JFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
+        {"nStretch", "(JFFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
         {"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
         {"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
         {"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
index f0e6299..c4b0072 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/EdgeEffectStretchActivity.java
@@ -18,8 +18,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.widget.HorizontalScrollView;
-import android.widget.ScrollView;
 
 public class EdgeEffectStretchActivity extends Activity {
 
@@ -27,10 +25,5 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.stretch_layout);
-        HorizontalScrollView hsv = findViewById(R.id.horizontal_scroll_view);
-        hsv.setStretchDistance(50f);
-
-        ScrollView sv = findViewById(R.id.scroll_view);
-        sv.setStretchDistance(50f);
     }
 }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
index 65d7363..6b6287d 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java
@@ -75,11 +75,11 @@
                 // Although we could do this in a single call, the real one won't be - so mimic that
                 if (dir.x != 0f) {
                     node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
-                            dir.x, 0f, maxStretchAmount);
+                            dir.x, 0f, maxStretchAmount, maxStretchAmount);
                 }
                 if (dir.y != 0f) {
                     node.stretch(0f, 0f, (float) getWidth(), (float) getHeight(),
-                            0f, dir.y, maxStretchAmount);
+                            0f, dir.y, maxStretchAmount, maxStretchAmount);
                 }
             }
         };
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
index 9bd933a..912aee6 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -415,6 +415,7 @@
                         bounds.height(),
                         mOverScrollX,
                         mOverScrollY,
+                        mStretchDistance,
                         mStretchDistance
                 );
             }
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
index d604244..67b9be5 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchySurfaceViewActivity.java
@@ -99,7 +99,8 @@
                 super.onDraw(canvas);
 
                 RenderNode node = ((RecordingCanvas) canvas).mNode;
-                node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f, 1f, 400f);
+                node.stretch(0f, 0f, getWidth(), getHeight() / 2f, 0f,
+                        1f, 400f, 400f);
             }
         };