Enabled usage of expand button in heads up notifications

Heads up notifications can now be expanded with the expand
button. The notification stays there until the user dismisses it
or collapses it again.

Change-Id: I0f1b729915317bbbd6f13c3d968c933ffbe6feeb
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index ce4eff5..62a2172 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1276,7 +1276,7 @@
     }
 
     @Override
-    public void onExpandClicked(View clickedView, boolean nowExpanded) {
+    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
     }
 
     protected class H extends Handler {
@@ -1359,6 +1359,7 @@
                     parent, false);
             row.setExpansionLogger(this, entry.notification.getKey());
             row.setGroupManager(mGroupManager);
+            row.setHeadsUpManager(mHeadsUpManager);
             row.setRemoteInputController(mRemoteInputController);
             row.setOnExpandClickListener(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 83853a2..fe219b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,6 +36,7 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 import com.android.systemui.statusbar.stack.StackScrollState;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -59,6 +60,11 @@
     private boolean mHasUserChangedExpansion;
     /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
     private boolean mUserExpanded;
+
+    /**
+     * Has this notification been expanded while it was pinned
+     */
+    private boolean mExpandedWhenPinned;
     /** Is the user touching this row */
     private boolean mUserLocked;
     /** Are we showing the "public" version */
@@ -103,6 +109,7 @@
     private boolean mIsSystemChildExpanded;
     private boolean mIsPinned;
     private FalsingManager mFalsingManager;
+    private HeadsUpManager mHeadsUpManager;
     private NotificationHeaderUtil mHeaderUtil = new NotificationHeaderUtil(this);
 
     private boolean mJustClicked;
@@ -115,14 +122,19 @@
         public void onClick(View v) {
             if (!mShowingPublic && mGroupManager.isSummaryOfGroup(mStatusBarNotification)) {
                 mGroupManager.toggleGroupExpansion(mStatusBarNotification);
-                mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
+                mOnExpandClickListener.onExpandClicked(mEntry,
                         mGroupManager.isGroupExpanded(mStatusBarNotification));
             } else {
-                boolean nowExpanded = !isExpanded();
-                setUserExpanded(nowExpanded);
+                boolean nowExpanded;
+                if (isPinned()) {
+                    nowExpanded = !mExpandedWhenPinned;
+                    mExpandedWhenPinned = nowExpanded;
+                } else {
+                    nowExpanded = !isExpanded();
+                    setUserExpanded(nowExpanded);
+                }
                 notifyHeightChanged(true);
-                mOnExpandClickListener.onExpandClicked(ExpandableNotificationRow.this,
-                        nowExpanded);
+                mOnExpandClickListener.onExpandClicked(mEntry, nowExpanded);
             }
         }
     };
@@ -386,6 +398,12 @@
      */
     public void setPinned(boolean pinned) {
         mIsPinned = pinned;
+        if (pinned) {
+            setIconAnimationRunning(true);
+            mExpandedWhenPinned = false;
+        } else if (mExpandedWhenPinned) {
+            setUserExpanded(true);
+        }
         setChronometerRunning(mLastChronometerRunning);
     }
 
@@ -393,11 +411,22 @@
         return mIsPinned;
     }
 
-    public int getHeadsUpHeight() {
+    /**
+     * @param atLeastMinHeight should the value returned be at least the minimum height.
+     *                         Used to avoid cyclic calls
+     * @return the height of the heads up notification when pinned
+     */
+    public int getPinnedHeadsUpHeight(boolean atLeastMinHeight) {
         if (mIsSummaryWithChildren) {
             return mChildrenContainer.getIntrinsicHeight();
         }
-        return mHeadsUpHeight;
+        if(mExpandedWhenPinned) {
+            return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
+        } else if (atLeastMinHeight) {
+            return Math.max(getMinHeight(), mHeadsUpHeight);
+        } else {
+            return mHeadsUpHeight;
+        }
     }
 
     /**
@@ -461,6 +490,10 @@
         mOnExpandClickListener = onExpandClickListener;
     }
 
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+        mHeadsUpManager = headsUpManager;
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -718,7 +751,6 @@
         if (isUserLocked()) {
             return getActualHeight();
         }
-        boolean inExpansionState = isExpanded();
         if (mGuts != null && mGuts.areGutsExposed()) {
             return mGuts.getHeight();
         } else if ((isChildInGroup() && !isGroupExpanded())) {
@@ -728,12 +760,14 @@
         } else if (mIsSummaryWithChildren && !mOnKeyguard) {
             return mChildrenContainer.getIntrinsicHeight();
         } else if (mIsHeadsUp) {
-            if (inExpansionState) {
+            if (isPinned()) {
+                return getPinnedHeadsUpHeight(true /* atLeastMinHeight */);
+            } else if (isExpanded()) {
                 return Math.max(getMaxExpandHeight(), mHeadsUpHeight);
             } else {
                 return Math.max(getMinHeight(), mHeadsUpHeight);
             }
-        } else if (inExpansionState) {
+        } else if (isExpanded()) {
             return getMaxExpandHeight();
         } else {
             return getMinHeight();
@@ -961,8 +995,12 @@
 
     @Override
     public int getMinHeight() {
-        if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
+        if (mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
+                return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
+        } else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
             return mChildrenContainer.getMinHeight();
+        } else if (mIsHeadsUp) {
+            return mHeadsUpHeight;
         }
         NotificationContentView showingLayout = getShowingLayout();
         return showingLayout.getMinHeight();
@@ -1067,6 +1105,6 @@
     }
 
     public interface OnExpandClickListener {
-        void onExpandClicked(View clickedView, boolean nowExpanded);
+        void onExpandClicked(NotificationData.Entry clickedEntry, boolean nowExpanded);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index df8c7fa..26abc48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -110,12 +110,12 @@
                     mInitialTouchX = x;
                     mInitialTouchY = y;
                     int expandedHeight = mPickedChild.getActualHeight();
+                    mHeadsUpManager.unpinAll();
                     mPanel.setPanelScrimMinFraction((float) expandedHeight
                             / mPanel.getMaxPanelHeight());
                     mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight
                             + mNotificationsTopPadding);
                     mPanel.clearNotificattonEffects();
-                    mHeadsUpManager.unpinAll();
                     return true;
                 }
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 7a4adbc..9671585 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -4035,9 +4035,10 @@
     }
 
     @Override
-    public void onExpandClicked(View clickedView, boolean nowExpanded) {
+    public void onExpandClicked(Entry clickedEntry, boolean nowExpanded) {
+        mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
         if (mState == StatusBarState.KEYGUARD && nowExpanded) {
-            goToLockedShade(clickedView);
+            goToLockedShade(clickedEntry.row);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index b38c3fe..f74f3c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -387,7 +387,7 @@
                     row.getLocationOnScreen(mTmpTwoArray);
                     minX = mTmpTwoArray[0];
                     maxX = mTmpTwoArray[0] + row.getWidth();
-                    maxY = row.getHeadsUpHeight();
+                    maxY = row.getIntrinsicHeight();
                     break;
                 }
             }
@@ -448,6 +448,8 @@
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
             setEntryPinned(entry, false /* isPinned */);
+            // maybe it got un sticky
+            entry.updateEntry(false /* updatePostTime */);
         }
     }
 
@@ -470,6 +472,10 @@
         mTrackingHeadsUp = trackingHeadsUp;
     }
 
+    public boolean isTrackingHeadsUp() {
+        return mTrackingHeadsUp;
+    }
+
     public void setIsExpanded(boolean isExpanded) {
         if (isExpanded != mIsExpanded) {
             mIsExpanded = isExpanded;
@@ -482,9 +488,16 @@
         }
     }
 
-    public int getTopHeadsUpHeight() {
+    /**
+     * @return the height of the top heads up notification when pinned. This is different from the
+     *         intrinsic height, which also includes whether the notification is system expanded and
+     *         is mainly used when dragging down from a heads up notification.
+     */
+    public int getTopHeadsUpPinnedHeight() {
         HeadsUpEntry topEntry = getTopEntry();
-        return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
+        return topEntry != null && topEntry.entry != null
+                ? topEntry.entry.row.getPinnedHeadsUpHeight(true /* atLeastMinHeight */)
+                : 0;
     }
 
     /**
@@ -558,6 +571,22 @@
     }
 
     /**
+     * Set an entry to be expanded and therefore stick in the heads up area if it's pinned
+     * until it's collapsed again.
+     */
+    public void setExpanded(NotificationData.Entry entry, boolean expanded) {
+        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+        if (headsUpEntry != null && headsUpEntry.expanded != expanded) {
+            headsUpEntry.expanded = expanded;
+            if (expanded) {
+                headsUpEntry.removeAutoRemovalCallbacks();
+            } else {
+                headsUpEntry.updateEntry(false /* updatePostTime */);
+            }
+        }
+    }
+
+    /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
      */
@@ -567,6 +596,7 @@
         public long earliestRemovaltime;
         private Runnable mRemoveHeadsUpRunnable;
         public boolean remoteInputActive;
+        public boolean expanded;
 
         public void setEntry(final NotificationData.Entry entry) {
             this.entry = entry;
@@ -601,7 +631,7 @@
             if (mEntriesToRemoveAfterExpand.contains(entry)) {
                 mEntriesToRemoveAfterExpand.remove(entry);
             }
-            if (!hasFullScreenIntent(entry) && !mRemoteInputActive) {
+            if (!isSticky()) {
                 long finishTime = postTime + mHeadsUpNotificationDecay;
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
                 mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
@@ -609,6 +639,11 @@
             mSortedEntries.add(HeadsUpEntry.this);
         }
 
+        private boolean isSticky() {
+            return (entry.row.isPinned() && expanded)
+                    || remoteInputActive || hasFullScreenIntent(entry);
+        }
+
         @Override
         public int compareTo(HeadsUpEntry o) {
             boolean selfFullscreen = hasFullScreenIntent(entry);
@@ -641,6 +676,8 @@
             removeAutoRemovalCallbacks();
             entry = null;
             mRemoveHeadsUpRunnable = null;
+            expanded = false;
+            remoteInputActive = false;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 3a97be6..a3f571e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -155,13 +155,7 @@
     }
 
     public int getInnerHeight() {
-        return mLayoutHeight - mTopPadding - getTopHeadsUpPushIn();
-    }
-
-    private int getTopHeadsUpPushIn() {
-        ExpandableNotificationRow topHeadsUpEntry = getTopHeadsUpEntry();
-        return topHeadsUpEntry != null ? topHeadsUpEntry.getHeadsUpHeight()
-                - topHeadsUpEntry.getMinHeight(): 0;
+        return mLayoutHeight - mTopPadding;
     }
 
     public boolean isShadeExpanded() {
@@ -180,11 +174,6 @@
         return mMaxHeadsUpTranslation;
     }
 
-    public ExpandableNotificationRow getTopHeadsUpEntry() {
-        HeadsUpManager.HeadsUpEntry topEntry = mHeadsUpManager.getTopEntry();
-        return topEntry == null ? null : topEntry.entry.row;
-    }
-
     public void setDismissAllInProgress(boolean dismissAllInProgress) {
         mDismissAllInProgress = dismissAllInProgress;
     }
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 0ed1527..bb0fdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -495,27 +495,28 @@
         int stackHeight;
         float paddingOffset;
         boolean trackingHeadsUp = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp();
-        int normalUnfoldPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight()
+        int normalUnfoldPositionStart = trackingHeadsUp
+                ? mHeadsUpManager.getTopHeadsUpPinnedHeight()
                 : minStackHeight;
         if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalUnfoldPositionStart
                 || getNotGoneChildCount() == 0) {
             paddingOffset = mTopPaddingOverflow;
             stackHeight = newStackHeight;
         } else {
-
-            // We did not reach the position yet where we actually start growing,
-            // so we translate the stack upwards.
-            int translationY = (newStackHeight - minStackHeight);
-            // A slight parallax effect is introduced in order for the stack to catch up with
-            // the top card.
-            float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
-                    / minStackHeight;
-            partiallyThere = Math.max(0, partiallyThere);
+            int translationY;
             if (!trackingHeadsUp) {
+                // We did not reach the position yet where we actually start growing,
+                // so we translate the stack upwards.
+                translationY = (newStackHeight - minStackHeight);
+                // A slight parallax effect is introduced in order for the stack to catch up with
+                // the top card.
+                float partiallyThere = (newStackHeight - mTopPadding - mTopPaddingOverflow)
+                        / minStackHeight;
+                partiallyThere = Math.max(0, partiallyThere);
                 translationY += (1 - partiallyThere) * (mBottomStackPeekSize +
                         mCollapseSecondCardPadding);
             } else {
-                translationY = (int) (height - mHeadsUpManager.getTopHeadsUpHeight());
+                translationY = (int) (height - normalUnfoldPositionStart);
             }
             paddingOffset = translationY - mTopPadding;
             stackHeight = (int) (height - (translationY - mTopPadding));
@@ -1506,7 +1507,7 @@
 
     public int getMinStackHeight() {
         final ExpandableView firstChild = getFirstChildNotGone();
-        final int firstChildMinHeight = firstChild != null ? (int) firstChild.getMinHeight()
+        final int firstChildMinHeight = firstChild != null ? firstChild.getMinHeight()
                 : mCollapsedSize;
         return firstChildMinHeight + mBottomStackPeekSize + mCollapseSecondCardPadding;
     }
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 5496963..822012d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -410,10 +410,6 @@
         // How far in is the element currently transitioning into the bottom stack.
         float yPositionInScrollView = 0.0f;
 
-        // If we have a heads-up higher than the collapsed height we need to add the difference to
-        // the padding of all other elements, i.e push in the top stack slightly.
-        ExpandableNotificationRow topHeadsUpEntry = ambientState.getTopHeadsUpEntry();
-
         int childCount = algorithmState.visibleChildren.size();
         int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f
                 ? algorithmState.lastTopStackIndex
@@ -422,7 +418,7 @@
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
             childViewState.location = StackViewState.LOCATION_UNKNOWN;
-            int childHeight = getMaxAllowedChildHeight(child, ambientState);
+            int childHeight = getMaxAllowedChildHeight(child);
             int minHeight = child.getMinHeight();
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
@@ -504,11 +500,6 @@
             currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
             yPositionInScrollView = yPositionInScrollViewAfterElement;
 
-            if (ambientState.isShadeExpanded() && topHeadsUpEntry != null
-                    && child != topHeadsUpEntry) {
-                childViewState.yTranslation += topHeadsUpEntry.getHeadsUpHeight() -
-                        mFirstChildMinHeight;
-            }
             childViewState.yTranslation += ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
         }
@@ -533,10 +524,6 @@
             StackViewState childState = resultState.getViewStateForView(row);
             boolean isTopEntry = topHeadsUpEntry == row;
             if (mIsExpanded) {
-                if (isTopEntry) {
-                    childState.height += row.getHeadsUpHeight() - mFirstChildMinHeight;
-                }
-                childState.height = Math.max(childState.height, row.getHeadsUpHeight());
                 // Ensure that the heads up is always visible even when scrolled off from the bottom
                 float bottomPosition = ambientState.getMaxHeadsUpTranslation() - childState.height;
                 childState.yTranslation = Math.min(childState.yTranslation,
@@ -545,12 +532,12 @@
             if (row.isPinned()) {
                 childState.yTranslation = Math.max(childState.yTranslation,
                         mNotificationsTopPadding);
-                childState.height = Math.max(row.getHeadsUpHeight(), childState.height);
+                childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
                 if (!isTopEntry) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
                     // the top most z-position
                     StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
-                    childState.height = row.getHeadsUpHeight();
+                    childState.height = row.getIntrinsicHeight();
                     childState.yTranslation = topState.yTranslation + topState.height
                             - childState.height;
                 }
@@ -608,16 +595,8 @@
                 mFirstChildMinHeight - childHeight);
     }
 
-    private int getMaxAllowedChildHeight(View child, AmbientState ambientState) {
-        if (child instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-            if (ambientState == null && row.isHeadsUp()
-                    || ambientState != null && ambientState.getTopHeadsUpEntry() == child) {
-                int extraSize = row.getIntrinsicHeight() - row.getHeadsUpHeight();
-                return mFirstChildMinHeight + extraSize;
-            }
-            return row.getIntrinsicHeight();
-        } else if (child instanceof ExpandableView) {
+    private int getMaxAllowedChildHeight(View child) {
+        if (child instanceof ExpandableView) {
             ExpandableView expandableView = (ExpandableView) child;
             return expandableView.getIntrinsicHeight();
         }
@@ -744,7 +723,7 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
-            int childHeight = getMaxAllowedChildHeight(child, ambientState);
+            int childHeight = getMaxAllowedChildHeight(child);
             float yPositionInScrollViewAfterElement = yPositionInScrollView
                     + childHeight
                     + mPaddingBetweenElements;
@@ -865,13 +844,6 @@
                 // current height or the end value of the animation.
                 mFirstChildMaxHeight = StackStateAnimator.getFinalActualHeight(
                         mFirstChildWhileExpanding);
-                if (mFirstChildWhileExpanding instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row =
-                            (ExpandableNotificationRow) mFirstChildWhileExpanding;
-                    if (row.isHeadsUp()) {
-                        mFirstChildMaxHeight += mFirstChildMinHeight - row.getHeadsUpHeight();
-                    }
-                }
             } else {
                 updateFirstChildMaxSizeToMaxHeight();
             }
@@ -893,7 +865,7 @@
                                 int oldBottom) {
                             if (mFirstChildWhileExpanding != null) {
                                 mFirstChildMaxHeight = getMaxAllowedChildHeight(
-                                        mFirstChildWhileExpanding, null);
+                                        mFirstChildWhileExpanding);
                             } else {
                                 mFirstChildMaxHeight = 0;
                             }
@@ -901,7 +873,7 @@
                         }
                     });
         } else {
-            mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding, null);
+            mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
         }
     }