Heads up notification can now scroll away after it was seen

Previously it would stick to the top forever, which was very
undesirable and unclear.

Fixes: 	62214133
Test: manual, add multiple huns, nothing sticks to the top
Test: add hun when qs expanded
Change-Id: I3338129ced49dacf01c658767aaedd0f1a408c44
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 23d9cae..e04bd0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -112,6 +112,7 @@
     private int mNotificationMaxHeight;
     private int mNotificationAmbientHeight;
     private int mIncreasedPaddingBetweenElements;
+    private boolean mMustStayOnScreen;
 
     /** Does this row contain layouts that can adapt to row expansion */
     private boolean mExpandable;
@@ -491,6 +492,7 @@
             notifyHeightChanged(false  /* needsAnimation */);
         }
         if (isHeadsUp) {
+            mMustStayOnScreen = true;
             setAboveShelf(true);
         } else if (isAboveShelf() != wasAboveShelf) {
             mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
@@ -517,6 +519,12 @@
         addChildNotification(row, -1);
     }
 
+    @Override
+    public void setHeadsUpIsVisible() {
+        super.setHeadsUpIsVisible();
+        mMustStayOnScreen = false;
+    }
+
     /**
      * Add a child notification to this view.
      *
@@ -1942,7 +1950,7 @@
 
     @Override
     public boolean mustStayOnScreen() {
-        return mIsHeadsUp;
+        return mIsHeadsUp && mMustStayOnScreen;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 18b9860..f762513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -478,6 +478,9 @@
         return false;
     }
 
+    public void setHeadsUpIsVisible() {
+    }
+
     public boolean isChildInGroup() {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
index e0fd481..0650e23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java
@@ -95,6 +95,12 @@
     public boolean inShelf;
 
     /**
+     * A state indicating whether a headsup is currently fully visible, even when not scrolled.
+     * Only valid if the view is heads upped.
+     */
+    public boolean headsUpIsVisible;
+
+    /**
      * How much the child overlaps with the previous child on top. This is used to
      * show the background properly when the child on top is translating away.
      */
@@ -126,6 +132,7 @@
             clipTopAmount = svs.clipTopAmount;
             notGoneIndex = svs.notGoneIndex;
             location = svs.location;
+            headsUpIsVisible = svs.headsUpIsVisible;
         }
     }
 
@@ -175,6 +182,10 @@
 
             expandableView.setTransformingInShelf(false);
             expandableView.setInShelf(inShelf);
+
+            if (headsUpIsVisible) {
+                expandableView.setHeadsUpIsVisible();
+            }
         }
     }
 
@@ -229,6 +240,10 @@
             expandableView.setTransformingInShelf(true);
         }
         expandableView.setInShelf(this.inShelf);
+
+        if (headsUpIsVisible) {
+            expandableView.setHeadsUpIsVisible();
+        }
     }
 
     private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
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 ebebfac..a3d2423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -624,7 +624,7 @@
         if (childViewState == null) {
             return false;
         }
-        if ((childViewState.location &= ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
+        if ((childViewState.location & ExpandableViewState.VISIBLE_LOCATIONS) == 0) {
             return false;
         }
         if (row.getVisibility() != View.VISIBLE) {
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 c060b08..195607d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -135,7 +135,7 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             ExpandableViewState state = resultState.getViewStateForView(child);
-            if (!child.mustStayOnScreen()) {
+            if (!child.mustStayOnScreen() || state.headsUpIsVisible) {
                 previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
                 previousNotificationStart = Math.max(drawStart, previousNotificationStart);
             }
@@ -378,6 +378,13 @@
         boolean isEmptyShadeView = child instanceof EmptyShadeView;
 
         childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
+        float inset = ambientState.getTopPadding() + ambientState.getStackTranslation();
+        if (child.mustStayOnScreen() && childViewState.yTranslation >= 0) {
+            // Even if we're not scrolled away we're in view and we're also not in the
+            // shelf. We can relax the constraints and let us scroll off the top!
+            float end = childViewState.yTranslation + childViewState.height + inset;
+            childViewState.headsUpIsVisible = end < ambientState.getMaxHeadsUpTranslation();
+        }
         if (isDismissView) {
             childViewState.yTranslation = Math.min(childViewState.yTranslation,
                     ambientState.getInnerHeight() - childHeight);
@@ -396,8 +403,7 @@
             Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
         }
 
-        childViewState.yTranslation += ambientState.getTopPadding()
-                + ambientState.getStackTranslation();
+        childViewState.yTranslation += inset;
         return currentYPosition;
     }
 
@@ -420,19 +426,21 @@
                 break;
             }
             ExpandableViewState childState = resultState.getViewStateForView(row);
-            if (topHeadsUpEntry == null) {
+            if (topHeadsUpEntry == null && row.mustStayOnScreen() && !childState.headsUpIsVisible) {
                 topHeadsUpEntry = row;
                 childState.location = ExpandableViewState.LOCATION_FIRST_HUN;
             }
             boolean isTopEntry = topHeadsUpEntry == row;
             float unmodifiedEndLocation = childState.yTranslation + childState.height;
             if (mIsExpanded) {
-                // Ensure that the heads up is always visible even when scrolled off
-                clampHunToTop(ambientState, row, childState);
-                if (i == 0 && ambientState.isAboveShelf(row)) {
-                    // the first hun can't get off screen.
-                    clampHunToMaxTranslation(ambientState, row, childState);
-                    childState.hidden = false;
+                if (row.mustStayOnScreen() && !childState.headsUpIsVisible) {
+                    // Ensure that the heads up is always visible even when scrolled off
+                    clampHunToTop(ambientState, row, childState);
+                    if (i == 0 && ambientState.isAboveShelf(row)) {
+                        // the first hun can't get off screen.
+                        clampHunToMaxTranslation(ambientState, row, childState);
+                        childState.hidden = false;
+                    }
                 }
             }
             if (row.isPinned()) {
@@ -493,6 +501,7 @@
         if (childViewState.yTranslation >= shelfStart) {
             childViewState.hidden = true;
             childViewState.inShelf = true;
+            childViewState.headsUpIsVisible = false;
         }
         if (!ambientState.isShadeExpanded()) {
             childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
@@ -531,7 +540,8 @@
         ExpandableViewState childViewState = resultState.getViewStateForView(child);
         int zDistanceBetweenElements = ambientState.getZDistanceBetweenElements();
         float baseZ = ambientState.getBaseZHeight();
-        if (child.mustStayOnScreen() && !ambientState.isDozingAndNotPulsing(child)
+        if (child.mustStayOnScreen() && !childViewState.headsUpIsVisible
+                && !ambientState.isDozingAndNotPulsing(child)
                 && childViewState.yTranslation < ambientState.getTopPadding()
                 + ambientState.getStackTranslation()) {
             if (childrenOnTop != 0.0f) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index e3c746b..588b758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -84,6 +84,7 @@
         viewState.scaleX = view.getScaleX();
         viewState.scaleY = view.getScaleY();
         viewState.inShelf = false;
+        viewState.headsUpIsVisible = false;
     }
 
     public ExpandableViewState getViewStateForView(View requestedView) {