Follow up CL for heads up notification refactoring

Change-Id: Iaf286dfd30a86d3d7d6dd947d3c10ac9eb8c2b1e
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 92bd0df..e264021 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -783,7 +783,8 @@
     protected void applyColorsAndBackgrounds(StatusBarNotification sbn,
             NotificationData.Entry entry) {
 
-        if (entry.expanded.getId() != com.android.internal.R.id.status_bar_latest_event_content) {
+        if (entry.getContentView().getId()
+                != com.android.internal.R.id.status_bar_latest_event_content) {
             // Using custom RemoteViews
             if (entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
                     && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP) {
@@ -808,8 +809,9 @@
 
     public boolean isMediaNotification(NotificationData.Entry entry) {
         // TODO: confirm that there's a valid media key
-        return entry.expandedBig != null &&
-               entry.expandedBig.findViewById(com.android.internal.R.id.media_actions) != null;
+        return entry.getExpandedContentView() != null &&
+               entry.getExpandedContentView()
+                       .findViewById(com.android.internal.R.id.media_actions) != null;
     }
 
     // The gear button in the guts that links to the app's own notification settings
@@ -1133,9 +1135,9 @@
     }
 
     /**
-     * if the interrupting notification had a fullscreen intent, fire it now.
+     * If there is an active heads-up notification and it has a fullscreen intent, fire it now.
      */
-    public abstract void escalateHeadsUp();
+    public abstract void maybeEscalateHeadsUp();
 
     /**
      * Save the current "public" (locked and secure) state of the lockscreen.
@@ -1336,8 +1338,8 @@
         View publicViewLocal = null;
         if (publicNotification != null) {
             try {
-                publicViewLocal = publicNotification.contentView.apply(mContext, contentContainerPublic,
-                        mOnClickHandler);
+                publicViewLocal = publicNotification.contentView.apply(mContext,
+                        contentContainerPublic, mOnClickHandler);
 
                 if (publicViewLocal != null) {
                     publicViewLocal.setIsRootNamespace(true);
@@ -1444,9 +1446,7 @@
         entry.row = row;
         entry.row.setHeightRange(mRowMinHeight, maxHeight);
         entry.row.setOnActivatedListener(this);
-        entry.expanded = contentViewLocal;
-        entry.expandedPublic = publicViewLocal;
-        entry.setBigContentView(bigContentViewLocal);
+        entry.row.setExpandable(bigContentViewLocal != null);
 
         applyColorsAndBackgrounds(sbn, entry);
 
@@ -1535,12 +1535,13 @@
 
         // See if we have somewhere to put that remote input
         if (remoteInput != null) {
-            if (entry.expandedBig != null) {
-                inflateRemoteInput(entry.expandedBig, remoteInput, actions);
+            View bigContentView = entry.getExpandedContentView();
+            if (bigContentView != null) {
+                inflateRemoteInput(bigContentView, remoteInput, actions);
             }
-            View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
-            if (headsUpChild != null) {
-                inflateRemoteInput(headsUpChild, remoteInput, actions);
+            View headsUpContentView = entry.getHeadsUpContentView();
+            if (headsUpContentView != null) {
+                inflateRemoteInput(headsUpContentView, remoteInput, actions);
             }
         }
 
@@ -1882,15 +1883,14 @@
             logUpdate(entry, n);
         }
         boolean applyInPlace = shouldApplyInPlace(entry, n);
-        final boolean shouldInterrupt = shouldInterrupt(notification);
-        final boolean alertAgain = alertAgain(entry, n);
+        boolean shouldInterrupt = shouldInterrupt(notification);
+        boolean alertAgain = alertAgain(entry, n);
 
         entry.notification = notification;
         mGroupManager.onEntryUpdated(entry, entry.notification);
 
         boolean updateSuccessful = false;
         if (applyInPlace) {
-            // We can just reapply the notifications in place
             if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
             try {
                 if (entry.icon != null) {
@@ -1911,7 +1911,7 @@
                 updateSuccessful = true;
             }
             catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
+                // It failed to apply cleanly.
                 Log.w(TAG, "Couldn't reapply views for package " + n.contentView.getPackage(), e);
             }
         }
@@ -1935,11 +1935,12 @@
         // swipe-dismissable)
         updateNotificationVetoButton(entry.row, notification);
 
-        // Is this for you?
-        boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
-        if (DEBUG) Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+        if (DEBUG) {
+            // Is this for you?
+            boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
+            Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
+        }
 
-        // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
     }
 
@@ -1950,7 +1951,7 @@
         StatusBarNotification oldNotification = oldEntry.notification;
         Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
                 + " ongoing=" + oldNotification.isOngoing()
-                + " expanded=" + oldEntry.expanded
+                + " expanded=" + oldEntry.getContentView()
                 + " contentView=" + oldNotification.getNotification().contentView
                 + " bigContentView=" + oldNotification.getNotification().bigContentView
                 + " publicView=" + oldNotification.getNotification().publicVersion
@@ -1963,7 +1964,8 @@
     }
 
     /**
-     * @return whether we can just reapply the RemoteViews in place when it is updated
+     * @return whether we can just reapply the RemoteViews from a notification in-place when it is
+     * updated
      */
     private boolean shouldApplyInPlace(Entry entry, Notification n) {
         StatusBarNotification oldNotification = entry.notification;
@@ -1981,15 +1983,15 @@
         final Notification publicNotification = n.publicVersion;
         final RemoteViews publicContentView = publicNotification != null
                 ? publicNotification.contentView : null;
-        boolean contentsUnchanged = entry.expanded != null
+        boolean contentsUnchanged = entry.getContentView() != null
                 && contentView.getPackage() != null
                 && oldContentView.getPackage() != null
                 && oldContentView.getPackage().equals(contentView.getPackage())
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         // large view may be null
         boolean bigContentsUnchanged =
-                (entry.getBigContentView() == null && bigContentView == null)
-                || ((entry.getBigContentView() != null && bigContentView != null)
+                (entry.getExpandedContentView() == null && bigContentView == null)
+                || ((entry.getExpandedContentView() != null && bigContentView != null)
                     && bigContentView.getPackage() != null
                     && oldBigContentView.getPackage() != null
                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
@@ -2021,12 +2023,12 @@
                 : null;
 
         // Reapply the RemoteViews
-        contentView.reapply(mContext, entry.expanded, mOnClickHandler);
-        if (bigContentView != null && entry.getBigContentView() != null) {
-            bigContentView.reapply(mContext, entry.getBigContentView(),
+        contentView.reapply(mContext, entry.getContentView(), mOnClickHandler);
+        if (bigContentView != null && entry.getExpandedContentView() != null) {
+            bigContentView.reapply(mContext, entry.getExpandedContentView(),
                     mOnClickHandler);
         }
-        View headsUpChild = entry.row.getPrivateLayout().getHeadsUpChild();
+        View headsUpChild = entry.getHeadsUpContentView();
         if (headsUpContentView != null && headsUpChild != null) {
             headsUpContentView.reapply(mContext, headsUpChild, mOnClickHandler);
         }
@@ -2049,7 +2051,7 @@
     }
 
     protected void notifyHeadsUpScreenOff() {
-        escalateHeadsUp();
+        maybeEscalateHeadsUp();
     }
 
     private boolean alertAgain(Entry oldEntry, Notification newNotification) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index cb8217e..9ef495d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -84,7 +84,6 @@
     private ExpansionLogger mLogger;
     private String mLoggingKey;
     private boolean mWasReset;
-
     private NotificationGuts mGuts;
     private StatusBarNotification mStatusBarNotification;
     private boolean mIsHeadsUp;
@@ -102,6 +101,7 @@
     private ViewStub mGutsStub;
     private boolean mHasExpandAction;
     private boolean mIsSystemChildExpanded;
+    private boolean mIsPinned;
     private OnClickListener mExpandClickListener = new OnClickListener() {
         @Override
         public void onClick(View v) {
@@ -109,7 +109,6 @@
                     !mChildrenExpanded);
         }
     };
-    private boolean mInShade;
 
     public NotificationContentView getPrivateLayout() {
         return mPrivateLayout;
@@ -284,12 +283,18 @@
         return realActualHeight;
     }
 
-    public void setInShade(boolean inShade) {
-        mInShade = inShade;
+    /**
+     * Set this notification to be pinned to the top if {@link #isHeadsUp()} is true. By doing this
+     * the notification will be rendered on top of the screen.
+     *
+     * @param pinned whether it is pinned
+     */
+    public void setPinned(boolean pinned) {
+        mIsPinned = pinned;
     }
 
-    public boolean isInShade() {
-        return mInShade;
+    public boolean isPinned() {
+        return mIsPinned;
     }
 
     public int getHeadsUpHeight() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 1c53655..110b14c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -32,37 +32,34 @@
 import com.android.systemui.R;
 
 /**
- * A frame layout containing the actual payload of the notification, including the contracted and
- * expanded layout. This class is responsible for clipping the content and and switching between the
- * expanded and contracted view depending on its clipped size.
+ * A frame layout containing the actual payload of the notification, including the contracted,
+ * expanded and heads up layout. This class is responsible for clipping the content and and
+ * switching between the expanded, contracted and the heads up view depending on its clipped size.
  */
 public class NotificationContentView extends FrameLayout {
 
     private static final long ANIMATION_DURATION_LENGTH = 170;
-    private static final int CONTRACTED = 1;
-    private static final int EXPANDED = 2;
-    private static final int HEADSUP = 3;
+    private static final int VISIBLE_TYPE_CONTRACTED = 0;
+    private static final int VISIBLE_TYPE_EXPANDED = 1;
+    private static final int VISIBLE_TYPE_HEADSUP = 2;
 
     private final Rect mClipBounds = new Rect();
+    private final int mSmallHeight;
+    private final int mHeadsUpHeight;
+    private final Interpolator mLinearInterpolator = new LinearInterpolator();
 
     private View mContractedChild;
     private View mExpandedChild;
     private View mHeadsUpChild;
 
     private NotificationViewWrapper mContractedWrapper;
-
-    private final int mSmallHeight;
-    private final int mHeadsUpHeight;
     private int mClipTopAmount;
-
     private int mContentHeight;
-
-    private final Interpolator mLinearInterpolator = new LinearInterpolator();
-    private int mVisibleView = CONTRACTED;
-
+    private int mVisibleType = VISIBLE_TYPE_CONTRACTED;
     private boolean mDark;
     private final Paint mFadePaint = new Paint();
     private boolean mAnimate;
+    private boolean mIsHeadsUp;
     private ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
             = new ViewTreeObserver.OnPreDrawListener() {
         @Override
@@ -72,7 +69,6 @@
             return true;
         }
     };
-    private boolean mIsHeadsUp;
 
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -105,9 +101,9 @@
                 // An actual height is set
                 size = Math.min(maxSize, layoutParams.height);
             }
-            int spec = size == Integer.MAX_VALUE ?
-                    MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) :
-                    MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
+            int spec = size == Integer.MAX_VALUE
+                    ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+                    : MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
             mExpandedChild.measure(widthMeasureSpec, spec);
             maxChildHeight = Math.max(maxChildHeight, mExpandedChild.getMeasuredHeight());
         }
@@ -153,7 +149,7 @@
         mContractedChild = null;
         mExpandedChild = null;
         mHeadsUpChild = null;
-        mVisibleView = CONTRACTED;
+        mVisibleType = VISIBLE_TYPE_CONTRACTED;
         if (resetActualHeight) {
             mContentHeight = mSmallHeight;
         }
@@ -263,30 +259,32 @@
         if (mContractedChild == null) {
             return;
         }
-        int visibleView = calculateVisibleView();
-        if (visibleView != mVisibleView || force) {
-            if (animate && mExpandedChild != null) {
-                runSwitchAnimation(visibleView);
+        int visibleType = calculateVisibleType();
+        if (visibleType != mVisibleType || force) {
+            if (animate && (visibleType == VISIBLE_TYPE_EXPANDED && mExpandedChild != null)
+                    || (visibleType == VISIBLE_TYPE_HEADSUP && mHeadsUpChild != null)
+                    || visibleType == VISIBLE_TYPE_CONTRACTED) {
+                runSwitchAnimation(visibleType);
             } else {
-                updateViewVisibilities(visibleView);
+                updateViewVisibilities(visibleType);
             }
-            mVisibleView = visibleView;
+            mVisibleType = visibleType;
         }
     }
 
-    private void updateViewVisibilities(int visibleView) {
-        boolean contractedVisible = visibleView == CONTRACTED;
+    private void updateViewVisibilities(int visibleType) {
+        boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
         mContractedChild.setVisibility(contractedVisible ? View.VISIBLE : View.INVISIBLE);
         mContractedChild.setAlpha(contractedVisible ? 1f : 0f);
         mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
         if (mExpandedChild != null) {
-            boolean expandedVisible = visibleView == EXPANDED;
+            boolean expandedVisible = visibleType == VISIBLE_TYPE_EXPANDED;
             mExpandedChild.setVisibility(expandedVisible ? View.VISIBLE : View.INVISIBLE);
             mExpandedChild.setAlpha(expandedVisible ? 1f : 0f);
             mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
         }
         if (mHeadsUpChild != null) {
-            boolean headsUpVisible = visibleView == HEADSUP;
+            boolean headsUpVisible = visibleType == VISIBLE_TYPE_HEADSUP;
             mHeadsUpChild.setVisibility(headsUpVisible ? View.VISIBLE : View.INVISIBLE);
             mHeadsUpChild.setAlpha(headsUpVisible ? 1f : 0f);
             mHeadsUpChild.setLayerType(LAYER_TYPE_NONE, null);
@@ -294,9 +292,9 @@
         setLayerType(LAYER_TYPE_NONE, null);
     }
 
-    private void runSwitchAnimation(int visibleView) {
-        View shownView = getViewFromFlag(visibleView);
-        View hiddenView = getViewFromFlag(mVisibleView);
+    private void runSwitchAnimation(int visibleType) {
+        View shownView = getViewForVisibleType(visibleType);
+        View hiddenView = getViewForVisibleType(mVisibleType);
         shownView.setVisibility(View.VISIBLE);
         hiddenView.setVisibility(View.VISIBLE);
         shownView.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
@@ -314,34 +312,42 @@
                 .withEndAction(new Runnable() {
                     @Override
                     public void run() {
-                        updateViewVisibilities(mVisibleView);
+                        updateViewVisibilities(mVisibleType);
                     }
                 });
     }
 
-    private View getViewFromFlag(int visibleView) {
-        switch (visibleView) {
-            case EXPANDED:
+    /**
+     * @param visibleType one of the static enum types in this view
+     * @return the corresponding view according to the given visible type
+     */
+    private View getViewForVisibleType(int visibleType) {
+        switch (visibleType) {
+            case VISIBLE_TYPE_EXPANDED:
                 return mExpandedChild;
-            case HEADSUP:
+            case VISIBLE_TYPE_HEADSUP:
                 return mHeadsUpChild;
+            default:
+                return mContractedChild;
         }
-        return mContractedChild;
     }
 
-    private int calculateVisibleView() {
+    /**
+     * @return one of the static enum types in this view, calculated form the current state
+     */
+    private int calculateVisibleType() {
         boolean noExpandedChild = mExpandedChild == null;
         if (mIsHeadsUp && mHeadsUpChild != null) {
             if (mContentHeight <= mHeadsUpChild.getHeight() || noExpandedChild) {
-                return HEADSUP;
+                return VISIBLE_TYPE_HEADSUP;
             } else {
-                return EXPANDED;
+                return VISIBLE_TYPE_EXPANDED;
             }
         } else {
             if (mContentHeight <= mSmallHeight || noExpandedChild) {
-                return CONTRACTED;
+                return VISIBLE_TYPE_CONTRACTED;
             } else {
-                return EXPANDED;
+                return VISIBLE_TYPE_EXPANDED;
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 429889d..2a8b4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -45,9 +45,6 @@
         public StatusBarNotification notification;
         public StatusBarIconView icon;
         public ExpandableNotificationRow row; // the outer expanded view
-        public View expanded; // the inflated RemoteViews
-        public View expandedPublic; // for insecure lockscreens
-        public View expandedBig;
         private boolean interruption;
         public boolean autoRedacted; // whether the redacted notification was generated by us
         public boolean legacy; // whether the notification has a legacy, dark background
@@ -58,14 +55,6 @@
             this.notification = n;
             this.icon = ic;
         }
-        public void setBigContentView(View bigContentView) {
-            this.expandedBig = bigContentView;
-            row.setExpandable(bigContentView != null);
-        }
-        public View getBigContentView() {
-            return expandedBig;
-        }
-        public View getPublicContentView() { return expandedPublic; }
 
         public void setInterruption() {
             interruption = true;
@@ -81,15 +70,28 @@
         public void reset() {
             // NOTE: Icon needs to be preserved for now.
             // We should fix this at some point.
-            expanded = null;
-            expandedPublic = null;
-            expandedBig = null;
             autoRedacted = false;
             legacy = false;
             if (row != null) {
                 row.reset();
             }
         }
+
+        public View getContentView() {
+            return row.getPrivateLayout().getContractedChild();
+        }
+
+        public View getExpandedContentView() {
+            return row.getPrivateLayout().getExpandedChild();
+        }
+
+        public View getHeadsUpContentView() {
+            return row.getPrivateLayout().getHeadsUpChild();
+        }
+
+        public View getPublicContentView() {
+            return row.getPublicLayout().getContractedChild();
+        }
     }
 
     private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
@@ -258,7 +260,7 @@
      */
     public boolean hasActiveClearableNotifications() {
         for (Entry e : mSortedAndFiltered) {
-            if (e.expanded != null) { // the view successfully inflated
+            if (e.getContentView() != null) { // the view successfully inflated
                 if (e.notification.isClearable()) {
                     return true;
                 }
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 3997807..fe7bc97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -27,7 +27,7 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
- * A Helper class to handle touches on the heads-up views
+ * A helper class to handle touches on the heads-up views.
  */
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
@@ -37,19 +37,30 @@
     private float mTouchSlop;
     private float mInitialTouchX;
     private float mInitialTouchY;
-    private boolean mMotionOnHeadsUpView;
+    private boolean mTouchingHeadsUpView;
     private boolean mTrackingHeadsUp;
     private boolean mCollapseSnoozes;
     private NotificationPanelView mPanel;
     private ExpandableNotificationRow mPickedChild;
 
+    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
+            NotificationStackScrollLayout stackScroller,
+            NotificationPanelView notificationPanelView) {
+        mHeadsUpManager = headsUpManager;
+        mStackScroller = stackScroller;
+        mPanel = notificationPanelView;
+        Context context = stackScroller.getContext();
+        final ViewConfiguration configuration = ViewConfiguration.get(context);
+        mTouchSlop = configuration.getScaledTouchSlop();
+    }
+
     public boolean isTrackingHeadsUp() {
         return mTrackingHeadsUp;
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (!mMotionOnHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
+        if (!mTouchingHeadsUpView && event.getActionMasked() != MotionEvent.ACTION_DOWN) {
             return false;
         }
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
@@ -65,10 +76,10 @@
                 mInitialTouchX = x;
                 setTrackingHeadsUp(false);
                 ExpandableView child = mStackScroller.getChildAtPosition(x, y);
-                mMotionOnHeadsUpView = false;
+                mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
-                    mMotionOnHeadsUpView = mPickedChild.isHeadsUp() && !mPickedChild.isInShade();
+                    mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
@@ -97,7 +108,8 @@
 
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
-                if (mPickedChild != null && mMotionOnHeadsUpView) {
+                if (mPickedChild != null && mTouchingHeadsUpView) {
+                    // We may swallow this click if the heads up just came in.
                     if (mHeadsUpManager.shouldSwallowClick(
                             mPickedChild.getStatusBarNotification().getKey())) {
                         endMotion();
@@ -141,20 +153,6 @@
     private void endMotion() {
         mTrackingPointer = -1;
         mPickedChild = null;
-        mMotionOnHeadsUpView = false;
-    }
-
-    public ExpandableView getPickedChild() {
-        return mPickedChild;
-    }
-
-    public void bind(HeadsUpManager headsUpManager, NotificationStackScrollLayout stackScroller,
-            NotificationPanelView notificationPanelView) {
-        mHeadsUpManager = headsUpManager;
-        mStackScroller = stackScroller;
-        mPanel = notificationPanelView;
-        Context context = stackScroller.getContext();
-        final ViewConfiguration configuration = ViewConfiguration.get(context);
-        mTouchSlop = configuration.getScaledTouchSlop();
+        mTouchingHeadsUpView = false;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b87c25b..8914fb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -182,11 +182,11 @@
 
     private float mKeyguardStatusBarAnimateAlpha = 1f;
     private int mOldLayoutDirection;
-    private HeadsUpTouchHelper mHeadsUpTouchHelper = new HeadsUpTouchHelper();
-    private boolean mPinnedHeadsUpExist;
-    private boolean mExpansionIsFromHeadsUp;
-    private int mBottomBarHeight;
+    private HeadsUpTouchHelper mHeadsUpTouchHelper;
+    private boolean mIsExpansionFromHeadsUp;
+    private int mNavigationBarBottomHeight;
     private boolean mExpandingFromHeadsUp;
+    private boolean mCollapsedOnDown;
     private int mPositionMinSideMargin;
     private int mLastOrientation = -1;
 
@@ -534,17 +534,16 @@
     }
 
     @Override
-    public boolean
-    onInterceptTouchEvent(MotionEvent event) {
+    public boolean onInterceptTouchEvent(MotionEvent event) {
         if (mBlockTouches) {
             return false;
         }
         initDownStates(event);
         if (mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
-            mExpansionIsFromHeadsUp = true;
+            mIsExpansionFromHeadsUp = true;
             return true;
         }
-        if (!isShadeCollapsed() && onQsIntercept(event)) {
+        if (!isFullyCollapsed() && onQsIntercept(event)) {
             return true;
         }
         return super.onInterceptTouchEvent(event);
@@ -641,6 +640,7 @@
             mOnlyAffordanceInThisMotion = false;
             mQsTouchAboveFalsingThreshold = mQsFullyExpanded;
             mDozingOnDown = isDozing();
+            mCollapsedOnDown = isFullyCollapsed();
         }
     }
 
@@ -695,7 +695,7 @@
             return true;
         }
         mHeadsUpTouchHelper.onTouchEvent(event);
-        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQSTouch(event)) {
+        if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) {
             return true;
         }
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) {
@@ -705,7 +705,7 @@
         return true;
     }
 
-    private boolean handleQSTouch(MotionEvent event) {
+    private boolean handleQsTouch(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
                 && mStatusBar.getBarState() != StatusBarState.KEYGUARD && !mQsExpanded
                 && mQsExpansionEnabled) {
@@ -718,7 +718,7 @@
             mInitialTouchY = event.getX();
             mInitialTouchX = event.getY();
         }
-        if (!isShadeCollapsed()) {
+        if (!isFullyCollapsed()) {
             handleQsDown(event);
         }
         if (!mQsExpandImmediate && mQsTracking) {
@@ -731,7 +731,7 @@
                 || event.getActionMasked() == MotionEvent.ACTION_UP) {
             mConflictingQsExpansionGesture = false;
         }
-        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isShadeCollapsed()
+        if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()
                 && mQsExpansionEnabled) {
             mTwoFingerQsExpandPossible = true;
         }
@@ -1191,8 +1191,8 @@
         updateEmptyShadeView();
         mQsNavbarScrim.setVisibility(mStatusBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
-                ? View.VISIBLE
-                : View.INVISIBLE);
+                        ? View.VISIBLE
+                        : View.INVISIBLE);
         if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
             mKeyguardUserSwitcher.hideIfNotSimple(true /* animate */);
         }
@@ -1386,7 +1386,7 @@
      * @return Whether we should intercept a gesture to open Quick Settings.
      */
     private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
-        if (!mQsExpansionEnabled) {
+        if (!mQsExpansionEnabled || mCollapsedOnDown) {
             return false;
         }
         View header = mKeyguardShowing ? mKeyguardStatusBar : mHeader;
@@ -1461,8 +1461,8 @@
         updateHeader();
         updateUnlockIcon();
         updateNotificationTranslucency();
-        mHeadsUpManager.setIsExpanded(!isShadeCollapsed());
-        mNotificationStackScroller.setShadeExpanded(!isShadeCollapsed());
+        mHeadsUpManager.setIsExpanded(!isFullyCollapsed());
+        mNotificationStackScroller.setShadeExpanded(!isFullyCollapsed());
         if (DEBUG) {
             invalidate();
         }
@@ -1535,21 +1535,19 @@
         float alpha;
         if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = 1f;
-            if (mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
-                mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
-            }
         } else {
             alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
                     / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
                     - mNotificationStackScroller.getCollapseSecondCardPadding());
             alpha = Math.max(0, Math.min(alpha, 1));
             alpha = (float) Math.pow(alpha, 0.75);
-            if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
-                mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
-            } else if (alpha == 1f
-                    && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
-                mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
-            }
+        }
+
+        if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
+            mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
+        } else if (alpha == 1f
+                && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
+            mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
         }
         mNotificationStackScroller.setAlpha(alpha);
     }
@@ -1615,7 +1613,7 @@
         }
         float stackTranslation = mNotificationStackScroller.getStackTranslation();
         float translation = stackTranslation / HEADER_RUBBERBAND_FACTOR;
-        if (mHeadsUpManager.hasPinnedHeadsUp() || mExpansionIsFromHeadsUp) {
+        if (mHeadsUpManager.hasPinnedHeadsUp() || mIsExpansionFromHeadsUp) {
             translation = mNotificationStackScroller.getTopPadding() + stackTranslation
                     - mNotificationTopPadding - mQsMinExpansionHeight;
         }
@@ -1683,16 +1681,16 @@
         mHeadsUpManager.onExpandingFinished();
         mIsExpanding = false;
         mScrollYOverride = -1;
-        if (isShadeCollapsed()) {
+        if (isFullyCollapsed()) {
             setListening(false);
         } else {
             setListening(true);
         }
         mQsExpandImmediate = false;
         mTwoFingerQsExpandPossible = false;
-        mExpansionIsFromHeadsUp = false;
-        mNotificationStackScroller.setTrackingHeadsUp(mHeadsUpTouchHelper.isTrackingHeadsUp());
-        mExpandingFromHeadsUp = mHeadsUpTouchHelper.isTrackingHeadsUp();
+        mIsExpansionFromHeadsUp = false;
+        mNotificationStackScroller.setTrackingHeadsUp(false);
+        mExpandingFromHeadsUp = false;
     }
 
     private void setListening(boolean listening) {
@@ -1793,13 +1791,13 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mBottomBarHeight = insets.getSystemWindowInsetBottom();
+        mNavigationBarBottomHeight = insets.getSystemWindowInsetBottom();
         updateMaxHeadsUpTranslation();
         return insets;
     }
 
     private void updateMaxHeadsUpTranslation() {
-        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mBottomBarHeight);
+        mNotificationStackScroller.setHeadsUpBoundaries(getHeight(), mNavigationBarBottomHeight);
     }
 
     @Override
@@ -2160,48 +2158,43 @@
     }
 
     @Override
-    public void OnPinnedHeadsUpExistChanged(final boolean exist, boolean changeImmediatly) {
-        if (exist != mPinnedHeadsUpExist) {
-            mPinnedHeadsUpExist = exist;
-            if (exist) {
-                mHeadsUpExistenceChangedRunnable.run();
-                updateNotificationTranslucency();
-            } else {
-                mNotificationStackScroller.performOnAnimationFinished(
-                        mHeadsUpExistenceChangedRunnable);
-            }
+    public void onPinnedModeChanged(final boolean inPinnedMode) {
+        if (inPinnedMode) {
+            mHeadsUpExistenceChangedRunnable.run();
+            updateNotificationTranslucency();
+        } else {
+            mNotificationStackScroller.runAfterAnimationFinished(
+                    mHeadsUpExistenceChangedRunnable);
         }
     }
 
     @Override
-    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
-        if (isHeadsUp) {
-            mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
-        }
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        mNotificationStackScroller.generateHeadsUpAnimation(headsUp, true);
     }
 
     @Override
-    public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    }
+
+    @Override
+    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
         mNotificationStackScroller.generateHeadsUpAnimation(entry.row, isHeadsUp);
     }
 
     @Override
-    protected boolean isShadeCollapsed() {
-        return mExpandedHeight == 0;
-    }
-
-    @Override
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
-        mHeadsUpTouchHelper.bind(headsUpManager, mNotificationStackScroller, this);
+        mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
+                this);
     }
 
     public void setTrackingHeadsUp(boolean tracking) {
         if (tracking) {
-            // otherwise we update the state when the expansion is finished
             mNotificationStackScroller.setTrackingHeadsUp(true);
             mExpandingFromHeadsUp = true;
         }
+        // otherwise we update the state when the expansion is finished
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 4452dd7..85f312c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -46,13 +46,13 @@
 public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
-    protected HeadsUpManager mHeadsUpManager;
-
     private final void logf(String fmt, Object... args) {
         Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
     }
 
     protected PhoneStatusBar mStatusBar;
+    protected HeadsUpManager mHeadsUpManager;
+
     private float mPeekHeight;
     private float mHintDistance;
     private int mEdgeTapAreaWidth;
@@ -242,15 +242,15 @@
         final float y = event.getY(pointerIndex);
 
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mGestureWaitForTouchSlop = isShadeCollapsed() || hasConflictingGestures();
-            mIgnoreXTouchSlop = isShadeCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
+            mGestureWaitForTouchSlop = isFullyCollapsed() || hasConflictingGestures();
+            mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
         }
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
                 mJustPeeked = false;
-                mPanelClosedOnDown = isShadeCollapsed();
+                mPanelClosedOnDown = isFullyCollapsed();
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mMotionAborted = false;
@@ -268,7 +268,7 @@
                             || mPeekPending || mPeekAnimator != null;
                     onTrackingStarted();
                 }
-                if (isShadeCollapsed()) {
+                if (isFullyCollapsed()) {
                     schedulePeek();
                 }
                 break;
@@ -472,7 +472,7 @@
                 mTouchSlopExceeded = false;
                 mJustPeeked = false;
                 mMotionAborted = false;
-                mPanelClosedOnDown = isShadeCollapsed();
+                mPanelClosedOnDown = isFullyCollapsed();
                 mHasLayoutedSinceDown = false;
                 mUpdateFlingOnLayout = false;
                 mTouchAboveFalsingThreshold = false;
@@ -707,7 +707,7 @@
         // If the user isn't actively poking us, let's update the height
         if ((!mTracking || isTrackingBlocked())
                 && mHeightAnimator == null
-                && !isShadeCollapsed()
+                && !isFullyCollapsed()
                 && currentMaxPanelHeight != mExpandedHeight
                 && !mPeekPending
                 && mPeekAnimator == null
@@ -1057,8 +1057,6 @@
      */
     protected abstract int getClearAllHeight();
 
-    protected abstract boolean isShadeCollapsed();
-
     public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
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 bf85ed5..b1d48f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1152,11 +1152,11 @@
 
     @Override
     public void removeNotification(String key, RankingMap ranking) {
-        boolean defferRemoval = false;
+        boolean deferRemoval = false;
         if (mHeadsUpManager.isHeadsUp(key)) {
-            defferRemoval = !mHeadsUpManager.removeNotification(key);
+            deferRemoval = !mHeadsUpManager.removeNotification(key);
         }
-        if (defferRemoval) {
+        if (deferRemoval) {
             mLatestRankingMap = ranking;
             mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
             return;
@@ -1838,8 +1838,8 @@
     }
 
     @Override
-    public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) {
-        if (exist) {
+    public void onPinnedModeChanged(boolean inPinnedMode) {
+        if (inPinnedMode) {
             mStatusBarWindowManager.setHeadsUpShowing(true);
         } else {
             Runnable endRunnable = new Runnable() {
@@ -1850,20 +1850,24 @@
                     }
                 }
             };
-            if (changeImmediatly) {
+            if (!mNotificationPanel.isFullyCollapsed()) {
                 endRunnable.run();
             } else {
-                mStackScroller.performOnAnimationFinished(endRunnable);
+                mStackScroller.runAfterAnimationFinished(endRunnable);
             }
         }
     }
 
     @Override
-    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
     }
 
     @Override
-    public void OnHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+    }
+
+    @Override
+    public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
         if (!isHeadsUp && mHeadsUpEntriesToRemoveOnSwitch.contains(entry)) {
             removeNotification(entry.key, mLatestRankingMap);
             mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
@@ -1880,10 +1884,11 @@
             boolean alertAgain) {
         final boolean wasHeadsUp = isHeadsUp(key);
         if (wasHeadsUp) {
-            mHeadsUpManager.updateNotification(entry, alertAgain);
             if (!shouldInterrupt) {
                 // We don't want this to be interrupting anymore, lets remove it
                 mHeadsUpManager.removeNotification(key);
+            } else {
+                mHeadsUpManager.updateNotification(entry, alertAgain);
             }
         } else if (shouldInterrupt && alertAgain) {
             // This notification was updated to be a heads-up, show it!
@@ -1929,7 +1934,7 @@
     }
 
     @Override
-    public void escalateHeadsUp() {
+    public void maybeEscalateHeadsUp() {
         TreeSet<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getSortedEntries();
         for (HeadsUpManager.HeadsUpEntry entry : entries) {
             final StatusBarNotification sbn = entry.entry.notification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e701783..ae98e76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -80,7 +80,7 @@
     private float mCurrentInFrontAlpha;
     private float mCurrentBehindAlpha;
     private float mCurrentHeadsUpAlpha = 1;
-    private int mAmountOfPinnedHeadsUps;
+    private int mPinnedHeadsUpCount;
     private float mTopHeadsUpDragAmount;
     private View mDraggedHeadsUpView;
 
@@ -347,25 +347,27 @@
     }
 
     @Override
-    public void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly) {
+    public void onPinnedModeChanged(boolean inPinnedMode) {
     }
 
     @Override
-    public void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp) {
-        if (isHeadsUp) {
-            mAmountOfPinnedHeadsUps++;
-        } else {
-            mAmountOfPinnedHeadsUps--;
-            if (headsUp == mDraggedHeadsUpView) {
-                mDraggedHeadsUpView = null;
-                mTopHeadsUpDragAmount = 0.0f;
-            }
+    public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
+        mPinnedHeadsUpCount++;
+        updateHeadsUpScrim(true);
+    }
+
+    @Override
+    public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
+        mPinnedHeadsUpCount--;
+        if (headsUp == mDraggedHeadsUpView) {
+            mDraggedHeadsUpView = null;
+            mTopHeadsUpDragAmount = 0.0f;
         }
         updateHeadsUpScrim(true);
     }
 
     @Override
-    public void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
+    public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
     }
 
     private void updateHeadsUpScrim(boolean animate) {
@@ -374,12 +376,10 @@
                 TAG_KEY_ANIM);
         float animEndValue = -1;
         if (previousAnimator != null) {
-            if ((animate || alpha == mCurrentHeadsUpAlpha)) {
-                // lets cancel any running animators
+            if (animate || alpha == mCurrentHeadsUpAlpha) {
                 previousAnimator.cancel();
             }
-            animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
-                    TAG_HUN_START_ALPHA);
+            animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_START_ALPHA);
         }
         if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) {
             if (animate) {
@@ -390,7 +390,7 @@
                 if (previousAnimator != null) {
                     float previousStartValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
                             TAG_HUN_START_ALPHA);
-                   float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
+                    float previousEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim,
                            TAG_HUN_END_ALPHA);
                     // we need to increase all animation keyframes of the previous animator by the
                     // relative change to the end value
@@ -410,6 +410,13 @@
         }
     }
 
+    /**
+     * Set the amount the current top heads up view is dragged. The range is from 0 to 1 and 0 means
+     * the heads up is in its resting space and 1 means it's fully dragged out.
+     *
+     * @param draggedHeadsUpView the dragged view
+     * @param topHeadsUpDragAmount how far is it dragged
+     */
     public void setTopHeadsUpDragAmount(View draggedHeadsUpView, float topHeadsUpDragAmount) {
         mTopHeadsUpDragAmount = topHeadsUpDragAmount;
         mDraggedHeadsUpView = draggedHeadsUpView;
@@ -417,9 +424,9 @@
     }
 
     private float calculateHeadsUpAlpha() {
-        if (mAmountOfPinnedHeadsUps >= 2) {
+        if (mPinnedHeadsUpCount >= 2) {
             return 1.0f;
-        } else if (mAmountOfPinnedHeadsUps == 0) {
+        } else if (mPinnedHeadsUpCount == 0) {
             return 0.0f;
         } else {
             return 1.0f - mTopHeadsUpDragAmount;
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 b4e4773..8f83daa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -35,11 +35,16 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Stack;
 import java.util.TreeSet;
 
+/**
+ * A manager which handles heads up notifications which is a special mode where
+ * they simply peek from the top of the screen.
+ */
 public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
     private static final String TAG = "HeadsUpManager";
     private static final boolean DEBUG = false;
@@ -48,7 +53,7 @@
     private final int mHeadsUpNotificationDecay;
     private final int mMinimumDisplayTime;
 
-    private final int mTouchSensitivityDelay;
+    private final int mTouchAcceptanceDelay;
     private final ArrayMap<String, Long> mSnoozedPackages;
     private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
     private final int mDefaultSnoozeLengthMs;
@@ -67,13 +72,12 @@
 
         @Override
         public boolean release(HeadsUpEntry instance) {
-            instance.removeAutoCancelCallbacks();
+            instance.reset();
             mPoolObjects.push(instance);
             return true;
         }
     };
 
-
     private PhoneStatusBar mBar;
     private int mSnoozeLengthMs;
     private ContentObserver mSettingsObserver;
@@ -86,13 +90,12 @@
     private boolean mTrackingHeadsUp;
     private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
     private boolean mIsExpanded;
-    private boolean mHasPinnedHeadsUp;
+    private boolean mHasPinnedNotification;
     private int[] mTmpTwoArray = new int[2];
 
     public HeadsUpManager(final Context context, ViewTreeObserver observer) {
         Resources resources = context.getResources();
-        mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
-        if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
+        mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
         mSnoozedPackages = new ArrayMap<>();
         mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
         mSnoozeLengthMs = mDefaultSnoozeLengthMs;
@@ -116,7 +119,6 @@
         context.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
                 mSettingsObserver);
-        if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
         observer.addOnComputeInternalInsetsListener(this);
     }
 
@@ -154,7 +156,7 @@
         if (alert) {
             HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
             headsUpEntry.updateEntry();
-            setEntryToShade(headsUpEntry, mIsExpanded, false /* justAdded */, false);
+            setEntryPinned(headsUpEntry, !mIsExpanded /* isPinned */);
         }
     }
 
@@ -165,22 +167,23 @@
         headsUpEntry.setEntry(entry);
         mHeadsUpEntries.put(entry.key, headsUpEntry);
         entry.row.setHeadsUp(true);
-        setEntryToShade(headsUpEntry, mIsExpanded /* inShade */, true /* justAdded */, false);
+        setEntryPinned(headsUpEntry, !mIsExpanded /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
-            listener.OnHeadsUpStateChanged(entry, true);
+            listener.onHeadsUpStateChanged(entry, true);
         }
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
-    private void setEntryToShade(HeadsUpEntry headsUpEntry, boolean inShade, boolean justAdded,
-            boolean forceImmediate) {
+    private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
         ExpandableNotificationRow row = headsUpEntry.entry.row;
-        if (row.isInShade() != inShade || justAdded) {
-            row.setInShade(inShade);
-            if (!justAdded || !inShade) {
-                updatePinnedHeadsUpState(forceImmediate);
-                for (OnHeadsUpChangedListener listener : mListeners) {
-                    listener.OnHeadsUpPinnedChanged(row, !inShade);
+        if (row.isPinned() != isPinned) {
+            row.setPinned(isPinned);
+            updatePinnedMode();
+            for (OnHeadsUpChangedListener listener : mListeners) {
+                if (isPinned) {
+                    listener.onHeadsUpPinned(row);
+                } else {
+                    listener.onHeadsUpUnPinned(row);
                 }
             }
         }
@@ -189,24 +192,23 @@
     private void removeHeadsUpEntry(NotificationData.Entry entry) {
         HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
         mSortedEntries.remove(remove);
-        mEntryPool.release(remove);
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         entry.row.setHeadsUp(false);
-        setEntryToShade(remove, true /* inShade */, false /* justAdded */,
-                false /* forceImmediate */);
+        setEntryPinned(remove, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
-            listener.OnHeadsUpStateChanged(entry, false);
+            listener.onHeadsUpStateChanged(entry, false);
         }
+        mEntryPool.release(remove);
     }
 
-    private void updatePinnedHeadsUpState(boolean forceImmediate) {
-        boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal();
-        if (hasPinnedHeadsUp == mHasPinnedHeadsUp) {
+    private void updatePinnedMode() {
+        boolean hasPinnedNotification = hasPinnedNotificationInternal();
+        if (hasPinnedNotification == mHasPinnedNotification) {
             return;
         }
-        mHasPinnedHeadsUp = hasPinnedHeadsUp;
-        for (OnHeadsUpChangedListener listener :mListeners) {
-            listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate);
+        mHasPinnedNotification = hasPinnedNotification;
+        for (OnHeadsUpChangedListener listener : mListeners) {
+            listener.onPinnedModeChanged(hasPinnedNotification);
         }
     }
 
@@ -222,7 +224,7 @@
             releaseImmediately(key);
             return true;
         } else {
-            getHeadsUpEntry(key).hideAsSoonAsPossible();
+            getHeadsUpEntry(key).removeAsSoonAsPossible();
             return false;
         }
     }
@@ -245,14 +247,13 @@
         return mHeadsUpEntries.containsKey(key);
     }
 
-
     /**
      * Push any current Heads Up notification down into the shade.
      */
     public void releaseAllImmediately() {
         if (DEBUG) Log.v(TAG, "releaseAllImmediately");
-        HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet());
-        for (String key: keys) {
+        ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
+        for (String key : keys) {
             releaseImmediately(key);
         }
     }
@@ -280,7 +281,7 @@
     }
 
     public void snooze() {
-        for (String key: mHeadsUpEntries.keySet()) {
+        for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
             String packageName = entry.entry.notification.getPackageName();
             mSnoozedPackages.put(snoozeKey(packageName, mUser),
@@ -310,8 +311,11 @@
     }
 
     /**
+     * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
+     * that a user might have consciously clicked on it.
+     *
      * @param key the key of the touched notification
-     * @return whether the touch is valid and should not be discarded
+     * @return whether the touch is invalid and should be discarded
      */
     public boolean shouldSwallowClick(String key) {
         HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -322,14 +326,14 @@
     }
 
     public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        if (!mIsExpanded && mHasPinnedHeadsUp) {
+        if (!mIsExpanded && mHasPinnedNotification) {
             int minX = Integer.MAX_VALUE;
             int maxX = 0;
             int minY = Integer.MAX_VALUE;
             int maxY = 0;
-            for (HeadsUpEntry entry: mSortedEntries) {
+            for (HeadsUpEntry entry : mSortedEntries) {
                 ExpandableNotificationRow row = entry.entry.row;
-                if (!row.isInShade()) {
+                if (row.isPinned()) {
                     row.getLocationOnScreen(mTmpTwoArray);
                     minX = Math.min(minX, mTmpTwoArray[0]);
                     minY = Math.min(minY, 0);
@@ -349,7 +353,7 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("HeadsUpManager state:");
-        pw.print("  mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
+        pw.print("  mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
         pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
         pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
         pw.print("  mUser="); pw.println(mUser);
@@ -365,38 +369,32 @@
     }
 
     public boolean hasPinnedHeadsUp() {
-        return mHasPinnedHeadsUp;
+        return mHasPinnedNotification;
     }
 
-    private boolean hasPinnedHeadsUpInternal() {
-        for (String key: mHeadsUpEntries.keySet()) {
+    private boolean hasPinnedNotificationInternal() {
+        for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
-            if (!entry.entry.row.isInShade()) {
+            if (entry.entry.row.isPinned()) {
                 return true;
             }
         }
         return false;
     }
 
-    public void addSwipedOutKey(String key) {
+    /**
+     * Notifies that a notification was swiped out and will be removed.
+     *
+     * @param key the notification key
+     */
+    public void addSwipedOutNotification(String key) {
         mSwipedOutKeys.add(key);
     }
 
-    public float getHighestPinnedHeadsUp() {
-        float max = 0;
-        for (HeadsUpEntry entry: mSortedEntries) {
-            if (!entry.entry.row.isInShade()) {
-                max = Math.max(max, entry.entry.row.getActualHeight());
-            }
-        }
-        return max;
-    }
-
-    public void releaseAllToShade() {
-        for (String key: mHeadsUpEntries.keySet()) {
+    public void unpinAll() {
+        for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
-            setEntryToShade(entry, true /* toShade */, false /* justAdded */,
-                    true /* forceImmediate */);
+            setEntryPinned(entry, false /* isPinned */);
         }
     }
 
@@ -420,7 +418,7 @@
         if (isExpanded != mIsExpanded) {
             mIsExpanded = isExpanded;
             if (isExpanded) {
-                releaseAllToShade();
+                unpinAll();
             }
         }
     }
@@ -430,6 +428,12 @@
         return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
     }
 
+    /**
+     * Compare two entries and decide how they should be ranked.
+     *
+     * @return -1 if the first argument should be ranked higher than the second, 1 if the second
+     * one should be ranked higher and 0 if they are equal.
+     */
     public int compare(NotificationData.Entry a, NotificationData.Entry b) {
         HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
         HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
@@ -439,6 +443,11 @@
         return aEntry.compareTo(bEntry);
     }
 
+
+    /**
+     * This represents a notification and how long it is in a heads up mode. It also manages its
+     * lifecycle automatically when created.
+     */
     public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
         public NotificationData.Entry entry;
         public long postTime;
@@ -449,7 +458,7 @@
             this.entry = entry;
 
             // The actual post time will be just after the heads-up really slided in
-            postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
+            postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
             mRemoveHeadsUpRunnable = new Runnable() {
                 @Override
                 public void run() {
@@ -467,7 +476,7 @@
             long currentTime = mClock.currentTimeMillis();
             earliestRemovaltime = currentTime + mMinimumDisplayTime;
             postTime = Math.max(postTime, currentTime);
-            removeAutoCancelCallbacks();
+            removeAutoRemovalCallbacks();
             if (canEntryDecay()) {
                 long finishTime = postTime + mHeadsUpNotificationDecay;
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -487,7 +496,7 @@
                             : -1;
         }
 
-        public void removeAutoCancelCallbacks() {
+        public void removeAutoRemovalCallbacks() {
             mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
         }
 
@@ -495,11 +504,17 @@
             return earliestRemovaltime < mClock.currentTimeMillis();
         }
 
-        public void hideAsSoonAsPossible() {
-            removeAutoCancelCallbacks();
+        public void removeAsSoonAsPossible() {
+            removeAutoRemovalCallbacks();
             mHandler.postDelayed(mRemoveHeadsUpRunnable,
                     earliestRemovaltime - mClock.currentTimeMillis());
         }
+
+        public void reset() {
+            removeAutoRemovalCallbacks();
+            entry = null;
+            mRemoveHeadsUpRunnable = null;
+        }
     }
 
     /**
@@ -519,8 +534,29 @@
     }
 
     public interface OnHeadsUpChangedListener {
-        void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
-        void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp);
-        void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
+        /**
+         * The state whether there exist pinned heads-ups or not changed.
+         *
+         * @param inPinnedMode whether there are any pinned heads-ups
+         */
+        void onPinnedModeChanged(boolean inPinnedMode);
+
+        /**
+         * A notification was just pinned to the top.
+         */
+        void onHeadsUpPinned(ExpandableNotificationRow headsUp);
+
+        /**
+         * A notification was just unpinned from the top.
+         */
+        void onHeadsUpUnPinned(ExpandableNotificationRow headsUp);
+
+        /**
+         * A notification just became a heads up or turned back to its normal state.
+         *
+         * @param entry the entry of the changed notification
+         * @param isHeadsUp whether the notification is now a headsUp notification
+         */
+        void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
new file mode 100644
index 0000000..05c0099
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/HeadsUpAppearInterpolator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.stack;
+
+import android.graphics.Path;
+import android.view.animation.PathInterpolator;
+
+/**
+ * An interpolator specifically designed for the appear animation of heads up notifications.
+ */
+public class HeadsUpAppearInterpolator extends PathInterpolator {
+    public HeadsUpAppearInterpolator() {
+        super(getAppearPath());
+    }
+
+    private static Path getAppearPath() {
+        Path path = new Path();
+        path.moveTo(0, 0);
+        float x1 = 250f;
+        float x2 = 150f;
+        float x3 = 100f;
+        float y1 = 90f;
+        float y2 = 78f;
+        float y3 = 80f;
+        float xTot = (x1 + x2 + x3);
+        path.cubicTo(x1 * 0.9f / xTot, 0f,
+                x1 * 0.8f / xTot, y1 / y3,
+                x1 / xTot , y1 / y3);
+        path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
+                (x1 + x2 * 0.2f) / xTot, y2 / y3,
+                (x1 + x2) / xTot, y2 / y3);
+        path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
+                (x1 + x2 + x3 * 0.2f) / xTot, 1f,
+                1f, 1f);
+        return path;
+    }
+}
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 88fc602..a1b0cae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -204,7 +204,6 @@
     private ViewGroup mScrollView;
     private boolean mInterceptDelegateEnabled;
     private boolean mDelegateToScrollView;
-
     private boolean mDisallowScrollingInThisMotion;
     private long mGoToFullShadeDelay;
     private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
@@ -487,9 +486,9 @@
         int stackHeight;
         float paddingOffset;
         boolean trackingHeadsUp = mTrackingHeadsUp;
-        int normalExpandPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight()
+        int normalUnfoldPositionStart = trackingHeadsUp ? mHeadsUpManager.getTopHeadsUpHeight()
                 : minStackHeight;
-        if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalExpandPositionStart
+        if (newStackHeight - mTopPadding - mTopPaddingOverflow >= normalUnfoldPositionStart
                 || getNotGoneChildCount() == 0) {
             paddingOffset = mTopPaddingOverflow;
             stackHeight = newStackHeight;
@@ -582,7 +581,7 @@
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
             if (row.isHeadsUp()) {
-                mHeadsUpManager.addSwipedOutKey(row.getStatusBarNotification().getKey());
+                mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
             }
         }
         final View veto = v.findViewById(R.id.veto);
@@ -626,10 +625,10 @@
         requestChildrenUpdate();
     }
 
-    public boolean isPinnedHeadsUp(View v) {
+    public static boolean isPinnedHeadsUp(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
-            return row.isHeadsUp() && !row.isInShade();
+            return row.isHeadsUp() && row.isPinned();
         }
         return false;
     }
@@ -711,7 +710,7 @@
             if (touchY >= top && touchY <= bottom && touchX >= left && touchX <= right) {
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
-                    if (row.isHeadsUp() && !row.isInShade()
+                    if (row.isHeadsUp() && row.isPinned()
                             && mHeadsUpManager.getTopEntry().entry.row != row) {
                         continue;
                     }
@@ -812,7 +811,8 @@
         }
         handleEmptySpaceClick(ev);
         boolean expandWantsIt = false;
-        if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
+        if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion
+                && isScrollingEnabled()) {
             if (isCancelOrUp) {
                 mExpandHelper.onlyObserveMovements(false);
             }
@@ -824,7 +824,8 @@
             }
         }
         boolean scrollerWantsIt = false;
-        if (!mSwipingInProgress && !mExpandingNotification && !mDisallowScrollingInThisMotion) {
+        if (mIsExpanded && !mSwipingInProgress && !mExpandingNotification
+                && !mDisallowScrollingInThisMotion) {
             scrollerWantsIt = onScrollTouch(ev);
         }
         boolean horizontalSwipeWantsIt = false;
@@ -1872,15 +1873,15 @@
             boolean onBottom = false;
             if (!mIsExpanded && !isHeadsUp) {
                 type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
-            } else if (mAddedHeadsUpChildren.contains(row) || (!row.isInShade() && !mIsExpanded)) {
-                if (!row.isInShade() || shouldHunAppearFromBottom(row)) {
+            } else if (mAddedHeadsUpChildren.contains(row) || (row.isPinned() && !mIsExpanded)) {
+                if (row.isPinned() || shouldHunAppearFromBottom(row)) {
                     // Our custom add animation
                     type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR;
                 } else {
                     // Normal add animation
                     type = AnimationEvent.ANIMATION_TYPE_ADD;
                 }
-                onBottom = row.isInShade();
+                onBottom = !row.isPinned();
             }
             AnimationEvent event = new AnimationEvent(row, type);
             event.headsUpFromBottom = onBottom;
@@ -2670,7 +2671,7 @@
         }
     }
 
-    public void performOnAnimationFinished(Runnable runnable) {
+    public void runAfterAnimationFinished(Runnable runnable) {
         mAnimationFinishedRunnables.add(runnable);
     }
 
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 2a49a4c..202063a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -311,7 +311,8 @@
                     StackViewState viewState = resultState.getViewStateForView(
                             nextChild);
                     // The child below the dragged one must be fully visible
-                    if (!isPinnedHeadsUpView(draggedView) || isPinnedHeadsUpView(nextChild)) {
+                    if (!NotificationStackScrollLayout.isPinnedHeadsUp(draggedView)
+                            || NotificationStackScrollLayout.isPinnedHeadsUp(nextChild)) {
                         viewState.alpha = 1;
                     }
                 }
@@ -324,14 +325,6 @@
         }
     }
 
-    private boolean isPinnedHeadsUpView(View view) {
-        if (view instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-            return row.isHeadsUp() && !row.isInShade();
-        }
-        return false;
-    }
-
     /**
      * Update the visible children on the state.
      */
@@ -380,7 +373,7 @@
     /**
      * Determine the positions for the views. This is the main part of the algorithm.
      *
-     *  @param resultState The result state to update if a change to the properties of a child occurs
+     * @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
      * @param ambientState The current ambient state
      */
@@ -515,11 +508,12 @@
             }
             StackViewState childState = resultState.getViewStateForView(row);
             boolean isTopEntry = topHeadsUpEntry == row;
-            if (!row.isInShade()) {
+            if (row.isPinned()) {
                 childState.yTranslation = 0;
                 childState.height = row.getHeadsUpHeight();
                 if (!isTopEntry) {
-                    // Ensure that a headsUp is never below the topmost headsUp
+                    // 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.yTranslation = topState.yTranslation + topState.height
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f5d94c8..b9466d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -21,11 +21,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
-import android.graphics.Path;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
@@ -78,6 +76,7 @@
     private final Interpolator mFastOutSlowInInterpolator;
     private final Interpolator mHeadsUpAppearInterpolator;
     private final int mGoToFullShadeAppearingTranslation;
+    private final StackViewState mTmpState = new StackViewState();
     public NotificationStackScrollLayout mHostLayout;
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
             new ArrayList<>();
@@ -95,7 +94,6 @@
     private ValueAnimator mTopOverScrollAnimator;
     private ValueAnimator mBottomOverScrollAnimator;
     private ExpandableNotificationRow mChildExpandingView;
-    private StackViewState mTmpState = new StackViewState();
     private int mHeadsUpAppearHeightBottom;
     private boolean mShadeExpanded;
 
@@ -106,25 +104,7 @@
         mGoToFullShadeAppearingTranslation =
                 hostLayout.getContext().getResources().getDimensionPixelSize(
                         R.dimen.go_to_full_shade_appearing_translation);
-        Path path = new Path();
-        path.moveTo(0, 0);
-        float x1 = 250f;
-        float x2 = 150f;
-        float x3 = 100f;
-        float y1 = 90f;
-        float y2 = 78f;
-        float y3 = 80f;
-        float xTot = (x1 + x2 + x3);
-        path.cubicTo(x1 * 0.9f / xTot, 0f,
-                x1 * 0.8f / xTot, y1 / y3,
-                x1 / xTot , y1 / y3);
-        path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
-                (x1 + x2 * 0.2f) / xTot, y2 / y3,
-                (x1 + x2) / xTot, y2 / y3);
-        path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
-                (x1 + x2 + x3 * 0.2f) / xTot, 1f,
-                1f, 1f);
-        mHeadsUpAppearInterpolator = new PathInterpolator(path);
+        mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator();
     }
 
     public boolean isRunning() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index dda40d3..a5684a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -121,7 +121,7 @@
     }
 
     @Override
-    public void escalateHeadsUp() {
+    public void maybeEscalateHeadsUp() {
     }
 
     @Override