Revert "Split HeadsUpManager implementation to HeadsUpManagerPhone"

This reverts commit 78db3d0b0fd4bcae0ec92913e2b39491bffa409b.

Reason for revert: Cause crash

Test: none
Change-Id: Iffdad2513158c0856a0d3b56f7667063f4bba91c
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb913..3ebeb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -379,7 +379,7 @@
         // Because space is usually constrained in the auto use-case, there should not be a
         // pinned notification when the shade has been expanded. Ensure this by removing all heads-
         // up notifications.
-        mHeadsUpManager.releaseAllImmediately();
+        mHeadsUpManager.removeAllHeadsUpEntries();
         super.animateExpandNotificationsPanel();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
deleted file mode 100644
index c45c538..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.res.Resources;
-import android.support.v4.util.ArraySet;
-import android.util.Log;
-import android.util.Pools;
-import android.view.View;
-import android.view.ViewTreeObserver;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.Dumpable;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashSet;
-import java.util.Stack;
-
-/**
- * A implementation of HeadsUpManager for phone and car.
- */
-public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable,
-       ViewTreeObserver.OnComputeInternalInsetsListener, VisualStabilityManager.Callback,
-       OnHeadsUpChangedListener {
-    private static final String TAG = "HeadsUpManagerPhone";
-    private static final boolean DEBUG = false;
-
-    private final View mStatusBarWindowView;
-    private final int mStatusBarHeight;
-    private final NotificationGroupManager mGroupManager;
-    private final StatusBar mBar;
-    private final VisualStabilityManager mVisualStabilityManager;
-
-    private boolean mReleaseOnExpandFinish;
-    private boolean mTrackingHeadsUp;
-    private HashSet<String> mSwipedOutKeys = new HashSet<>();
-    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
-    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
-            = new ArraySet<>();
-    private boolean mIsExpanded;
-    private int[] mTmpTwoArray = new int[2];
-    private boolean mHeadsUpGoingAway;
-    private boolean mWaitingOnCollapseWhenGoingAway;
-    private boolean mIsObserving;
-    private int mStatusBarState;
-
-    private final Pools.Pool<HeadsUpEntryPhone> mEntryPool = new Pools.Pool<HeadsUpEntryPhone>() {
-        private Stack<HeadsUpEntryPhone> mPoolObjects = new Stack<>();
-
-        @Override
-        public HeadsUpEntryPhone acquire() {
-            if (!mPoolObjects.isEmpty()) {
-                return mPoolObjects.pop();
-            }
-            return new HeadsUpEntryPhone();
-        }
-
-        @Override
-        public boolean release(@NonNull HeadsUpEntryPhone instance) {
-            instance.reset();
-            mPoolObjects.push(instance);
-            return true;
-        }
-    };
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  Constructor:
-
-    public HeadsUpManagerPhone(@NonNull final Context context, @NonNull View statusBarWindowView,
-            @NonNull NotificationGroupManager groupManager, @NonNull StatusBar bar,
-            @NonNull VisualStabilityManager visualStabilityManager) {
-        super(context);
-
-        mStatusBarWindowView = statusBarWindowView;
-        mGroupManager = groupManager;
-        mBar = bar;
-        mVisualStabilityManager = visualStabilityManager;
-
-        Resources resources = mContext.getResources();
-        mStatusBarHeight = resources.getDimensionPixelSize(
-                com.android.internal.R.dimen.status_bar_height);
-
-        addListener(new OnHeadsUpChangedListener() {
-            @Override
-            public void onHeadsUpPinnedModeChanged(boolean hasPinnedNotification) {
-                if (DEBUG) Log.w(TAG, "onHeadsUpPinnedModeChanged");
-                updateTouchableRegionListener();
-            }
-        });
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  Public methods:
-
-    /**
-     * 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 invalid and should be discarded
-     */
-    public boolean shouldSwallowClick(@NonNull String key) {
-        HeadsUpManager.HeadsUpEntry entry = getHeadsUpEntry(key);
-        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
-            return true;
-        }
-        return false;
-    }
-
-    public void onExpandingFinished() {
-        if (mReleaseOnExpandFinish) {
-            releaseAllImmediately();
-            mReleaseOnExpandFinish = false;
-        } else {
-            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
-                if (isHeadsUp(entry.key)) {
-                    // Maybe the heads-up was removed already
-                    removeHeadsUpEntry(entry);
-                }
-            }
-        }
-        mEntriesToRemoveAfterExpand.clear();
-    }
-
-    /**
-     * Sets the tracking-heads-up flag. If the flag is true, HeadsUpManager doesn't remove the entry
-     * from the list even after a Heads Up Notification is gone.
-     */
-    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
-        mTrackingHeadsUp = trackingHeadsUp;
-    }
-
-    /**
-     * Notify that the status bar panel gets expanded or collapsed.
-     *
-     * @param isExpanded True to notify expanded, false to notify collapsed.
-     */
-    public void setIsPanelExpanded(boolean isExpanded) {
-        if (isExpanded != mIsExpanded) {
-            mIsExpanded = isExpanded;
-            if (isExpanded) {
-                // make sure our state is sane
-                mWaitingOnCollapseWhenGoingAway = false;
-                mHeadsUpGoingAway = false;
-                updateTouchableRegionListener();
-            }
-        }
-    }
-
-    /**
-     * Set the current state of the statusbar.
-     */
-    public void setStatusBarState(int statusBarState) {
-        mStatusBarState = statusBarState;
-    }
-
-    /**
-     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
-     * animating out. This is used to keep the touchable regions in a sane state.
-     */
-    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
-        if (headsUpGoingAway != mHeadsUpGoingAway) {
-            mHeadsUpGoingAway = headsUpGoingAway;
-            if (!headsUpGoingAway) {
-                waitForStatusBarLayout();
-            }
-            updateTouchableRegionListener();
-        }
-    }
-
-    /**
-     * Notifies that a remote input textbox in notification gets active or inactive.
-     * @param entry The entry of the target notification.
-     * @param remoteInputActive True to notify active, False to notify inactive.
-     */
-    public void setRemoteInputActive(
-            @NonNull NotificationData.Entry entry, boolean remoteInputActive) {
-        HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(entry.key);
-        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
-            headsUpEntry.remoteInputActive = remoteInputActive;
-            if (remoteInputActive) {
-                headsUpEntry.removeAutoRemovalCallbacks();
-            } else {
-                headsUpEntry.updateEntry(false /* updatePostTime */);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    public void removeMinimumDisplayTimeForTesting() {
-        mMinimumDisplayTime = 1;
-        mHeadsUpNotificationDecay = 1;
-        mTouchAcceptanceDelay = 1;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  HeadsUpManager public methods overrides:
-
-    @Override
-    public boolean isTrackingHeadsUp() {
-        return mTrackingHeadsUp;
-    }
-
-    @Override
-    public void snooze() {
-        super.snooze();
-        mReleaseOnExpandFinish = true;
-    }
-
-    /**
-     * React to the removal of the notification in the heads up.
-     *
-     * @return true if the notification was removed and false if it still needs to be kept around
-     * for a bit since it wasn't shown long enough
-     */
-    @Override
-    public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
-        if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
-            return super.removeNotification(key, ignoreEarliestRemovalTime);
-        } else {
-            HeadsUpEntryPhone entry = getHeadsUpEntryPhone(key);
-            entry.removeAsSoonAsPossible();
-            return false;
-        }
-    }
-
-    public void addSwipedOutNotification(@NonNull String key) {
-        mSwipedOutKeys.add(key);
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  Dumpable overrides:
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("HeadsUpManagerPhone state:");
-        dumpInternal(fd, pw, args);
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  ViewTreeObserver.OnComputeInternalInsetsListener overrides:
-
-    /**
-     * Overridden from TreeObserver.
-     */
-    @Override
-    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        if (mIsExpanded || mBar.isBouncerShowing()) {
-            // The touchable region is always the full area when expanded
-            return;
-        }
-        if (hasPinnedHeadsUp()) {
-            ExpandableNotificationRow topEntry = getTopEntry().row;
-            if (topEntry.isChildInGroup()) {
-                final ExpandableNotificationRow groupSummary
-                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
-                if (groupSummary != null) {
-                    topEntry = groupSummary;
-                }
-            }
-            topEntry.getLocationOnScreen(mTmpTwoArray);
-            int minX = mTmpTwoArray[0];
-            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
-            int maxY = topEntry.getIntrinsicHeight();
-
-            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(minX, 0, maxX, maxY);
-        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
-            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
-            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
-        }
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  VisualStabilityManager.Callback overrides:
-
-    @Override
-    public void onReorderingAllowed() {
-        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
-        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
-            if (isHeadsUp(entry.key)) {
-                // Maybe the heads-up was removed already
-                removeHeadsUpEntry(entry);
-            }
-        }
-        mEntriesToRemoveWhenReorderingAllowed.clear();
-        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  HeadsUpManager utility (protected) methods overrides:
-
-    @Override
-    protected HeadsUpEntry createHeadsUpEntry() {
-        return mEntryPool.acquire();
-    }
-
-    @Override
-    protected void releaseHeadsUpEntry(HeadsUpEntry entry) {
-        mEntryPool.release((HeadsUpEntryPhone) entry);
-    }
-
-    @Override
-    protected boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
-          return mStatusBarState != StatusBarState.KEYGUARD && !mIsExpanded
-                  || super.shouldHeadsUpBecomePinned(entry);
-    }
-
-    @Override
-    protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
-        super.dumpInternal(fd, pw, args);
-        pw.print("  mStatusBarState="); pw.println(mStatusBarState);
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  Protected utility methods:
-
-    @Nullable
-    protected HeadsUpEntryPhone getHeadsUpEntryPhone(@NonNull String key) {
-        return (HeadsUpEntryPhone) getHeadsUpEntry(key);
-    }
-
-    @Nullable
-    protected HeadsUpEntryPhone getTopHeadsUpEntryPhone() {
-        return (HeadsUpEntryPhone) getTopHeadsUpEntry();
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  Private utility methods:
-
-    private boolean wasShownLongEnough(@NonNull String key) {
-        if (mSwipedOutKeys.contains(key)) {
-            // We always instantly dismiss views being manually swiped out.
-            mSwipedOutKeys.remove(key);
-            return true;
-        }
-
-        HeadsUpEntryPhone headsUpEntry = getHeadsUpEntryPhone(key);
-        HeadsUpEntryPhone topEntry = getTopHeadsUpEntryPhone();
-        if (headsUpEntry != topEntry) {
-            return true;
-        }
-        return headsUpEntry.wasShownLongEnough();
-    }
-
-    /**
-     * We need to wait on the whole panel to collapse, before we can remove the touchable region
-     * listener.
-     */
-    private void waitForStatusBarLayout() {
-        mWaitingOnCollapseWhenGoingAway = true;
-        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft,
-                    int oldTop, int oldRight, int oldBottom) {
-                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
-                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
-                    mWaitingOnCollapseWhenGoingAway = false;
-                    updateTouchableRegionListener();
-                }
-            }
-        });
-    }
-
-    private void updateTouchableRegionListener() {
-        boolean shouldObserve = hasPinnedHeadsUp() || mHeadsUpGoingAway
-                || mWaitingOnCollapseWhenGoingAway;
-        if (shouldObserve == mIsObserving) {
-            return;
-        }
-        if (shouldObserve) {
-            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
-            mStatusBarWindowView.requestLayout();
-        } else {
-            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
-        }
-        mIsObserving = shouldObserve;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////////////////////////
-    //  HeadsUpEntryPhone:
-
-    protected class HeadsUpEntryPhone extends HeadsUpManager.HeadsUpEntry {
-        public void setEntry(@NonNull final NotificationData.Entry entry) {
-           Runnable removeHeadsUpRunnable = () -> {
-                if (!mVisualStabilityManager.isReorderingAllowed()) {
-                    mEntriesToRemoveWhenReorderingAllowed.add(entry);
-                    mVisualStabilityManager.addReorderingAllowedCallback(
-                            HeadsUpManagerPhone.this);
-                } else if (!mTrackingHeadsUp) {
-                    removeHeadsUpEntry(entry);
-                } else {
-                    mEntriesToRemoveAfterExpand.add(entry);
-                }
-            };
-
-            super.setEntry(entry, removeHeadsUpRunnable);
-        }
-
-        public boolean wasShownLongEnough() {
-            return earliestRemovaltime < mClock.currentTimeMillis();
-        }
-
-        @Override
-        public void updateEntry(boolean updatePostTime) {
-            super.updateEntry(updatePostTime);
-
-            if (mEntriesToRemoveAfterExpand.contains(entry)) {
-                mEntriesToRemoveAfterExpand.remove(entry);
-            }
-            if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
-                mEntriesToRemoveWhenReorderingAllowed.remove(entry);
-            }
-        }
-
-        @Override
-        public void expanded(boolean expanded) {
-            if (this.expanded == expanded) {
-                return;
-            }
-
-            this.expanded = expanded;
-            if (expanded) {
-                removeAutoRemovalCallbacks();
-            } else {
-                updateEntry(false /* updatePostTime */);
-            }
-        }
-    }
-}
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 2bfdefe..c85571c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -23,7 +23,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 /**
@@ -31,7 +31,7 @@
  */
 public class HeadsUpTouchHelper implements Gefingerpoken {
 
-    private HeadsUpManagerPhone mHeadsUpManager;
+    private HeadsUpManager mHeadsUpManager;
     private NotificationStackScrollLayout mStackScroller;
     private int mTrackingPointer;
     private float mTouchSlop;
@@ -43,7 +43,7 @@
     private NotificationPanelView mPanel;
     private ExpandableNotificationRow mPickedChild;
 
-    public HeadsUpTouchHelper(HeadsUpManagerPhone headsUpManager,
+    public HeadsUpTouchHelper(HeadsUpManager headsUpManager,
             NotificationStackScrollLayout stackScroller,
             NotificationPanelView notificationPanelView) {
         mHeadsUpManager = headsUpManager;
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 52d005c..cd2e77a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -68,12 +68,14 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 import java.util.List;
+import java.util.Collection;
 
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener,
@@ -1569,7 +1571,7 @@
     private void updatePanelExpanded() {
         boolean isExpanded = !isFullyCollapsed();
         if (mPanelExpanded != isExpanded) {
-            mHeadsUpManager.setIsPanelExpanded(isExpanded);
+            mHeadsUpManager.setIsExpanded(isExpanded);
             mStatusBar.setPanelExpanded(isExpanded);
             mPanelExpanded = isExpanded;
         }
@@ -2336,7 +2338,7 @@
     }
 
     @Override
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         super.setHeadsUpManager(headsUpManager);
         mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager, mNotificationStackScroller,
                 this);
@@ -2628,8 +2630,8 @@
         }
     }
 
-    public void setPulsing(boolean pulsing) {
-        mKeyguardStatusView.setPulsing(pulsing);
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+        mKeyguardStatusView.setPulsing(pulsing != null);
         positionClockAndNotifications();
         mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
                 + mKeyguardStatusView.getClockBottom());
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 6daabed..2b7e474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -50,7 +50,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -75,7 +75,7 @@
     }
 
     protected StatusBar mStatusBar;
-    protected HeadsUpManagerPhone mHeadsUpManager;
+    protected HeadsUpManager mHeadsUpManager;
 
     private float mPeekHeight;
     private float mHintDistance;
@@ -1252,7 +1252,7 @@
      */
     protected abstract int getClearAllHeight();
 
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3777a6c..1bf719a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -208,7 +208,6 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -220,7 +219,6 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
@@ -811,14 +809,15 @@
                 .commit();
         mIconController = Dependency.get(StatusBarIconController.class);
 
-        mHeadsUpManager = new HeadsUpManagerPhone(context, mStatusBarWindow, mGroupManager, this,
-                mVisualStabilityManager);
+        mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow, mGroupManager);
+        mHeadsUpManager.setBar(this);
         mHeadsUpManager.addListener(this);
         mHeadsUpManager.addListener(mNotificationPanel);
         mHeadsUpManager.addListener(mGroupManager);
         mHeadsUpManager.addListener(mVisualStabilityManager);
         mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
+        mHeadsUpManager.setVisualStabilityManager(mVisualStabilityManager);
         putComponent(HeadsUpManager.class, mHeadsUpManager);
 
         mEntryManager.setUpWithPresenter(this, mStackScroller, this, mHeadsUpManager);
@@ -1349,8 +1348,7 @@
 
     @Override
     public void onPerformRemoveNotification(StatusBarNotification n) {
-        if (mStackScroller.hasPulsingNotifications() &&
-                    !mHeadsUpManager.hasHeadsUpNotifications()) {
+        if (mStackScroller.hasPulsingNotifications() && mHeadsUpManager.getAllEntries().isEmpty()) {
             // We were showing a pulse for a notification, but no notifications are pulsing anymore.
             // Finish the pulse.
             mDozeScrimController.pulseOutNow();
@@ -2099,8 +2097,9 @@
     }
 
     public void maybeEscalateHeadsUp() {
-        mHeadsUpManager.getAllEntries().forEach(entry -> {
-            final StatusBarNotification sbn = entry.notification;
+        Collection<HeadsUpManager.HeadsUpEntry> entries = mHeadsUpManager.getAllEntries();
+        for (HeadsUpManager.HeadsUpEntry entry : entries) {
+            final StatusBarNotification sbn = entry.entry.notification;
             final Notification notification = sbn.getNotification();
             if (notification.fullScreenIntent != null) {
                 if (DEBUG) {
@@ -2110,11 +2109,11 @@
                     EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION,
                             sbn.getKey());
                     notification.fullScreenIntent.send();
-                    entry.notifyFullScreenIntentLaunched();
+                    entry.entry.notifyFullScreenIntentLaunched();
                 } catch (PendingIntent.CanceledException e) {
                 }
             }
-        });
+        }
         mHeadsUpManager.releaseAllImmediately();
     }
 
@@ -4659,22 +4658,24 @@
                 @Override
                 public void onPulseStarted() {
                     callback.onPulseStarted();
-                    if (mHeadsUpManager.hasHeadsUpNotifications()) {
+                    Collection<HeadsUpManager.HeadsUpEntry> pulsingEntries =
+                            mHeadsUpManager.getAllEntries();
+                    if (!pulsingEntries.isEmpty()) {
                         // Only pulse the stack scroller if there's actually something to show.
                         // Otherwise just show the always-on screen.
-                        setPulsing(true);
+                        setPulsing(pulsingEntries);
                     }
                 }
 
                 @Override
                 public void onPulseFinished() {
                     callback.onPulseFinished();
-                    setPulsing(false);
+                    setPulsing(null);
                 }
 
-                private void setPulsing(boolean pulsing) {
+                private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
                     mNotificationPanel.setPulsing(pulsing);
-                    mVisualStabilityManager.setPulsing(pulsing);
+                    mVisualStabilityManager.setPulsing(pulsing != null);
                     mIgnoreTouchWhilePulsing = false;
                 }
             }, reason);
@@ -4822,7 +4823,7 @@
 
 
     // for heads up notifications
-    protected HeadsUpManagerPhone mHeadsUpManager;
+    protected HeadsUpManager mHeadsUpManager;
 
     private AboveShelfObserver mAboveShelfObserver;
 
@@ -4925,7 +4926,7 @@
                 // Release the HUN notification to the shade.
 
                 if (isPresenterFullyCollapsed()) {
-                    HeadsUpUtil.setIsClickedHeadsUpNotification(row, true);
+                    HeadsUpManager.setIsClickedNotification(row, true);
                 }
                 //
                 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
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 a2b896d..53dfb24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,68 +16,118 @@
 
 package com.android.systemui.statusbar.policy;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.os.SystemClock;
 import android.os.Handler;
 import android.os.Looper;
-import android.util.ArrayMap;
+import android.os.SystemClock;
 import android.provider.Settings;
+import android.support.v4.util.ArraySet;
+import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Pools;
+import android.view.View;
+import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.stream.Stream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Stack;
 
 /**
  * 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 {
+public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener,
+        VisualStabilityManager.Callback {
     private static final String TAG = "HeadsUpManager";
     private static final boolean DEBUG = false;
     private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
+    private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
 
-    protected final Clock mClock = new Clock();
-    protected final Context mContext;
-    protected final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
-    protected final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final int mHeadsUpNotificationDecay;
+    private final int mMinimumDisplayTime;
 
-    protected int mHeadsUpNotificationDecay;
-    protected int mMinimumDisplayTime;
-    protected int mTouchAcceptanceDelay;
-    protected int mSnoozeLengthMs;
-    protected boolean mHasPinnedNotification;
-    protected int mUser;
-
-    private final HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+    private final int mTouchAcceptanceDelay;
     private final ArrayMap<String, Long> mSnoozedPackages;
-    private final ContentObserver mSettingsObserver;
+    private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
+    private final int mDefaultSnoozeLengthMs;
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
 
-    public HeadsUpManager(@NonNull final Context context) {
+        private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
+
+        @Override
+        public HeadsUpEntry acquire() {
+            if (!mPoolObjects.isEmpty()) {
+                return mPoolObjects.pop();
+            }
+            return new HeadsUpEntry();
+        }
+
+        @Override
+        public boolean release(HeadsUpEntry instance) {
+            instance.reset();
+            mPoolObjects.push(instance);
+            return true;
+        }
+    };
+
+    private final View mStatusBarWindowView;
+    private final int mStatusBarHeight;
+    private final Context mContext;
+    private final NotificationGroupManager mGroupManager;
+    private StatusBar mBar;
+    private int mSnoozeLengthMs;
+    private ContentObserver mSettingsObserver;
+    private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
+    private HashSet<String> mSwipedOutKeys = new HashSet<>();
+    private int mUser;
+    private Clock mClock;
+    private boolean mReleaseOnExpandFinish;
+    private boolean mTrackingHeadsUp;
+    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
+    private ArraySet<NotificationData.Entry> mEntriesToRemoveWhenReorderingAllowed
+            = new ArraySet<>();
+    private boolean mIsExpanded;
+    private boolean mHasPinnedNotification;
+    private int[] mTmpTwoArray = new int[2];
+    private boolean mHeadsUpGoingAway;
+    private boolean mWaitingOnCollapseWhenGoingAway;
+    private boolean mIsObserving;
+    private boolean mRemoteInputActive;
+    private float mExpandedHeight;
+    private VisualStabilityManager mVisualStabilityManager;
+    private int mStatusBarState;
+
+    public HeadsUpManager(final Context context, View statusBarWindowView,
+                          NotificationGroupManager groupManager) {
         mContext = context;
-        Resources resources = context.getResources();
-        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
-        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+        Resources resources = mContext.getResources();
         mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
         mSnoozedPackages = new ArrayMap<>();
-        int defaultSnoozeLengthMs =
-                resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
+        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
+        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
+        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
+        mClock = new Clock();
 
         mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
-                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, defaultSnoozeLengthMs);
+                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
         mSettingsObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
@@ -92,26 +142,47 @@
         context.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
                 mSettingsObserver);
+        mStatusBarWindowView = statusBarWindowView;
+        mGroupManager = groupManager;
+        mStatusBarHeight = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
     }
 
-    /**
-     * Adds an OnHeadUpChangedListener to observe events.
-     */
-    public void addListener(@NonNull OnHeadsUpChangedListener listener) {
+    private void updateTouchableRegionListener() {
+        boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
+                || mWaitingOnCollapseWhenGoingAway;
+        if (shouldObserve == mIsObserving) {
+            return;
+        }
+        if (shouldObserve) {
+            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+            mStatusBarWindowView.requestLayout();
+        } else {
+            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        }
+        mIsObserving = shouldObserve;
+    }
+
+    public void setBar(StatusBar bar) {
+        mBar = bar;
+    }
+
+    public void addListener(OnHeadsUpChangedListener listener) {
         mListeners.add(listener);
     }
 
-    /**
-     * Removes the OnHeadUpChangedListener from the observer list.
-     */
-    public void removeListener(@NonNull OnHeadsUpChangedListener listener) {
+    public void removeListener(OnHeadsUpChangedListener listener) {
         mListeners.remove(listener);
     }
 
+    public StatusBar getBar() {
+        return mBar;
+    }
+
     /**
      * Called when posting a new notification to the heads up.
      */
-    public void showNotification(@NonNull NotificationData.Entry headsUp) {
+    public void showNotification(NotificationData.Entry headsUp) {
         if (DEBUG) Log.v(TAG, "showNotification");
         addHeadsUpEntry(headsUp);
         updateNotification(headsUp, true);
@@ -121,7 +192,7 @@
     /**
      * Called when updating or posting a notification to the heads up.
      */
-    public void updateNotification(@NonNull NotificationData.Entry headsUp, boolean alert) {
+    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
         if (DEBUG) Log.v(TAG, "updateNotification");
 
         headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -133,13 +204,14 @@
                 // with the groupmanager
                 return;
             }
-            headsUpEntry.updateEntry(true /* updatePostTime */);
+            headsUpEntry.updateEntry();
             setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
         }
     }
 
-    private void addHeadsUpEntry(@NonNull NotificationData.Entry entry) {
-        HeadsUpEntry headsUpEntry = createHeadsUpEntry();
+    private void addHeadsUpEntry(NotificationData.Entry entry) {
+        HeadsUpEntry headsUpEntry = mEntryPool.acquire();
+
         // This will also add the entry to the sortedList
         headsUpEntry.setEntry(entry);
         mHeadsUpEntries.put(entry.key, headsUpEntry);
@@ -151,17 +223,16 @@
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
     }
 
-    protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationData.Entry entry) {
-        return hasFullScreenIntent(entry);
+    private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
+        return mStatusBarState != StatusBarState.KEYGUARD
+                && !mIsExpanded || hasFullScreenIntent(entry);
     }
 
-    protected boolean hasFullScreenIntent(@NonNull NotificationData.Entry entry) {
+    private boolean hasFullScreenIntent(NotificationData.Entry entry) {
         return entry.notification.getNotification().fullScreenIntent != null;
     }
 
-    protected void setEntryPinned(
-            @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
-        if (DEBUG) Log.v(TAG, "setEntryPinned: " + isPinned);
+    private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
         ExpandableNotificationRow row = headsUpEntry.entry.row;
         if (row.isPinned() != isPinned) {
             row.setPinned(isPinned);
@@ -176,35 +247,33 @@
         }
     }
 
-    protected void removeHeadsUpEntry(NotificationData.Entry entry) {
+    private void removeHeadsUpEntry(NotificationData.Entry entry) {
         HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
-        onHeadsUpEntryRemoved(remove);
-        releaseHeadsUpEntry(remove);
-    }
-
-    protected void onHeadsUpEntryRemoved(HeadsUpEntry remove) {
-        NotificationData.Entry entry = remove.entry;
         entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         entry.row.setHeadsUp(false);
         setEntryPinned(remove, false /* isPinned */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
+        mEntryPool.release(remove);
     }
 
-    protected void updatePinnedMode() {
+    public void removeAllHeadsUpEntries() {
+        for (String key : mHeadsUpEntries.keySet()) {
+            removeHeadsUpEntry(mHeadsUpEntries.get(key).entry);
+        }
+    }
+
+    private void updatePinnedMode() {
         boolean hasPinnedNotification = hasPinnedNotificationInternal();
         if (hasPinnedNotification == mHasPinnedNotification) {
             return;
         }
-        if (DEBUG) {
-            Log.v(TAG, "Pinned mode changed: " + mHasPinnedNotification + " -> " +
-                       hasPinnedNotification);
-        }
         mHasPinnedNotification = hasPinnedNotification;
         if (mHasPinnedNotification) {
             MetricsLogger.count(mContext, "note_peek", 1);
         }
+        updateTouchableRegionListener();
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
         }
@@ -216,36 +285,47 @@
      * @return true if the notification was removed and false if it still needs to be kept around
      * for a bit since it wasn't shown long enough
      */
-    public boolean removeNotification(@NonNull String key, boolean ignoreEarliestRemovalTime) {
-        if (DEBUG) Log.v(TAG, "removeNotification");
-        releaseImmediately(key);
-        return true;
+    public boolean removeNotification(String key, boolean ignoreEarliestRemovalTime) {
+        if (DEBUG) Log.v(TAG, "remove");
+        if (wasShownLongEnough(key) || ignoreEarliestRemovalTime) {
+            releaseImmediately(key);
+            return true;
+        } else {
+            getHeadsUpEntry(key).removeAsSoonAsPossible();
+            return false;
+        }
     }
 
-    /**
-     * Returns if the given notification is in the Heads Up Notification list or not.
-     */
+    private boolean wasShownLongEnough(String key) {
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+        HeadsUpEntry topEntry = getTopEntry();
+        if (mSwipedOutKeys.contains(key)) {
+            // We always instantly dismiss views being manually swiped out.
+            mSwipedOutKeys.remove(key);
+            return true;
+        }
+        if (headsUpEntry != topEntry) {
+            return true;
+        }
+        return headsUpEntry.wasShownLongEnough();
+    }
+
     public boolean isHeadsUp(String key) {
         return mHeadsUpEntries.containsKey(key);
     }
 
     /**
-     * Pushes any current Heads Up notification down into the shade.
+     * Push any current Heads Up notification down into the shade.
      */
     public void releaseAllImmediately() {
         if (DEBUG) Log.v(TAG, "releaseAllImmediately");
-        Iterator<HeadsUpEntry> iterator = mHeadsUpEntries.values().iterator();
-        while (iterator.hasNext()) {
-            HeadsUpEntry entry = iterator.next();
-            iterator.remove();
-            onHeadsUpEntryRemoved(entry);
+        ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
+        for (String key : keys) {
+            releaseImmediately(key);
         }
     }
 
-    /**
-     * Pushes the given Heads Up notification down into the shade.
-     */
-    public void releaseImmediately(@NonNull String key) {
+    public void releaseImmediately(String key) {
         HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
         if (headsUpEntry == null) {
             return;
@@ -254,14 +334,11 @@
         removeHeadsUpEntry(shadeEntry);
     }
 
-    /**
-     * Returns if the given notification is snoozed or not.
-     */
-    public boolean isSnoozed(@NonNull String packageName) {
+    public boolean isSnoozed(String packageName) {
         final String key = snoozeKey(packageName, mUser);
         Long snoozedUntil = mSnoozedPackages.get(key);
         if (snoozedUntil != null) {
-            if (snoozedUntil > mClock.currentTimeMillis()) {
+            if (snoozedUntil > SystemClock.elapsedRealtime()) {
                 if (DEBUG) Log.v(TAG, key + " snoozed");
                 return true;
             }
@@ -270,61 +347,33 @@
         return false;
     }
 
-    /**
-     * Snoozes all current Heads Up Notifications.
-     */
     public void snooze() {
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
             String packageName = entry.entry.notification.getPackageName();
             mSnoozedPackages.put(snoozeKey(packageName, mUser),
-                    mClock.currentTimeMillis() + mSnoozeLengthMs);
+                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
         }
+        mReleaseOnExpandFinish = true;
     }
 
-    private static String snoozeKey(@NonNull String packageName, int user) {
+    private static String snoozeKey(String packageName, int user) {
         return user + "," + packageName;
     }
 
-    protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
+    private HeadsUpEntry getHeadsUpEntry(String key) {
         return mHeadsUpEntries.get(key);
     }
 
-    /**
-     * Returns the entry of given Heads Up Notification.
-     *
-     * @param key Key of heads up notification
-     */
-    public NotificationData.Entry getEntry(@NonNull String key) {
-        HeadsUpEntry entry = mHeadsUpEntries.get(key);
-        return entry != null ? entry.entry : null;
+    public NotificationData.Entry getEntry(String key) {
+        return mHeadsUpEntries.get(key).entry;
     }
 
-    /**
-     * Returns the stream of all current Heads Up Notifications.
-     */
-    @NonNull
-    public Stream<NotificationData.Entry> getAllEntries() {
-        return mHeadsUpEntries.values().stream().map(headsUpEntry -> headsUpEntry.entry);
+    public Collection<HeadsUpEntry> getAllEntries() {
+        return mHeadsUpEntries.values();
     }
 
-    /**
-     * Returns the top Heads Up Notification, which appeares to show at first.
-     */
-    @Nullable
-    public NotificationData.Entry getTopEntry() {
-        HeadsUpEntry topEntry = getTopHeadsUpEntry();
-        return (topEntry != null) ? topEntry.entry : null;
-    }
-
-    /**
-     * Returns if any heads up notification is available or not.
-     */
-    public boolean hasHeadsUpNotifications() {
-        return !mHeadsUpEntries.isEmpty();
-    }
-
-    protected HeadsUpEntry getTopHeadsUpEntry() {
+    public HeadsUpEntry getTopEntry() {
         if (mHeadsUpEntries.isEmpty()) {
             return null;
         }
@@ -338,21 +387,56 @@
     }
 
     /**
-     * Sets the current user.
+     * 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 invalid and should be discarded
      */
+    public boolean shouldSwallowClick(String key) {
+        HeadsUpEntry entry = mHeadsUpEntries.get(key);
+        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
+            return true;
+        }
+        return false;
+    }
+
+    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
+        if (mIsExpanded || mBar.isBouncerShowing()) {
+            // The touchable region is always the full area when expanded
+            return;
+        }
+        if (mHasPinnedNotification) {
+            ExpandableNotificationRow topEntry = getTopEntry().entry.row;
+            if (topEntry.isChildInGroup()) {
+                final ExpandableNotificationRow groupSummary
+                        = mGroupManager.getGroupSummary(topEntry.getStatusBarNotification());
+                if (groupSummary != null) {
+                    topEntry = groupSummary;
+                }
+            }
+            topEntry.getLocationOnScreen(mTmpTwoArray);
+            int minX = mTmpTwoArray[0];
+            int maxX = mTmpTwoArray[0] + topEntry.getWidth();
+            int maxY = topEntry.getIntrinsicHeight();
+
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(minX, 0, maxX, maxY);
+        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
+        }
+    }
+
     public void setUser(int user) {
         mUser = user;
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("HeadsUpManager state:");
-        dumpInternal(fd, pw, args);
-    }
-
-    protected void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("  mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
         pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
-        pw.print("  now="); pw.println(mClock.currentTimeMillis());
+        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
         pw.print("  mUser="); pw.println(mUser);
         for (HeadsUpEntry entry: mHeadsUpEntries.values()) {
             pw.print("  HeadsUpEntry="); pw.println(entry.entry);
@@ -365,9 +449,6 @@
         }
     }
 
-    /**
-     * Returns if there are any pinned Heads Up Notifications or not.
-     */
     public boolean hasPinnedHeadsUp() {
         return mHasPinnedNotification;
     }
@@ -383,8 +464,14 @@
     }
 
     /**
-     * Unpins all pinned Heads Up Notifications.
+     * 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 void unpinAll() {
         for (String key : mHeadsUpEntries.keySet()) {
             HeadsUpEntry entry = mHeadsUpEntries.get(key);
@@ -394,13 +481,60 @@
         }
     }
 
-    /**
-     * Returns the value of the tracking-heads-up flag. See the doc of {@code setTrackingHeadsUp} as
-     * well.
-     */
+    public void onExpandingFinished() {
+        if (mReleaseOnExpandFinish) {
+            releaseAllImmediately();
+            mReleaseOnExpandFinish = false;
+        } else {
+            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
+                if (isHeadsUp(entry.key)) {
+                    // Maybe the heads-up was removed already
+                    removeHeadsUpEntry(entry);
+                }
+            }
+        }
+        mEntriesToRemoveAfterExpand.clear();
+    }
+
+    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
+        mTrackingHeadsUp = trackingHeadsUp;
+    }
+
     public boolean isTrackingHeadsUp() {
-        // Might be implemented in subclass.
-        return false;
+        return mTrackingHeadsUp;
+    }
+
+    public void setIsExpanded(boolean isExpanded) {
+        if (isExpanded != mIsExpanded) {
+            mIsExpanded = isExpanded;
+            if (isExpanded) {
+                // make sure our state is sane
+                mWaitingOnCollapseWhenGoingAway = false;
+                mHeadsUpGoingAway = false;
+                updateTouchableRegionListener();
+            }
+        }
+    }
+
+    /**
+     * @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();
+        if (topEntry == null || topEntry.entry == null) {
+            return 0;
+        }
+        ExpandableNotificationRow row = topEntry.entry.row;
+        if (row.isChildInGroup()) {
+            final ExpandableNotificationRow groupSummary
+                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
+            if (groupSummary != null) {
+                row = groupSummary;
+            }
+        }
+        return row.getPinnedHeadsUpHeight();
     }
 
     /**
@@ -419,67 +553,147 @@
     }
 
     /**
-     * Sets an entry to be expanded and therefore stick in the heads up area if it's pinned
-     * until it's collapsed again.
+     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+     * animating out. This is used to keep the touchable regions in a sane state.
      */
+    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+        if (headsUpGoingAway != mHeadsUpGoingAway) {
+            mHeadsUpGoingAway = headsUpGoingAway;
+            if (!headsUpGoingAway) {
+                waitForStatusBarLayout();
+            }
+            updateTouchableRegionListener();
+        }
+    }
+
+    /**
+     * We need to wait on the whole panel to collapse, before we can remove the touchable region
+     * listener.
+     */
+    private void waitForStatusBarLayout() {
+        mWaitingOnCollapseWhenGoingAway = true;
+        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
+                    mWaitingOnCollapseWhenGoingAway = false;
+                    updateTouchableRegionListener();
+                }
+            }
+        });
+    }
+
+    public static void setIsClickedNotification(View child, boolean clicked) {
+        child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
+    }
+
+    public static boolean isClickedHeadsUpNotification(View child) {
+        Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
+        return clicked != null && clicked;
+    }
+
+    public void setRemoteInputActive(NotificationData.Entry entry, boolean remoteInputActive) {
+        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+        if (headsUpEntry != null && headsUpEntry.remoteInputActive != remoteInputActive) {
+            headsUpEntry.remoteInputActive = remoteInputActive;
+            if (remoteInputActive) {
+                headsUpEntry.removeAutoRemovalCallbacks();
+            } else {
+                headsUpEntry.updateEntry(false /* updatePostTime */);
+            }
+        }
+    }
 
     /**
      * 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(@NonNull NotificationData.Entry entry, boolean expanded) {
-        HeadsUpManager.HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
-        if (headsUpEntry != null && entry.row.isPinned()) {
-            headsUpEntry.expanded(expanded);
+    public void setExpanded(NotificationData.Entry entry, boolean expanded) {
+        HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(entry.key);
+        if (headsUpEntry != null && headsUpEntry.expanded != expanded && entry.row.isPinned()) {
+            headsUpEntry.expanded = expanded;
+            if (expanded) {
+                headsUpEntry.removeAutoRemovalCallbacks();
+            } else {
+                headsUpEntry.updateEntry(false /* updatePostTime */);
+            }
         }
     }
 
-    @NonNull
-    protected HeadsUpEntry createHeadsUpEntry() {
-        return new HeadsUpEntry();
+    @Override
+    public void onReorderingAllowed() {
+        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false);
+        for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) {
+            if (isHeadsUp(entry.key)) {
+                // Maybe the heads-up was removed already
+                removeHeadsUpEntry(entry);
+            }
+        }
+        mEntriesToRemoveWhenReorderingAllowed.clear();
+        mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true);
     }
 
-    protected void releaseHeadsUpEntry(@NonNull HeadsUpEntry entry) {
-        // Do nothing for HeadsUpEntry.
+    public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) {
+        mVisualStabilityManager = visualStabilityManager;
+    }
+
+    public void setStatusBarState(int statusBarState) {
+        mStatusBarState = statusBarState;
     }
 
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
      * lifecycle automatically when created.
      */
-    protected class HeadsUpEntry implements Comparable<HeadsUpEntry> {
-        @Nullable public NotificationData.Entry entry;
+    public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
+        public NotificationData.Entry entry;
         public long postTime;
-        public boolean remoteInputActive;
         public long earliestRemovaltime;
+        private Runnable mRemoveHeadsUpRunnable;
+        public boolean remoteInputActive;
         public boolean expanded;
 
-        private Runnable mRemoveHeadsUpRunnable;
-
-        public void setEntry(@Nullable final NotificationData.Entry entry) {
-            setEntry(entry, null);
-        }
-
-        public void setEntry(@Nullable final NotificationData.Entry entry,
-                @Nullable Runnable removeHeadsUpRunnable) {
+        public void setEntry(final NotificationData.Entry entry) {
             this.entry = entry;
-            this.mRemoveHeadsUpRunnable = removeHeadsUpRunnable;
 
             // The actual post time will be just after the heads-up really slided in
             postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
-            updateEntry(true /* updatePostTime */);
+            mRemoveHeadsUpRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    if (!mVisualStabilityManager.isReorderingAllowed()) {
+                        mEntriesToRemoveWhenReorderingAllowed.add(entry);
+                        mVisualStabilityManager.addReorderingAllowedCallback(HeadsUpManager.this);
+                    } else if (!mTrackingHeadsUp) {
+                        removeHeadsUpEntry(entry);
+                    } else {
+                        mEntriesToRemoveAfterExpand.add(entry);
+                    }
+                }
+            };
+            updateEntry();
+        }
+
+        public void updateEntry() {
+            updateEntry(true);
         }
 
         public void updateEntry(boolean updatePostTime) {
-            if (DEBUG) Log.v(TAG, "updateEntry");
-
             long currentTime = mClock.currentTimeMillis();
             earliestRemovaltime = currentTime + mMinimumDisplayTime;
             if (updatePostTime) {
                 postTime = Math.max(postTime, currentTime);
             }
             removeAutoRemovalCallbacks();
-
+            if (mEntriesToRemoveAfterExpand.contains(entry)) {
+                mEntriesToRemoveAfterExpand.remove(entry);
+            }
+            if (mEntriesToRemoveWhenReorderingAllowed.contains(entry)) {
+                mEntriesToRemoveWhenReorderingAllowed.remove(entry);
+            }
             if (!isSticky()) {
                 long finishTime = postTime + mHeadsUpNotificationDecay;
                 long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
@@ -493,7 +707,7 @@
         }
 
         @Override
-        public int compareTo(@NonNull HeadsUpEntry o) {
+        public int compareTo(HeadsUpEntry o) {
             boolean isPinned = entry.row.isPinned();
             boolean otherPinned = o.entry.row.isPinned();
             if (isPinned && !otherPinned) {
@@ -520,29 +734,26 @@
                             : -1;
         }
 
-        public void expanded(boolean expanded) {
-            this.expanded = expanded;
-        }
-
-        public void reset() {
-            entry = null;
-            expanded = false;
-            remoteInputActive = false;
-            removeAutoRemovalCallbacks();
-            mRemoveHeadsUpRunnable = null;
-        }
-
         public void removeAutoRemovalCallbacks() {
-            if (mRemoveHeadsUpRunnable != null)
-                mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+            mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
+        }
+
+        public boolean wasShownLongEnough() {
+            return earliestRemovaltime < mClock.currentTimeMillis();
         }
 
         public void removeAsSoonAsPossible() {
-            if (mRemoveHeadsUpRunnable != null) {
-                removeAutoRemovalCallbacks();
-                mHandler.postDelayed(mRemoveHeadsUpRunnable,
-                        earliestRemovaltime - mClock.currentTimeMillis());
-            }
+            removeAutoRemovalCallbacks();
+            mHandler.postDelayed(mRemoveHeadsUpRunnable,
+                    earliestRemovaltime - mClock.currentTimeMillis());
+        }
+
+        public void reset() {
+            removeAutoRemovalCallbacks();
+            entry = null;
+            mRemoveHeadsUpRunnable = null;
+            expanded = false;
+            remoteInputActive = false;
         }
     }
 
@@ -551,4 +762,5 @@
             return SystemClock.elapsedRealtime();
         }
     }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
deleted file mode 100644
index 1e3c123c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpUtil.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2017 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.policy;
-
-import android.view.View;
-
-import com.android.systemui.R;
-
-/**
- * A class of utility static methods for heads up notifications.
- */
-public final class HeadsUpUtil {
-    private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
-
-    /**
-     * Set the given view as clicked or not-clicked.
-     * @param view The view to be set the flag to.
-     * @param clicked True to set as clicked. False to not-clicked.
-     */
-    public static void setIsClickedHeadsUpNotification(View view, boolean clicked) {
-        view.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
-    }
-
-    /**
-     * Check if the given view has the flag of "clicked notification"
-     * @param view The view to be checked.
-     * @return True if the view has clicked. False othrewise.
-     */
-    public static boolean isClickedHeadsUpNotification(View view) {
-        Boolean clicked = (Boolean) view.getTag(TAG_CLICKED_NOTIFICATION);
-        return clicked != null && clicked;
-    }
-}
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 d7a810e..424858a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -64,7 +64,7 @@
     private boolean mPanelTracking;
     private boolean mExpansionChanging;
     private boolean mPanelFullWidth;
-    private boolean mPulsing;
+    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
     private boolean mUnlockHintRunning;
     private boolean mQsCustomizerShowing;
     private int mIntrinsicPadding;
@@ -315,18 +315,23 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing;
+        return mPulsing != null;
     }
 
-    public void setPulsing(boolean hasPulsing) {
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> hasPulsing) {
         mPulsing = hasPulsing;
     }
 
     public boolean isPulsing(NotificationData.Entry entry) {
-        if (!mPulsing || mHeadsUpManager == null) {
+        if (mPulsing == null) {
             return false;
         }
-        return mHeadsUpManager.getAllEntries().anyMatch(e -> (e == entry));
+        for (HeadsUpManager.HeadsUpEntry e : mPulsing) {
+            if (e.entry == entry) {
+                return true;
+            }
+        }
+        return false;
     }
 
     public boolean isPanelTracking() {
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 1b55a5b..c114a6f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -92,11 +92,10 @@
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ScrollAdapter;
 
 import android.support.v4.graphics.ColorUtils;
@@ -289,7 +288,7 @@
     private HashSet<View> mClearOverlayViewsWhenFinished = new HashSet<>();
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
-    private HeadsUpManagerPhone mHeadsUpManager;
+    private HeadsUpManager mHeadsUpManager;
     private boolean mTrackingHeadsUp;
     private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
@@ -359,7 +358,7 @@
         }
     };
     private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
-    private boolean mPulsing;
+    private Collection<HeadsUpManager.HeadsUpEntry> mPulsing;
     private boolean mDrawBackgroundAsSrc;
     private boolean mFadingOut;
     private boolean mParentNotFullyVisible;
@@ -691,7 +690,7 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
-        if (mPulsing) {
+        if (mPulsing != null) {
             mTopPadding = mClockBottom;
         } else {
             mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
@@ -921,27 +920,6 @@
     }
 
     /**
-     * @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.
-     */
-    private int getTopHeadsUpPinnedHeight() {
-        NotificationData.Entry topEntry = mHeadsUpManager.getTopEntry();
-        if (topEntry == null) {
-            return 0;
-        }
-        ExpandableNotificationRow row = topEntry.row;
-        if (row.isChildInGroup()) {
-            final ExpandableNotificationRow groupSummary
-                    = mGroupManager.getGroupSummary(row.getStatusBarNotification());
-            if (groupSummary != null) {
-                row = groupSummary;
-            }
-        }
-        return row.getPinnedHeadsUpHeight();
-    }
-
-    /**
      * @return the position from where the appear transition ends when expanding.
      *         Measured in absolute height.
      */
@@ -952,7 +930,7 @@
             int minNotificationsForShelf = 1;
             if (mTrackingHeadsUp
                     || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDark())) {
-                appearPosition = getTopHeadsUpPinnedHeight();
+                appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
                 minNotificationsForShelf = 2;
             } else {
                 appearPosition = 0;
@@ -1220,9 +1198,9 @@
                 if (slidingChild instanceof ExpandableNotificationRow) {
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().row != row
+                            && mHeadsUpManager.getTopEntry().entry.row != row
                             && mGroupManager.getGroupSummary(
-                                mHeadsUpManager.getTopEntry().row.getStatusBarNotification())
+                                mHeadsUpManager.getTopEntry().entry.row.getStatusBarNotification())
                                 != row) {
                         continue;
                     }
@@ -2142,7 +2120,7 @@
 
     @Override
     public boolean hasPulsingNotifications() {
-        return mPulsing;
+        return mPulsing != null;
     }
 
     private void updateScrollability() {
@@ -2775,7 +2753,7 @@
     }
 
     private boolean isClickedHeadsUp(View child) {
-        return HeadsUpUtil.isClickedHeadsUpNotification(child);
+        return HeadsUpManager.isClickedHeadsUpNotification(child);
     }
 
     /**
@@ -4280,7 +4258,7 @@
         mAnimationFinishedRunnables.add(runnable);
     }
 
-    public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
+    public void setHeadsUpManager(HeadsUpManager headsUpManager) {
         mHeadsUpManager = headsUpManager;
         mAmbientState.setHeadsUpManager(headsUpManager);
     }
@@ -4348,8 +4326,8 @@
         return mIsExpanded;
     }
 
-    public void setPulsing(boolean pulsing, int clockBottom) {
-        if (!mPulsing && !pulsing) {
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
+        if (mPulsing == null && pulsing == null) {
             return;
         }
         mPulsing = pulsing;
@@ -4488,7 +4466,7 @@
         pw.println(String.format("[%s: pulsing=%s qsCustomizerShowing=%s visibility=%s"
                         + " alpha:%f scrollY:%d]",
                 this.getClass().getSimpleName(),
-                mPulsing ? "T":"f",
+                mPulsing != null ?"T":"f",
                 mAmbientState.isQsCustomizerShowing() ? "T":"f",
                 getVisibility() == View.VISIBLE ? "visible"
                         : getVisibility() == View.GONE ? "gone"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
index 04a7bd7..682b849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java
@@ -30,7 +30,7 @@
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
-import com.android.systemui.statusbar.policy.HeadsUpUtil;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 /**
  * A state of a view. This can be used to apply a set of view properties to a view with
@@ -582,7 +582,7 @@
         animator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                HeadsUpUtil.setIsClickedHeadsUpNotification(child, false);
+                HeadsUpManager.setIsClickedNotification(child, false);
                 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
                 child.setTag(TAG_START_TRANSLATION_Y, null);
                 child.setTag(TAG_END_TRANSLATION_Y, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index f3c1171..6e7477f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -32,7 +32,6 @@
 import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationInflaterTest;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
@@ -52,7 +51,7 @@
     public NotificationTestHelper(Context context) {
         mContext = context;
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
+        mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager);
     }
 
     public ExpandableNotificationRow createRow() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
deleted file mode 100644
index 28f9417..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2018 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.phone;
-
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
-import android.view.View;
-import android.service.notification.StatusBarNotification;
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.VisualStabilityManager;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.assertFalse;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class HeadsUpManagerPhoneTest extends SysuiTestCase {
-    @Rule public MockitoRule rule = MockitoJUnit.rule();
-
-    private static final String TEST_PACKAGE_NAME = "test";
-    private static final int TEST_UID = 0;
-
-    private HeadsUpManagerPhone mHeadsUpManager;
-
-    private NotificationData.Entry mEntry;
-    private StatusBarNotification mSbn;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    @Mock private NotificationGroupManager mGroupManager;
-    @Mock private View mStatusBarWindowView;
-    @Mock private StatusBar mBar;
-    @Mock private ExpandableNotificationRow mRow;
-    @Mock private VisualStabilityManager mVSManager;
-
-    @Before
-    public void setUp() {
-        when(mVSManager.isReorderingAllowed()).thenReturn(true);
-
-        mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarWindowView, mGroupManager, mBar, mVSManager);
-
-        Notification.Builder n = new Notification.Builder(mContext, "")
-                .setSmallIcon(R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text");
-        mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
-             0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
-
-        mEntry = new NotificationData.Entry(mSbn);
-        mEntry.row = mRow;
-        mEntry.expandedIcon = mock(StatusBarIconView.class);
-    }
-
-    @Test
-    public void testBasicOperations() {
-        // Check the initial state.
-        assertNull(mHeadsUpManager.getEntry(mEntry.key));
-        assertNull(mHeadsUpManager.getTopEntry());
-        assertEquals(0, mHeadsUpManager.getAllEntries().count());
-        assertFalse(mHeadsUpManager.hasHeadsUpNotifications());
-
-        // Add a notification.
-        mHeadsUpManager.showNotification(mEntry);
-
-        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
-        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
-        assertEquals(1, mHeadsUpManager.getAllEntries().count());
-        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
-        // Update the notification.
-        mHeadsUpManager.updateNotification(mEntry, false);
-
-        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
-        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
-        assertEquals(1, mHeadsUpManager.getAllEntries().count());
-        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-
-        // Remove but defer, since the notification is visible on display.
-        mHeadsUpManager.removeNotification(mEntry.key, false);
-
-        assertEquals(mEntry, mHeadsUpManager.getEntry(mEntry.key));
-        assertEquals(mEntry, mHeadsUpManager.getTopEntry());
-        assertEquals(1, mHeadsUpManager.getAllEntries().count());
-        assertTrue(mHeadsUpManager.hasHeadsUpNotifications());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 31442af..bdf9b1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -86,8 +86,8 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -110,7 +110,7 @@
     @Mock private UnlockMethodCache mUnlockMethodCache;
     @Mock private KeyguardIndicationController mKeyguardIndicationController;
     @Mock private NotificationStackScrollLayout mStackScroller;
-    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private HeadsUpManager mHeadsUpManager;
     @Mock private SystemServicesProxy mSystemServicesProxy;
     @Mock private NotificationPanelView mNotificationPanelView;
     @Mock private IStatusBarService mBarService;
@@ -588,7 +588,7 @@
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
-                NotificationStackScrollLayout stack, HeadsUpManagerPhone hum,
+                NotificationStackScrollLayout stack, HeadsUpManager hum,
                 PowerManager pm, NotificationPanelView panelView,
                 IStatusBarService barService, NotificationListener notificationListener,
                 NotificationLogger notificationLogger,
@@ -650,7 +650,7 @@
         public void setUpForTest(NotificationPresenter presenter,
                 NotificationListContainer listContainer,
                 Callback callback,
-                HeadsUpManagerPhone headsUpManager,
+                HeadsUpManager headsUpManager,
                 NotificationData notificationData) {
             super.setUpWithPresenter(presenter, listContainer, callback, headsUpManager);
             mNotificationData = notificationData;