Clipping to the top roundness when scrolling

We are now clipping properly to the top roundness,
such that the scrolling works properly and you never
see a hard edge on the top.

Test: add notifications, scroll, observe nice clipping
Bug: 69168591
Change-Id: I991eb33337eee8f0e1cee56e01e4e457146cb145
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 8a3645a..66b3a75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -58,6 +58,7 @@
     private static final Path EMPTY_PATH = new Path();
 
     private final Rect mOutlineRect = new Rect();
+    private final Path mClipPath = new Path();
     private boolean mCustomOutline;
     private float mOutlineAlpha = -1f;
     private float mOutlineRadius;
@@ -75,6 +76,8 @@
      * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
      */
     protected boolean mShouldTranslateContents;
+    private boolean mClipRoundedToClipTopAmount;
+    private float mDistanceToTopRoundness = -1;
 
     private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
         @Override
@@ -159,8 +162,8 @@
         return roundedRectPath;
     }
 
-    private void getRoundedRectPath(int left, int top, int right, int bottom, float topRoundness,
-            float bottomRoundness, Path outPath) {
+    public static void getRoundedRectPath(int left, int top, int right, int bottom,
+            float topRoundness, float bottomRoundness, Path outPath) {
         outPath.reset();
         int width = right - left;
         float topRoundnessX = topRoundness;
@@ -197,20 +200,50 @@
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         canvas.save();
+        Path intersectPath = null;
+        if (mClipRoundedToClipTopAmount) {
+            int left = 0;
+            int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
+            int right = getWidth();
+            int bottom = (int) Math.max(getActualHeight() - mClipBottomAmount,
+                    top + mOutlineRadius);
+            ExpandableOutlineView.getRoundedRectPath(left, top, right, bottom, mOutlineRadius,
+                    0.0f,
+                    mClipPath);
+            intersectPath = mClipPath;
+        }
+        boolean clipped = false;
         if (childNeedsClipping(child)) {
             Path clipPath = getCustomClipPath(child);
             if (clipPath == null) {
                 clipPath = getClipPath();
             }
             if (clipPath != null) {
+                if (intersectPath != null) {
+                    clipPath.op(intersectPath, Path.Op.INTERSECT);
+                }
                 canvas.clipPath(clipPath);
+                clipped = true;
             }
         }
+        if (!clipped && intersectPath != null) {
+            canvas.clipPath(intersectPath);
+        }
         boolean result = super.drawChild(canvas, child, drawingTime);
         canvas.restore();
         return result;
     }
 
+    @Override
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+        super.setDistanceToTopRoundness(distanceToTopRoundness);
+        if (distanceToTopRoundness != mDistanceToTopRoundness) {
+            mClipRoundedToClipTopAmount = distanceToTopRoundness >= 0;
+            mDistanceToTopRoundness = distanceToTopRoundness;
+            invalidate();
+        }
+    }
+
     protected boolean childNeedsClipping(View child) {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index f762513..eafa825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -130,6 +130,14 @@
         }
     }
 
+    /**
+     * Set the distance to the top roundness, from where we should start clipping a value above
+     * or equal to 0 is the effective distance, and if a value below 0 is received, there should
+     * be no clipping.
+     */
+    public void setDistanceToTopRoundness(float distanceToTopRoundness) {
+    }
+
     public void setActualHeight(int actualHeight) {
         setActualHeight(actualHeight, true /* notifyListeners */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 1d2baf1..6350107 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -657,11 +657,32 @@
 
     private void onPreDrawDuringAnimation() {
         mShelf.updateAppearance();
+        updateClippingToTopRoundedCorner();
         if (!mNeedsAnimation && !mChildrenUpdateRequested) {
             updateBackground();
         }
     }
 
+    private void updateClippingToTopRoundedCorner() {
+        Float clipStart = (float) mTopPadding;
+        Float clipEnd = clipStart + mCornerRadius;
+        boolean first = true;
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (child.getVisibility() == GONE) {
+                continue;
+            }
+            float start = child.getTranslationY();
+            float end = start + Math.max(child.getActualHeight() - child.getClipBottomAmount(),
+                    0);
+            boolean clip = clipStart > start && clipStart < end
+                    || clipEnd >= start && clipEnd <= end;
+            clip &= !(first && mOwnScrollY == 0);
+            child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0) : -1);
+            first = false;
+        }
+    }
+
     private void updateScrollStateForAddedChildren() {
         if (mChildrenToAddAnimated.isEmpty()) {
             return;
@@ -2981,6 +3002,7 @@
             mAnimationEvents.clear();
             updateBackground();
             updateViewShadows();
+            updateClippingToTopRoundedCorner();
         } else {
             applyCurrentState();
         }
@@ -3674,6 +3696,7 @@
         setAnimationRunning(false);
         updateBackground();
         updateViewShadows();
+        updateClippingToTopRoundedCorner();
     }
 
     private void updateViewShadows() {