Merge "Allow clipping of notifications to be toggled."
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 22c25ab..e603c9f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -50,6 +50,7 @@
 
         <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
+            android:layout_marginTop="@dimen/notification_panel_margin_top"
             android:layout_width="@dimen/notification_panel_width"
             android:layout_height="match_parent"
             android:layout_gravity="@integer/notification_panel_layout_gravity"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 74992d8..fd019bb 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -359,4 +359,13 @@
     <!-- Whether or not to show the expand button at the end of the notification header. -->
     <bool name="config_showNotificationExpandButtonAtEnd">false</bool>
 
+    <!-- Whether or the notifications should be clipped to be reduced in height if it has been
+         scrolled to the top of the screen. -->
+    <bool name="config_clipNotificationScrollToTop">true</bool>
+
+    <!-- Whether or not the notification contents should be clipped to any background that is
+         set on the notification container. For example, if this value is true and the background
+         has rounded corners, then the contents will be clipped to those corners. -->
+    <bool name="config_clipNotificationsToOutline">false</bool>
+
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d031fe8..ae125b5 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -200,6 +200,13 @@
     <!-- Width for the notification panel and related windows -->
     <dimen name="match_parent">-1px</dimen>
     <dimen name="standard_notification_panel_width">416dp</dimen>
+
+    <!-- The top margin of the panel that holds the list of notifications. -->
+    <dimen name="notification_panel_margin_top">0dp</dimen>
+
+    <!-- The bottom margin of the panel that holds the list of notifications. -->
+    <dimen name="notification_panel_margin_bottom">0dp</dimen>
+
     <dimen name="notification_panel_width">@dimen/match_parent</dimen>
 
     <dimen name="volume_dialog_panel_width">@dimen/standard_notification_panel_width</dimen>
@@ -320,6 +327,9 @@
     <!-- The height of the divider between the individual notifications. -->
     <dimen name="notification_divider_height">0.5dp</dimen>
 
+    <!-- The corner radius of the shadow behind the notification. -->
+    <dimen name="notification_shadow_radius">0dp</dimen>
+
     <!-- The height of a notification header -->
     <dimen name="notification_header_height">53dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7277ff9..0c74f45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -798,7 +798,9 @@
         return mMenuRow;
     }
 
+    @Override
     public void onDensityOrFontScaleChanged() {
+        super.onDensityOrFontScaleChanged();
         initDimens();
         if (mIsSummaryWithChildren) {
             if (mChildrenContainer != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index 553c478..f687708 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -34,6 +34,7 @@
     private final Rect mOutlineRect = new Rect();
     private boolean mCustomOutline;
     private float mOutlineAlpha = -1f;
+    private float mOutlineRadius;
 
     /**
      * {@code true} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -46,12 +47,13 @@
         public void getOutline(View view, Outline outline) {
             int translation = mShouldTranslateContents ? 0 : (int) getTranslation();
             if (!mCustomOutline) {
-                outline.setRect(translation,
+                outline.setRoundRect(translation,
                         mClipTopAmount,
                         getWidth() + translation,
-                        Math.max(getActualHeight() - mClipBottomAmount, mClipTopAmount));
+                        Math.max(getActualHeight() - mClipBottomAmount, mClipTopAmount),
+                        mOutlineRadius);
             } else {
-                outline.setRect(mOutlineRect);
+                outline.setRoundRect(mOutlineRect, mOutlineRadius);
             }
             outline.setAlpha(mOutlineAlpha);
         }
@@ -60,9 +62,20 @@
     public ExpandableOutlineView(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOutlineProvider(mProvider);
+        initDimens();
+    }
+
+    private void initDimens() {
         Resources res = getResources();
         mShouldTranslateContents =
                 res.getBoolean(R.bool.config_translateNotificationContentsOnSwipe);
+        mOutlineRadius = res.getDimension(R.dimen.notification_shadow_radius);
+        setClipToOutline(res.getBoolean(R.bool.config_clipNotificationsToOutline));
+    }
+
+    public void onDensityOrFontScaleChanged() {
+        initDimens();
+        invalidateOutline();
     }
 
     @Override
@@ -119,8 +132,8 @@
     }
 
     /**
-     * @return whether the view currently needs an outline. This is usually false in case it doesn't
-     * have a background.
+     * @return Whether the view currently needs an outline. This is usually {@code false} in case
+     * it doesn't have a background.
      */
     protected boolean needsOutline() {
         if (isChildInGroup()) {
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 771e02a..859c435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -147,6 +147,7 @@
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
     private int mTopPadding;
+    private int mBottomMargin;
     private int mBottomInset = 0;
 
     /**
@@ -510,17 +511,19 @@
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mOverflingDistance = configuration.getScaledOverflingDistance();
-        mCollapsedSize = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_min_height);
+
+        Resources res = context.getResources();
+        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
         mStackScrollAlgorithm.initView(context);
         mAmbientState.reload(context);
-        mPaddingBetweenElements = Math.max(1, context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_divider_height));
-        mIncreasedPaddingBetweenElements = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
-        mMinTopOverScrollToEscape = getResources().getDimensionPixelSize(
+        mPaddingBetweenElements = Math.max(1,
+                res.getDimensionPixelSize(R.dimen.notification_divider_height));
+        mIncreasedPaddingBetweenElements =
+                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+        mMinTopOverScrollToEscape = res.getDimensionPixelSize(
                 R.dimen.min_top_overscroll_to_qs);
-        mStatusBarHeight = getResources().getDimensionPixelOffset(R.dimen.status_bar_height);
+        mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
+        mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
     }
 
     public void setDrawBackgroundAsSrc(boolean asSrc) {
@@ -1993,7 +1996,7 @@
                 }
             }
         }
-        mContentHeight = height + mTopPadding;
+        mContentHeight = height + mTopPadding + mBottomMargin;
         updateScrollability();
         mAmbientState.setLayoutMaxHeight(mContentHeight);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index b5db78d..10a9644 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -17,10 +17,10 @@
 package com.android.systemui.statusbar.stack;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-
 import com.android.systemui.R;
 import com.android.systemui.statusbar.DismissView;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -48,6 +48,7 @@
 
     private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
     private boolean mIsExpanded;
+    private boolean mClipNotificationScrollToTop;
     private int mStatusBarHeight;
 
     public StackScrollAlgorithm(Context context) {
@@ -59,13 +60,14 @@
     }
 
     private void initConstants(Context context) {
-        mPaddingBetweenElements = context.getResources().getDimensionPixelSize(
+        Resources res = context.getResources();
+        mPaddingBetweenElements = res.getDimensionPixelSize(
                 R.dimen.notification_divider_height);
-        mIncreasedPaddingBetweenElements = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
-        mCollapsedSize = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_min_height);
-        mStatusBarHeight = context.getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+        mIncreasedPaddingBetweenElements =
+                res.getDimensionPixelSize(R.dimen.notification_divider_height_increased);
+        mCollapsedSize = res.getDimensionPixelSize(R.dimen.notification_min_height);
+        mStatusBarHeight = res.getDimensionPixelSize(R.dimen.status_bar_height);
+        mClipNotificationScrollToTop = res.getBoolean(R.bool.config_clipNotificationScrollToTop);
     }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -142,7 +144,8 @@
             float newNotificationEnd = newYTranslation + newHeight;
             boolean isHeadsUp = (child instanceof ExpandableNotificationRow)
                     && ((ExpandableNotificationRow) child).isPinned();
-            if (!state.inShelf && newYTranslation < previousNotificationEnd
+            if (mClipNotificationScrollToTop
+                    && !state.inShelf && newYTranslation < previousNotificationEnd
                     && (!isHeadsUp || ambientState.isShadeExpanded())) {
                 // The previous view is overlapping on top, clip!
                 float overlapAmount = previousNotificationEnd - newYTranslation;