Fix heads up views freeing too early.

In the previous CL, heads up views were freed while they were still
animating away.  As a result, there could be a sudden shift in the view
the notification uses (most easily reproducible if you tap on a HUN with
actions as the actions disappear).  This CL introduces an API to set a
a listener for when the content view becomes inactive and remove it then.

Bug: 111809944
Test: runtest systemui, manual (HUN with full screen intent, tap, see
that it goes away without visual jank)

Change-Id: Ib27bcf993f6be41aa4fde50b39817004610f5fd4
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 2c384d0..21a33b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_AMBIENT;
+
 import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
@@ -85,6 +87,7 @@
         for (OnAmbientChangedListener listener : mListeners) {
             listener.onAmbientStateChanged(entry, false);
         }
+        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index 4a50a17..28d339a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -19,9 +19,9 @@
 import static com.android.systemui.statusbar.NotificationRemoteInputManager
         .FORCE_REMOTE_INPUT_HISTORY;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_AMBIENT_VIEW;
+        .FLAG_CONTENT_VIEW_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_HEADS_UP_VIEW;
+        .FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -458,7 +458,7 @@
      */
     private void showAlertingView(NotificationData.Entry entry,
             @InflationFlag int inflatedFlags) {
-        if ((inflatedFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             // Possible for shouldHeadsUp to change between the inflation starting and ending.
             // If it does and we no longer need to heads up, we should free the view.
             if (shouldHeadsUp(entry)) {
@@ -466,14 +466,14 @@
                 // Mark as seen immediately
                 setNotificationShown(entry.notification);
             } else {
-                entry.row.updateInflationFlag(FLAG_REINFLATE_HEADS_UP_VIEW, false);
+                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
             }
         }
-        if ((inflatedFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+        if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             if (shouldPulse(entry)) {
                 mAmbientPulseManager.showNotification(entry);
             } else {
-                entry.row.updateInflationFlag(FLAG_REINFLATE_AMBIENT_VIEW, false);
+                entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
             }
         }
     }
@@ -666,8 +666,8 @@
         row.setSmartActions(entry.smartActions);
         row.setEntry(entry);
 
-        row.updateInflationFlag(FLAG_REINFLATE_HEADS_UP_VIEW, shouldHeadsUp(entry));
-        row.updateInflationFlag(FLAG_REINFLATE_AMBIENT_VIEW, shouldPulse(entry));
+        row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
+        row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry));
         row.inflateViews();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 51da868..23492aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -17,10 +17,12 @@
 package com.android.systemui.statusbar.notification.row;
 
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_AMBIENT_VIEW;
+        .FLAG_CONTENT_VIEW_AMBIENT;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_HEADS_UP_VIEW;
+        .FLAG_CONTENT_VIEW_HEADS_UP;
 import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
 
 import android.animation.Animator;
@@ -454,6 +456,33 @@
     }
 
     /**
+     * Marks a content view as freeable, setting it so that future inflations do not reinflate
+     * and ensuring that the view is freed when it is safe to remove.
+     *
+     * @param inflationFlag flag corresponding to the content view to be freed
+     */
+    public void freeContentViewWhenSafe(@InflationFlag int inflationFlag) {
+        // View should not be reinflated in the future
+        updateInflationFlag(inflationFlag, false);
+        Runnable freeViewRunnable = () ->
+                mNotificationInflater.freeNotificationView(inflationFlag);
+        switch (inflationFlag) {
+            case FLAG_CONTENT_VIEW_HEADS_UP:
+                getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_HEADSUP,
+                        freeViewRunnable);
+                break;
+            case FLAG_CONTENT_VIEW_AMBIENT:
+                getPrivateLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+                        freeViewRunnable);
+                getPublicLayout().performWhenContentInactive(VISIBLE_TYPE_AMBIENT,
+                        freeViewRunnable);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
      * Update whether or not a content view should be inflated.
      *
      * @param flag the flag corresponding to the content view
@@ -607,7 +636,7 @@
             headsUpHeight = mMaxHeadsUpHeight;
         }
         NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
-                NotificationContentView.VISIBLE_TYPE_HEADSUP);
+                VISIBLE_TYPE_HEADSUP);
         if (headsUpWrapper != null) {
             headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
         }
@@ -642,12 +671,8 @@
         if (isHeadsUp) {
             mMustStayOnScreen = true;
             setAboveShelf(true);
-        } else {
-            if (isAboveShelf() != wasAboveShelf) {
-                mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
-            }
-            updateInflationFlag(FLAG_REINFLATE_HEADS_UP_VIEW, false);
-            mNotificationInflater.freeNotificationView(FLAG_REINFLATE_HEADS_UP_VIEW);
+        } else if (isAboveShelf() != wasAboveShelf) {
+            mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
         }
     }
 
@@ -657,10 +682,6 @@
 
     public void setAmbientPulsing(boolean isAmbientPulsing) {
         mIsAmbientPulsing = isAmbientPulsing;
-        if (!isAmbientPulsing) {
-            updateInflationFlag(FLAG_REINFLATE_AMBIENT_VIEW, false);
-            mNotificationInflater.freeNotificationView(FLAG_REINFLATE_AMBIENT_VIEW);
-        }
     }
 
     public void setGroupManager(NotificationGroupManager groupManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 700dfa1..7856451 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -108,6 +109,10 @@
     private NotificationGroupManager mGroupManager;
     private RemoteInputController mRemoteInputController;
     private Runnable mExpandedVisibleListener;
+    /**
+     * List of listeners for when content views become inactive (i.e. not the showing view).
+     */
+    private final ArrayMap<View, Runnable> mOnContentViewInactiveListeners = new ArrayMap<>();
 
     private final ViewTreeObserver.OnPreDrawListener mEnableAnimationPredrawListener
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -1171,6 +1176,7 @@
 
     public void onNotificationUpdated(NotificationData.Entry entry) {
         mStatusBarNotification = entry.notification;
+        mOnContentViewInactiveListeners.clear();
         mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
         updateAllSingleLineViews();
         if (mContractedChild != null) {
@@ -1628,6 +1634,58 @@
         fireExpandedVisibleListenerIfVisible();
     }
 
+    /**
+     * Set a one-shot listener to run when a given content view becomes inactive.
+     *
+     * @param visibleType visible type corresponding to the content view to listen
+     * @param listener runnable to run once when the content view becomes inactive
+     */
+    public void performWhenContentInactive(int visibleType, Runnable listener) {
+        View view = getViewForVisibleType(visibleType);
+        // View is already inactive
+        if (view == null || isContentViewInactive(visibleType)) {
+            listener.run();
+            return;
+        }
+        mOnContentViewInactiveListeners.put(view, listener);
+    }
+
+    /**
+     * Whether or not the content view is inactive.  This means it should not be visible
+     * or the showing content as removing it would cause visual jank.
+     *
+     * @param visibleType visible type corresponding to the content view to be removed
+     * @return true if the content view is inactive, false otherwise
+     */
+    public boolean isContentViewInactive(int visibleType) {
+        View view = getViewForVisibleType(visibleType);
+        return isContentViewInactive(view);
+    }
+
+    /**
+     * Whether or not the content view is inactive.
+     *
+     * @param view view to see if its inactive
+     * @return true if the view is inactive, false o/w
+     */
+    private boolean isContentViewInactive(View view) {
+        if (view == null) {
+            return true;
+        }
+        return view.getVisibility() != VISIBLE && getViewForVisibleType(mVisibleType) != view;
+    }
+
+    @Override
+    protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
+        super.onChildVisibilityChanged(child, oldVisibility, newVisibility);
+        if (isContentViewInactive(child)) {
+            Runnable listener = mOnContentViewInactiveListeners.remove(child);
+            if (listener != null) {
+                listener.run();
+            }
+        }
+    }
+
     public void setIsLowPriority(boolean isLowPriority) {
         mIsLowPriority = isLowPriority;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
index 7ded594..ea1892b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.app.Notification;
@@ -59,54 +62,54 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
-            prefix = {"FLAG_REINFLATE_"},
+            prefix = {"FLAG_CONTENT_VIEW_"},
             value = {
-                FLAG_REINFLATE_CONTENT_VIEW,
-                FLAG_REINFLATE_EXPANDED_VIEW,
-                FLAG_REINFLATE_HEADS_UP_VIEW,
-                FLAG_REINFLATE_AMBIENT_VIEW,
-                FLAG_REINFLATE_PUBLIC_VIEW,
-                FLAG_REINFLATE_ALL})
+                FLAG_CONTENT_VIEW_CONTRACTED,
+                FLAG_CONTENT_VIEW_EXPANDED,
+                FLAG_CONTENT_VIEW_HEADS_UP,
+                FLAG_CONTENT_VIEW_AMBIENT,
+                FLAG_CONTENT_VIEW_PUBLIC,
+                FLAG_CONTENT_VIEW_ALL})
     public @interface InflationFlag {}
     /**
      * The default, contracted view.  Seen when the shade is pulled down and in the lock screen
      * if there is no worry about content sensitivity.
      */
-    public static final int FLAG_REINFLATE_CONTENT_VIEW = 1;
+    public static final int FLAG_CONTENT_VIEW_CONTRACTED = 1;
 
     /**
      * The expanded view.  Seen when the user expands a notification.
      */
-    public static final int FLAG_REINFLATE_EXPANDED_VIEW = 1 << 1;
+    public static final int FLAG_CONTENT_VIEW_EXPANDED = 1 << 1;
 
     /**
      * The heads up view.  Seen when a high priority notification peeks in from the top.
      */
-    public static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1 << 2;
+    public static final int FLAG_CONTENT_VIEW_HEADS_UP = 1 << 2;
 
     /**
      * The ambient view.  Seen when a high priority notification is received and the phone
      * is dozing.
      */
-    public static final int FLAG_REINFLATE_AMBIENT_VIEW = 1 << 3;
+    public static final int FLAG_CONTENT_VIEW_AMBIENT = 1 << 3;
 
     /**
      * The public view.  This is a version of the contracted view that hides sensitive
      * information and is used on the lock screen if we determine that the notification's
      * content should be hidden.
      */
-    public static final int FLAG_REINFLATE_PUBLIC_VIEW = 1 << 4;
+    public static final int FLAG_CONTENT_VIEW_PUBLIC = 1 << 4;
 
-    public static final int FLAG_REINFLATE_ALL = ~0;
+    public static final int FLAG_CONTENT_VIEW_ALL = ~0;
 
     /**
      * Content views that must be inflated at all times.
      */
     @InflationFlag
     private static final int REQUIRED_INFLATION_FLAGS =
-            FLAG_REINFLATE_CONTENT_VIEW
-            | FLAG_REINFLATE_EXPANDED_VIEW
-            | FLAG_REINFLATE_PUBLIC_VIEW;
+            FLAG_CONTENT_VIEW_CONTRACTED
+            | FLAG_CONTENT_VIEW_EXPANDED
+            | FLAG_CONTENT_VIEW_PUBLIC;
 
     /**
      * The set of content views to inflate.
@@ -144,7 +147,7 @@
         if (childInGroup != mIsChildInGroup) {
             mIsChildInGroup = childInGroup;
             if (mIsLowPriority) {
-                int flags = FLAG_REINFLATE_CONTENT_VIEW | FLAG_REINFLATE_EXPANDED_VIEW;
+                int flags = FLAG_CONTENT_VIEW_CONTRACTED | FLAG_CONTENT_VIEW_EXPANDED;
                 inflateNotificationViews(flags);
             }
         }
@@ -172,7 +175,7 @@
             if (mRow.getEntry() == null) {
                 return;
             }
-            inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+            inflateNotificationViews(FLAG_CONTENT_VIEW_AMBIENT);
         }
     }
 
@@ -252,23 +255,41 @@
     }
 
     /**
-     * Frees the content view associated with the inflation flag.
+     * Frees the content view associated with the inflation flag.  Will only succeed if the
+     * view is safe to remove.
      *
      * @param inflateFlag the flag corresponding to the content view which should be freed
      */
     public void freeNotificationView(@InflationFlag int inflateFlag) {
+        if ((mInflationFlags & inflateFlag) != 0) {
+            // The view should still be inflated.
+            return;
+        }
         switch (inflateFlag) {
-            case FLAG_REINFLATE_HEADS_UP_VIEW:
-                mRow.getPrivateLayout().setHeadsUpChild(null);
-                mCachedContentViews.remove(FLAG_REINFLATE_HEADS_UP_VIEW);
+            case FLAG_CONTENT_VIEW_HEADS_UP:
+                if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) {
+                    mRow.getPrivateLayout().setHeadsUpChild(null);
+                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP);
+                }
                 break;
-            case FLAG_REINFLATE_AMBIENT_VIEW:
-                mRow.getShowingLayout().setAmbientChild(null);
-                mCachedContentViews.remove(FLAG_REINFLATE_AMBIENT_VIEW);
+            case FLAG_CONTENT_VIEW_AMBIENT:
+                boolean privateSafeToRemove = mRow.getPrivateLayout().isContentViewInactive(
+                        VISIBLE_TYPE_AMBIENT);
+                boolean publicSafeToRemove = mRow.getPublicLayout().isContentViewInactive(
+                        VISIBLE_TYPE_AMBIENT);
+                if (privateSafeToRemove) {
+                    mRow.getPrivateLayout().setAmbientChild(null);
+                }
+                if (publicSafeToRemove) {
+                    mRow.getPublicLayout().setAmbientChild(null);
+                }
+                if (privateSafeToRemove && publicSafeToRemove) {
+                    mCachedContentViews.remove(FLAG_CONTENT_VIEW_AMBIENT);
+                }
                 break;
-            case FLAG_REINFLATE_CONTENT_VIEW:
-            case FLAG_REINFLATE_EXPANDED_VIEW:
-            case FLAG_REINFLATE_PUBLIC_VIEW:
+            case FLAG_CONTENT_VIEW_CONTRACTED:
+            case FLAG_CONTENT_VIEW_EXPANDED:
+            case FLAG_CONTENT_VIEW_PUBLIC:
             default:
                 break;
         }
@@ -280,23 +301,23 @@
             Context packageContext) {
         InflationProgress result = new InflationProgress();
         isLowPriority = isLowPriority && !isChildInGroup;
-        if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
             result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
             result.newExpandedView = createExpandedView(builder, isLowPriority);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
             result.newHeadsUpView = builder.createHeadsUpContentView(usesIncreasedHeadsUpHeight);
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
             result.newPublicView = builder.makePublicContentView();
         }
 
-        if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+        if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
             result.newAmbientView = redactAmbient ? builder.makePublicAmbientNotification()
                     : builder.makeAmbientNotification();
         }
@@ -316,11 +337,11 @@
         NotificationContentView publicLayout = row.getPublicLayout();
         final HashMap<Integer, CancellationSignal> runningInflations = new HashMap<>();
 
-        int flag = FLAG_REINFLATE_CONTENT_VIEW;
+        int flag = FLAG_CONTENT_VIEW_CONTRACTED;
         if ((reInflateFlags & flag) != 0) {
             boolean isNewView =
                     !canReapplyRemoteView(result.newContentView,
-                            cachedContentViews.get(FLAG_REINFLATE_CONTENT_VIEW));
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -339,12 +360,12 @@
                     runningInflations, applyCallback);
         }
 
-        flag = FLAG_REINFLATE_EXPANDED_VIEW;
+        flag = FLAG_CONTENT_VIEW_EXPANDED;
         if ((reInflateFlags & flag) != 0) {
             if (result.newExpandedView != null) {
                 boolean isNewView =
                         !canReapplyRemoteView(result.newExpandedView,
-                                cachedContentViews.get(FLAG_REINFLATE_EXPANDED_VIEW));
+                                cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -365,12 +386,12 @@
             }
         }
 
-        flag = FLAG_REINFLATE_HEADS_UP_VIEW;
+        flag = FLAG_CONTENT_VIEW_HEADS_UP;
         if ((reInflateFlags & flag) != 0) {
             if (result.newHeadsUpView != null) {
                 boolean isNewView =
                         !canReapplyRemoteView(result.newHeadsUpView,
-                                cachedContentViews.get(FLAG_REINFLATE_HEADS_UP_VIEW));
+                                cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
                 ApplyCallback applyCallback = new ApplyCallback() {
                     @Override
                     public void setResultView(View v) {
@@ -386,16 +407,16 @@
                         redactAmbient, isNewView, remoteViewClickHandler, callback,
                         privateLayout, privateLayout.getHeadsUpChild(),
                         privateLayout.getVisibleWrapper(
-                                NotificationContentView.VISIBLE_TYPE_HEADSUP), runningInflations,
+                                VISIBLE_TYPE_HEADSUP), runningInflations,
                         applyCallback);
             }
         }
 
-        flag = FLAG_REINFLATE_PUBLIC_VIEW;
+        flag = FLAG_CONTENT_VIEW_PUBLIC;
         if ((reInflateFlags & flag) != 0) {
             boolean isNewView =
                     !canReapplyRemoteView(result.newPublicView,
-                            cachedContentViews.get(FLAG_REINFLATE_PUBLIC_VIEW));
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -414,12 +435,12 @@
                     runningInflations, applyCallback);
         }
 
-        flag = FLAG_REINFLATE_AMBIENT_VIEW;
+        flag = FLAG_CONTENT_VIEW_AMBIENT;
         if ((reInflateFlags & flag) != 0) {
             NotificationContentView newParent = redactAmbient ? publicLayout : privateLayout;
             boolean isNewView = (!canReapplyAmbient(row, redactAmbient)
                     || !canReapplyRemoteView(result.newAmbientView,
-                            cachedContentViews.get(FLAG_REINFLATE_AMBIENT_VIEW)));
+                            cachedContentViews.get(FLAG_CONTENT_VIEW_AMBIENT)));
             ApplyCallback applyCallback = new ApplyCallback() {
                 @Override
                 public void setResultView(View v) {
@@ -570,40 +591,40 @@
         NotificationContentView privateLayout = row.getPrivateLayout();
         NotificationContentView publicLayout = row.getPublicLayout();
         if (runningInflations.isEmpty()) {
-            if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
                 if (result.inflatedContentView != null) {
                     privateLayout.setContractedChild(result.inflatedContentView);
                 }
-                cachedContentViews.put(FLAG_REINFLATE_CONTENT_VIEW, result.newContentView);
+                cachedContentViews.put(FLAG_CONTENT_VIEW_CONTRACTED, result.newContentView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_EXPANDED_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0) {
                 if (result.inflatedExpandedView != null) {
                     privateLayout.setExpandedChild(result.inflatedExpandedView);
                 } else if (result.newExpandedView == null) {
                     privateLayout.setExpandedChild(null);
                 }
-                cachedContentViews.put(FLAG_REINFLATE_EXPANDED_VIEW, result.newExpandedView);
+                cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView);
                 row.setExpandable(result.newExpandedView != null);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_HEADS_UP_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0) {
                 if (result.inflatedHeadsUpView != null) {
                     privateLayout.setHeadsUpChild(result.inflatedHeadsUpView);
                 } else if (result.newHeadsUpView == null) {
                     privateLayout.setHeadsUpChild(null);
                 }
-                cachedContentViews.put(FLAG_REINFLATE_HEADS_UP_VIEW, result.newHeadsUpView);
+                cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) {
                 if (result.inflatedPublicView != null) {
                     publicLayout.setContractedChild(result.inflatedPublicView);
                 }
-                cachedContentViews.put(FLAG_REINFLATE_PUBLIC_VIEW, result.newPublicView);
+                cachedContentViews.put(FLAG_CONTENT_VIEW_PUBLIC, result.newPublicView);
             }
 
-            if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
+            if ((reInflateFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
                 if (result.inflatedAmbientView != null) {
                     NotificationContentView newParent = redactAmbient
                             ? publicLayout : privateLayout;
@@ -612,7 +633,7 @@
                     newParent.setAmbientChild(result.inflatedAmbientView);
                     otherParent.setAmbientChild(null);
                 }
-                cachedContentViews.put(FLAG_REINFLATE_AMBIENT_VIEW, result.newAmbientView);
+                cachedContentViews.put(FLAG_CONTENT_VIEW_AMBIENT, result.newAmbientView);
             }
             entry.headsUpStatusBarText = result.headsUpStatusBarText;
             entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
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 d477587..b4d24d16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -151,6 +153,7 @@
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, false);
         }
+        entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
     }
 
     protected void updatePinnedMode() {
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 50147d5..aca1f90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -18,9 +18,6 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_ALL;
-
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
@@ -190,7 +187,8 @@
                 .setSmallIcon(R.drawable.ic_person)
                 .setContentTitle("Title")
                 .setContentText("Text")
-                .setPublicVersion(publicVersion);
+                .setPublicVersion(publicVersion)
+                .setStyle(new Notification.BigTextStyle().bigText("Big Text"));
         if (isGroupSummary) {
             notificationBuilder.setGroupSummary(true);
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 16ba3d0..cfc7526 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -18,13 +18,12 @@
 
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_HEADS_UP_VIEW;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -41,6 +40,7 @@
 import android.app.NotificationChannel;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.ArraySet;
 import android.view.NotificationHeaderView;
@@ -140,26 +140,15 @@
     }
 
     @Test
-    public void testHeadsUpViewShouldBeFreedWhenNotHeadsUp() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_REINFLATE_ALL);
-        row.setHeadsUp(true);
+    public void testFreeContentViewWhenSafe() throws Exception {
+        ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
 
-        row.setHeadsUp(false);
+        row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
 
         assertNull(row.getPrivateLayout().getHeadsUpChild());
     }
 
     @Test
-    public void testAmbientViewShouldBeFreedWhenNotPulsing() throws Exception {
-        ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_REINFLATE_ALL);
-        row.setAmbientPulsing(true);
-
-        row.setAmbientPulsing(false);
-
-        assertNull(row.getShowingLayout().getAmbientChild());
-    }
-
-    @Test
     public void testAboveShelfChangedListenerCalled() throws Exception {
         ExpandableNotificationRow row = mNotificationTestHelper.createRow();
         AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
index 803f2a8..150d933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java
@@ -16,12 +16,9 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_ALL;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_EXPANDED_VIEW;
-import static com.android.systemui.statusbar.notification.row.NotificationInflater
-        .FLAG_REINFLATE_HEADS_UP_VIEW;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_HEADS_UP;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_ALL;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_EXPANDED;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -99,7 +96,7 @@
     public void testIncreasedHeadsUpBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeadsUpHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
         verify(builder).createHeadsUpContentView(true);
     }
 
@@ -107,7 +104,7 @@
     public void testIncreasedHeightBeingUsed() {
         mNotificationInflater.setUsesIncreasedHeight(true);
         Notification.Builder builder = spy(mBuilder);
-        mNotificationInflater.inflateNotificationViews(FLAG_REINFLATE_ALL, builder, mContext);
+        mNotificationInflater.inflateNotificationViews(FLAG_CONTENT_VIEW_ALL, builder, mContext);
         verify(builder).createContentView(true);
     }
 
@@ -120,7 +117,7 @@
 
     @Test
     public void testInflationOnlyInflatesSetFlags() throws Exception {
-        mNotificationInflater.updateInflationFlag(FLAG_REINFLATE_HEADS_UP_VIEW,
+        mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
                 true /* shouldInflate */);
         runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
                 mNotificationInflater);
@@ -163,7 +160,7 @@
                 new NotificationInflater.InflationProgress();
         result.packageContext = mContext;
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0,
+        NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
                 new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
                 true /* isNewView */, new RemoteViews.OnClickHandler(),
                 new NotificationInflater.InflationCallback() {
@@ -197,7 +194,7 @@
     /* Cancelling requires us to be on the UI thread otherwise we might have a race */
     @Test
     public void testSupersedesExistingTask() {
-        mNotificationInflater.addInflationFlags(FLAG_REINFLATE_ALL);
+        mNotificationInflater.addInflationFlags(FLAG_CONTENT_VIEW_ALL);
         mNotificationInflater.inflateNotificationViews();
 
         // Trigger inflation of content and expanded only.
@@ -208,7 +205,7 @@
         NotificationInflater.AsyncInflationTask asyncInflationTask =
                 (NotificationInflater.AsyncInflationTask) runningTask;
         assertEquals("Successive inflations don't inherit the previous flags!",
-                asyncInflationTask.getReInflateFlags(), FLAG_REINFLATE_ALL);
+                asyncInflationTask.getReInflateFlags(), FLAG_CONTENT_VIEW_ALL);
         runningTask.abort();
     }