summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java346
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java83
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java57
12 files changed, 550 insertions, 174 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 2c384d0f4d80..21a33b0271db 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 @@ public final class AmbientPulseManager extends AlertingNotificationManager {
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/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index e89e6e89bc07..2db99453e36c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -256,9 +256,9 @@ public class NotificationMediaManager implements Dumpable {
private boolean isMediaNotification(NotificationData.Entry entry) {
// TODO: confirm that there's a valid media key
- return entry.getExpandedContentView() != null &&
- entry.getExpandedContentView()
- .findViewById(com.android.internal.R.id.media_actions) != null;
+ return entry.row.getExpandedContentView() != null
+ && entry.row.getExpandedContentView().findViewById(
+ com.android.internal.R.id.media_actions) != null;
}
private void clearCurrentMediaNotificationSession() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index d097c8e706ba..fbf12ed39561 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -50,7 +50,6 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.View;
import android.widget.ImageView;
-import android.widget.RemoteViews;
import androidx.annotation.Nullable;
@@ -102,11 +101,6 @@ public class NotificationData {
public boolean autoRedacted; // whether the redacted notification was generated by us
public int targetSdk;
private long lastFullScreenIntentLaunchTime = NOT_LAUNCHED_YET;
- public RemoteViews cachedContentView;
- public RemoteViews cachedBigContentView;
- public RemoteViews cachedHeadsUpContentView;
- public RemoteViews cachedPublicContentView;
- public RemoteViews cachedAmbientContentView;
public CharSequence remoteInputText;
public List<SnoozeCriterion> snoozeCriteria;
public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
@@ -178,14 +172,6 @@ public class NotificationData {
}
}
- public View getExpandedContentView() {
- return row.getPrivateLayout().getExpandedChild();
- }
-
- public View getPublicContentView() {
- return row.getPublicLayout().getContractedChild();
- }
-
public void notifyFullScreenIntentLaunched() {
setInterruption();
lastFullScreenIntentLaunchTime = SystemClock.elapsedRealtime();
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 a3e982e77522..28d339aaeab2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -18,6 +18,10 @@ package com.android.systemui.statusbar.notification;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
import static com.android.systemui.statusbar.NotificationRemoteInputManager
.FORCE_REMOTE_INPUT_HISTORY;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_HEADS_UP;
import android.annotation.Nullable;
import android.app.Notification;
@@ -71,6 +75,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationUiAdjustment;
import com.android.systemui.statusbar.NotificationUpdateHandler;
import com.android.systemui.statusbar.notification.row.NotificationInflater;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -440,25 +445,48 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
private void addEntry(NotificationData.Entry shadeEntry) {
- if (shouldHeadsUp(shadeEntry)) {
- mHeadsUpManager.showNotification(shadeEntry);
- // Mark as seen immediately
- setNotificationShown(shadeEntry.notification);
- }
- if (shouldPulse(shadeEntry)) {
- mAmbientPulseManager.showNotification(shadeEntry);
- }
addNotificationViews(shadeEntry);
mCallback.onNotificationAdded(shadeEntry);
}
+ /**
+ * Adds the entry to the respective alerting manager if the content view was inflated and
+ * the entry should still alert.
+ *
+ * @param entry entry to add
+ * @param inflatedFlags flags representing content views that were inflated
+ */
+ private void showAlertingView(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
+ 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)) {
+ mHeadsUpManager.showNotification(entry);
+ // Mark as seen immediately
+ setNotificationShown(entry.notification);
+ } else {
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+ }
+ }
+ if ((inflatedFlags & FLAG_CONTENT_VIEW_AMBIENT) != 0) {
+ if (shouldPulse(entry)) {
+ mAmbientPulseManager.showNotification(entry);
+ } else {
+ entry.row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT);
+ }
+ }
+ }
+
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
mPendingNotifications.remove(entry.key);
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
boolean isNew = mNotificationData.get(entry.key) == null;
if (isNew && !entry.row.isRemoved()) {
+ showAlertingView(entry, inflatedFlags);
addEntry(entry);
} else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
mVisualStabilityManager.onLowPriorityUpdated(entry);
@@ -636,7 +664,11 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setSmartActions(entry.smartActions);
- row.updateNotification(entry);
+ row.setEntry(entry);
+
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry));
+ row.inflateViews();
}
protected void addNotificationViews(NotificationData.Entry entry) {
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 bce613a33859..23492aa62e37 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,12 +17,19 @@
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_CONTENT_VIEW_AMBIENT;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater
+ .FLAG_CONTENT_VIEW_HEADS_UP;
import static com.android.systemui.statusbar.notification.row.NotificationInflater.InflationCallback;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -83,6 +90,7 @@ import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
import com.android.systemui.statusbar.notification.logging.NotificationCounters;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -429,12 +437,59 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
}
- public void updateNotification(NotificationData.Entry entry) {
+ /**
+ * Set the entry for the row.
+ *
+ * @param entry the entry this row is tied to
+ */
+ public void setEntry(@NonNull NotificationData.Entry entry) {
mEntry = entry;
mStatusBarNotification = entry.notification;
+ cacheIsSystemNotification();
+ }
+
+ /**
+ * Inflate views based off the inflation flags set. Inflation happens asynchronously.
+ */
+ public void inflateViews() {
mNotificationInflater.inflateNotificationViews();
+ }
- cacheIsSystemNotification();
+ /**
+ * 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
+ * @param shouldInflate true if it should be inflated, false if it should not
+ */
+ public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+ mNotificationInflater.updateInflationFlag(flag, shouldInflate);
}
/**
@@ -581,7 +636,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
headsUpHeight = mMaxHeadsUpHeight;
}
NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
- NotificationContentView.VISIBLE_TYPE_HEADSUP);
+ VISIBLE_TYPE_HEADSUP);
if (headsUpWrapper != null) {
headsUpHeight = Math.max(headsUpHeight, headsUpWrapper.getMinLayoutHeight());
}
@@ -2616,6 +2671,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return shouldShowPublic() ? mPublicLayout : mPrivateLayout;
}
+ public View getExpandedContentView() {
+ return getPrivateLayout().getExpandedChild();
+ }
+
public void setLegacy(boolean legacy) {
for (NotificationContentView l : mLayouts) {
l.setLegacy(legacy);
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 4ef8dbb19318..78564515a2c2 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.content.Context;
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 @@ public class NotificationContentView extends FrameLayout {
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() {
@@ -517,6 +522,14 @@ public class NotificationContentView extends FrameLayout {
removeView(mAmbientChild);
}
if (child == null) {
+ mAmbientChild = null;
+ mAmbientWrapper = null;
+ if (mVisibleType == VISIBLE_TYPE_AMBIENT) {
+ mVisibleType = VISIBLE_TYPE_CONTRACTED;
+ }
+ if (mTransformationStartVisibleType == VISIBLE_TYPE_AMBIENT) {
+ mTransformationStartVisibleType = UNDEFINED;
+ }
return;
}
addView(child);
@@ -1163,6 +1176,7 @@ public class NotificationContentView extends FrameLayout {
public void onNotificationUpdated(NotificationData.Entry entry) {
mStatusBarNotification = entry.notification;
+ mOnContentViewInactiveListeners.clear();
mBeforeN = entry.targetSdk < Build.VERSION_CODES.N;
updateAllSingleLineViews();
if (mContractedChild != null) {
@@ -1620,6 +1634,58 @@ public class NotificationContentView extends FrameLayout {
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 aa4765a349b4..ea1892be1b1f 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,12 +16,17 @@
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;
import android.content.Context;
import android.os.AsyncTask;
import android.os.CancellationSignal;
import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
@@ -35,6 +40,8 @@ import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.Assert;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -52,14 +59,64 @@ import java.util.concurrent.atomic.AtomicInteger;
public class NotificationInflater {
public static final String TAG = "NotificationInflater";
- @VisibleForTesting
- static final int FLAG_REINFLATE_ALL = ~0;
- private static final int FLAG_REINFLATE_CONTENT_VIEW = 1<<0;
- @VisibleForTesting
- static final int FLAG_REINFLATE_EXPANDED_VIEW = 1<<1;
- private static final int FLAG_REINFLATE_HEADS_UP_VIEW = 1<<2;
- private static final int FLAG_REINFLATE_PUBLIC_VIEW = 1<<3;
- private static final int FLAG_REINFLATE_AMBIENT_VIEW = 1<<4;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true,
+ prefix = {"FLAG_CONTENT_VIEW_"},
+ value = {
+ 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_CONTENT_VIEW_CONTRACTED = 1;
+
+ /**
+ * The expanded view. Seen when the user expands a notification.
+ */
+ 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_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_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_CONTENT_VIEW_PUBLIC = 1 << 4;
+
+ 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_CONTENT_VIEW_CONTRACTED
+ | FLAG_CONTENT_VIEW_EXPANDED
+ | FLAG_CONTENT_VIEW_PUBLIC;
+
+ /**
+ * The set of content views to inflate.
+ */
+ @InflationFlag
+ private int mInflationFlags = REQUIRED_INFLATION_FLAGS;
+
private static final InflationExecutor EXECUTOR = new InflationExecutor();
private final ExpandableNotificationRow mRow;
@@ -71,6 +128,7 @@ public class NotificationInflater {
private InflationCallback mCallback;
private boolean mRedactAmbient;
private List<Notification.Action> mSmartActions;
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews = new ArrayMap<>();
public NotificationInflater(ExpandableNotificationRow row) {
mRow = row;
@@ -89,10 +147,10 @@ public class NotificationInflater {
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);
}
- } ;
+ }
}
public void setUsesIncreasedHeight(boolean usesIncreasedHeight) {
@@ -117,38 +175,67 @@ public class NotificationInflater {
if (mRow.getEntry() == null) {
return;
}
- inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+ inflateNotificationViews(FLAG_CONTENT_VIEW_AMBIENT);
}
}
/**
+ * Set whether or not a particular content view is needed and whether or not it should be
+ * inflated. These flags will be used when we inflate or reinflate.
+ *
+ * @param flag the {@link InflationFlag} corresponding to the view that should/should not be
+ * inflated
+ * @param shouldInflate true if the view should be inflated, false otherwise
+ */
+ public void updateInflationFlag(@InflationFlag int flag, boolean shouldInflate) {
+ if (shouldInflate) {
+ mInflationFlags |= flag;
+ } else if ((REQUIRED_INFLATION_FLAGS & flag) == 0) {
+ mInflationFlags &= ~flag;
+ }
+ }
+
+ /**
+ * Add flags for which content views should be inflated in addition to those already set.
+ *
+ * @param flags a set of {@link InflationFlag} corresponding to content views that should be
+ * inflated
+ */
+ public void addInflationFlags(@InflationFlag int flags) {
+ mInflationFlags |= flags;
+ }
+
+ /**
* Inflate all views of this notification on a background thread. This is asynchronous and will
* notify the callback once it's finished.
*/
public void inflateNotificationViews() {
- inflateNotificationViews(FLAG_REINFLATE_ALL);
+ inflateNotificationViews(mInflationFlags);
}
/**
- * Reinflate all views for the specified flags on a background thread. This is asynchronous and
- * will notify the callback once it's finished.
+ * Inflate all views for the specified flags on a background thread. This is asynchronous and
+ * will notify the callback once it's finished. If the content view is already inflated, this
+ * will reinflate it.
*
- * @param reInflateFlags flags which views should be reinflated. Use {@link #FLAG_REINFLATE_ALL}
- * to reinflate all of views.
+ * @param reInflateFlags flags which views should be inflated. Should be a subset of
+ * {@link NotificationInflater#mInflationFlags} as only those will be
+ * inflated/reinflated.
*/
- @VisibleForTesting
- void inflateNotificationViews(int reInflateFlags) {
+ private void inflateNotificationViews(@InflationFlag int reInflateFlags) {
if (mRow.isRemoved()) {
// We don't want to reinflate anything for removed notifications. Otherwise views might
// be readded to the stack, leading to leaks. This may happen with low-priority groups
// where the removal of already removed children can lead to a reinflation.
return;
}
+ // Only inflate the ones that are set.
+ reInflateFlags |= mInflationFlags;
StatusBarNotification sbn = mRow.getEntry().notification;
- AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
- mIsLowPriority,
- mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
- mCallback, mRemoteViewClickHandler, mSmartActions);
+ AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mCachedContentViews,
+ mRow, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight,
+ mUsesIncreasedHeadsUpHeight, mRedactAmbient, mCallback, mRemoteViewClickHandler,
+ mSmartActions);
if (mCallback != null && mCallback.doInflateSynchronous()) {
task.onPostExecute(task.doInBackground());
} else {
@@ -157,38 +244,80 @@ public class NotificationInflater {
}
@VisibleForTesting
- InflationProgress inflateNotificationViews(int reInflateFlags,
+ InflationProgress inflateNotificationViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, Context packageContext) {
InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority,
mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
mRedactAmbient, packageContext);
- apply(result, reInflateFlags, mRow, mRedactAmbient, mRemoteViewClickHandler, null);
+ apply(result, reInflateFlags, mCachedContentViews, mRow, mRedactAmbient,
+ mRemoteViewClickHandler, null);
return result;
}
- private static InflationProgress createRemoteViews(int reInflateFlags,
+ /**
+ * 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_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_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_CONTENT_VIEW_CONTRACTED:
+ case FLAG_CONTENT_VIEW_EXPANDED:
+ case FLAG_CONTENT_VIEW_PUBLIC:
+ default:
+ break;
+ }
+ }
+
+ private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags,
Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup,
boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
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();
}
@@ -199,18 +328,20 @@ public class NotificationInflater {
return result;
}
- public static CancellationSignal apply(InflationProgress result, int reInflateFlags,
+ public static CancellationSignal apply(InflationProgress result,
+ @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
ExpandableNotificationRow row, boolean redactAmbient,
RemoteViews.OnClickHandler remoteViewClickHandler,
@Nullable InflationCallback callback) {
- NotificationData.Entry entry = row.getEntry();
NotificationContentView privateLayout = row.getPrivateLayout();
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, entry.cachedContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newContentView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_CONTRACTED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -222,18 +353,19 @@ public class NotificationInflater {
return result.newContentView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row, redactAmbient,
- isNewView, remoteViewClickHandler, callback, entry, privateLayout,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row, redactAmbient,
+ isNewView, remoteViewClickHandler, callback, privateLayout,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED),
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,
- entry.cachedBigContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newExpandedView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_EXPANDED));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -245,8 +377,8 @@ public class NotificationInflater {
return result.newExpandedView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
privateLayout, privateLayout.getExpandedChild(),
privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -254,11 +386,12 @@ public class NotificationInflater {
}
}
- 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,
- entry.cachedHeadsUpContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newHeadsUpView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_HEADS_UP));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -270,19 +403,20 @@ public class NotificationInflater {
return result.newHeadsUpView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ 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,
- entry.cachedPublicContentView);
+ boolean isNewView =
+ !canReapplyRemoteView(result.newPublicView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_PUBLIC));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -294,18 +428,19 @@ public class NotificationInflater {
return result.newPublicView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
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, entry.cachedAmbientContentView);
+ boolean isNewView = (!canReapplyAmbient(row, redactAmbient)
+ || !canReapplyRemoteView(result.newAmbientView,
+ cachedContentViews.get(FLAG_CONTENT_VIEW_AMBIENT)));
ApplyCallback applyCallback = new ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -317,15 +452,15 @@ public class NotificationInflater {
return result.newAmbientView;
}
};
- applyRemoteView(result, reInflateFlags, flag, row,
- redactAmbient, isNewView, remoteViewClickHandler, callback, entry,
+ applyRemoteView(result, reInflateFlags, flag, cachedContentViews, row,
+ redactAmbient, isNewView, remoteViewClickHandler, callback,
newParent, newParent.getAmbientChild(), newParent.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_AMBIENT), runningInflations,
applyCallback);
}
// Let's try to finish, maybe nobody is even inflating anything
- finishIfDone(result, reInflateFlags, runningInflations, callback, row,
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations, callback, row,
redactAmbient);
CancellationSignal cancellationSignal = new CancellationSignal();
cancellationSignal.setOnCancelListener(
@@ -335,11 +470,11 @@ public class NotificationInflater {
@VisibleForTesting
static void applyRemoteView(final InflationProgress result,
- final int reInflateFlags, int inflationId,
- final ExpandableNotificationRow row,
- final boolean redactAmbient, boolean isNewView,
+ final @InflationFlag int reInflateFlags, @InflationFlag int inflationId,
+ final ArrayMap<Integer, RemoteViews> cachedContentViews,
+ final ExpandableNotificationRow row, final boolean redactAmbient, boolean isNewView,
RemoteViews.OnClickHandler remoteViewClickHandler,
- @Nullable final InflationCallback callback, NotificationData.Entry entry,
+ @Nullable final InflationCallback callback,
NotificationContentView parentLayout, View existingView,
NotificationViewWrapper existingWrapper,
final HashMap<Integer, CancellationSignal> runningInflations,
@@ -362,7 +497,7 @@ public class NotificationInflater {
existingWrapper.onReinflated();
}
} catch (Exception e) {
- handleInflationError(runningInflations, e, entry.notification, callback);
+ handleInflationError(runningInflations, e, row.getStatusBarNotification(), callback);
// Add a running inflation to make sure we don't trigger callbacks.
// Safe to do because only happens in tests.
runningInflations.put(inflationId, new CancellationSignal());
@@ -381,8 +516,8 @@ public class NotificationInflater {
existingWrapper.onReinflated();
}
runningInflations.remove(inflationId);
- finishIfDone(result, reInflateFlags, runningInflations, callback, row,
- redactAmbient);
+ finishIfDone(result, reInflateFlags, cachedContentViews, runningInflations,
+ callback, row, redactAmbient);
}
@Override
@@ -407,7 +542,8 @@ public class NotificationInflater {
onViewApplied(newView);
} catch (Exception anotherException) {
runningInflations.remove(inflationId);
- handleInflationError(runningInflations, e, entry.notification, callback);
+ handleInflationError(runningInflations, e, row.getStatusBarNotification(),
+ callback);
}
}
};
@@ -430,8 +566,9 @@ public class NotificationInflater {
runningInflations.put(inflationId, cancellationSignal);
}
- private static void handleInflationError(HashMap<Integer, CancellationSignal> runningInflations,
- Exception e, StatusBarNotification notification, @Nullable InflationCallback callback) {
+ private static void handleInflationError(
+ HashMap<Integer, CancellationSignal> runningInflations, Exception e,
+ StatusBarNotification notification, @Nullable InflationCallback callback) {
Assert.isMainThread();
runningInflations.values().forEach(CancellationSignal::cancel);
if (callback != null) {
@@ -444,7 +581,8 @@ public class NotificationInflater {
*
* @return true if the inflation was finished
*/
- private static boolean finishIfDone(InflationProgress result, int reInflateFlags,
+ private static boolean finishIfDone(InflationProgress result,
+ @InflationFlag int reInflateFlags, ArrayMap<Integer, RemoteViews> cachedContentViews,
HashMap<Integer, CancellationSignal> runningInflations,
@Nullable InflationCallback endListener, ExpandableNotificationRow row,
boolean redactAmbient) {
@@ -453,40 +591,40 @@ public class NotificationInflater {
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);
}
- entry.cachedContentView = 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);
}
- entry.cachedBigContentView = 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);
}
- entry.cachedHeadsUpContentView = 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);
}
- entry.cachedPublicContentView = 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;
@@ -495,12 +633,12 @@ public class NotificationInflater {
newParent.setAmbientChild(result.inflatedAmbientView);
otherParent.setAmbientChild(null);
}
- entry.cachedAmbientContentView = result.newAmbientView;
+ cachedContentViews.put(FLAG_CONTENT_VIEW_AMBIENT, result.newAmbientView);
}
entry.headsUpStatusBarText = result.headsUpStatusBarText;
entry.headsUpStatusBarTextPublic = result.headsUpStatusBarTextPublic;
if (endListener != null) {
- endListener.onAsyncInflationFinished(row.getEntry());
+ endListener.onAsyncInflationFinished(row.getEntry(), reInflateFlags);
}
return true;
}
@@ -552,7 +690,15 @@ public class NotificationInflater {
public interface InflationCallback {
void handleInflationException(StatusBarNotification notification, Exception e);
- void onAsyncInflationFinished(NotificationData.Entry entry);
+
+ /**
+ * Callback for after the content views finish inflating.
+ *
+ * @param entry the entry with the content views set
+ * @param inflatedFlags the flags associated with the content views that were inflated
+ */
+ void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags);
/**
* Used to disable async-ness for tests. Should only be used for tests.
@@ -563,18 +709,13 @@ public class NotificationInflater {
}
public void clearCachesAndReInflate() {
- NotificationData.Entry entry = mRow.getEntry();
- entry.cachedAmbientContentView = null;
- entry.cachedBigContentView = null;
- entry.cachedContentView = null;
- entry.cachedHeadsUpContentView = null;
- entry.cachedPublicContentView = null;
+ mCachedContentViews.clear();
inflateNotificationViews();
}
private static boolean canReapplyAmbient(ExpandableNotificationRow row, boolean redactAmbient) {
NotificationContentView ambientView = redactAmbient ? row.getPublicLayout()
- : row.getPrivateLayout(); ;
+ : row.getPrivateLayout();
return ambientView.getAmbientChild() != null;
}
@@ -589,7 +730,8 @@ public class NotificationInflater {
private final InflationCallback mCallback;
private final boolean mUsesIncreasedHeadsUpHeight;
private final boolean mRedactAmbient;
- private int mReInflateFlags;
+ private @InflationFlag int mReInflateFlags;
+ private final ArrayMap<Integer, RemoteViews> mCachedContentViews;
private ExpandableNotificationRow mRow;
private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -597,15 +739,16 @@ public class NotificationInflater {
private List<Notification.Action> mSmartActions;
private AsyncInflationTask(StatusBarNotification notification,
- int reInflateFlags, ExpandableNotificationRow row, boolean isLowPriority,
- boolean isChildInGroup, boolean usesIncreasedHeight,
+ @InflationFlag int reInflateFlags,
+ ArrayMap<Integer, RemoteViews> cachedContentViews, ExpandableNotificationRow row,
+ boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight,
boolean usesIncreasedHeadsUpHeight, boolean redactAmbient,
- InflationCallback callback,
- RemoteViews.OnClickHandler remoteViewClickHandler,
+ InflationCallback callback, RemoteViews.OnClickHandler remoteViewClickHandler,
List<Notification.Action> smartActions) {
mRow = row;
mSbn = notification;
mReInflateFlags = reInflateFlags;
+ mCachedContentViews = cachedContentViews;
mContext = mRow.getContext();
mIsLowPriority = isLowPriority;
mIsChildInGroup = isChildInGroup;
@@ -622,6 +765,7 @@ public class NotificationInflater {
}
@VisibleForTesting
+ @InflationFlag
public int getReInflateFlags() {
return mReInflateFlags;
}
@@ -642,10 +786,9 @@ public class NotificationInflater {
packageContext);
processor.processNotification(notification, recoveredBuilder);
}
- return createRemoteViews(mReInflateFlags,
- recoveredBuilder, mIsLowPriority, mIsChildInGroup,
- mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
- packageContext);
+ return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority,
+ mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight,
+ mRedactAmbient, packageContext);
} catch (Exception e) {
mError = e;
return null;
@@ -655,8 +798,8 @@ public class NotificationInflater {
@Override
protected void onPostExecute(InflationProgress result) {
if (mError == null) {
- mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
- mRemoteViewClickHandler, this);
+ mCancellationSignal = apply(result, mReInflateFlags, mCachedContentViews, mRow,
+ mRedactAmbient, mRemoteViewClickHandler, this);
} else {
handleError(mError);
}
@@ -706,10 +849,11 @@ public class NotificationInflater {
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @InflationFlag int inflatedFlags) {
mRow.getEntry().onInflationTaskFinished();
mRow.onNotificationUpdated();
- mCallback.onAsyncInflationFinished(mRow.getEntry());
+ mCallback.onAsyncInflationFinished(mRow.getEntry(), inflatedFlags);
}
@Override
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 d477587f8ecb..b4d24d16113e 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 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
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 edf29ac1c4f8..aca1f90b5aa8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -31,9 +31,9 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.RemoteViews;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import com.android.systemui.statusbar.notification.row.NotificationInflaterTest;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
@@ -67,16 +67,50 @@ public class NotificationTestHelper {
mGroupManager.setHeadsUpManager(mHeadsUpManager);
}
+ /**
+ * Creates a generic row.
+ *
+ * @return a generic row with no special properties.
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow() throws Exception {
return createRow(PKG, UID);
}
+ /**
+ * Create a row with the package and user id specified.
+ *
+ * @param pkg package
+ * @param uid user id
+ * @return a row with a notification using the package and user id
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
}
+ /**
+ * Creates a row based off the notification given.
+ *
+ * @param notification the notification
+ * @return a row built off the notification
+ * @throws Exception
+ */
public ExpandableNotificationRow createRow(Notification notification) throws Exception {
- return generateRow(notification, PKG, UID, false /* isGroupRow */);
+ return generateRow(notification, PKG, UID, 0 /* extraInflationFlags */);
+ }
+
+ /**
+ * Create a row with the specified content views inflated in addition to the default.
+ *
+ * @param extraInflationFlags the flags corresponding to the additional content views that
+ * should be inflated
+ * @return a row with the specified content views inflated in addition to the default
+ * @throws Exception
+ */
+ public ExpandableNotificationRow createRow(@InflationFlag int extraInflationFlags)
+ throws Exception {
+ return generateRow(createNotification(), PKG, UID, extraInflationFlags);
}
/**
@@ -122,34 +156,53 @@ public class NotificationTestHelper {
boolean isGroupSummary,
@Nullable String groupKey)
throws Exception {
+ Notification notif = createNotification(isGroupSummary, groupKey);
+ return generateRow(notif, pkg, uid, 0 /* inflationFlags */);
+ }
+
+ /**
+ * Creates a generic notification.
+ *
+ * @return a notification with no special properties
+ */
+ private Notification createNotification() {
+ return createNotification(false /* isGroupSummary */, null /* groupKey */);
+ }
+
+ /**
+ * Creates a notification with the given parameters.
+ *
+ * @param isGroupSummary whether the notification is a group summary
+ * @param groupKey the group key for the notification group used across notifications
+ * @return a notification that is in the group specified or standalone if unspecified
+ */
+ private Notification createNotification(boolean isGroupSummary,
+ @Nullable String groupKey) {
Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
R.drawable.ic_person)
.setCustomContentView(new RemoteViews(mContext.getPackageName(),
R.layout.custom_view_dark))
.build();
- Notification.Builder notificationBuilder =
- new Notification.Builder(mContext, "channelId")
- .setSmallIcon(R.drawable.ic_person)
- .setContentTitle("Title")
- .setContentText("Text")
- .setPublicVersion(publicVersion);
-
- // Group notification setup
+ Notification.Builder notificationBuilder = new Notification.Builder(mContext, "channelId")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text")
+ .setPublicVersion(publicVersion)
+ .setStyle(new Notification.BigTextStyle().bigText("Big Text"));
if (isGroupSummary) {
notificationBuilder.setGroupSummary(true);
}
if (!TextUtils.isEmpty(groupKey)) {
notificationBuilder.setGroup(groupKey);
}
-
- return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
+ return notificationBuilder.build();
}
private ExpandableNotificationRow generateRow(
Notification notification,
String pkg,
int uid,
- boolean isGroupRow)
+ @InflationFlag int extraInflationFlags)
throws Exception {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
mContext.LAYOUT_INFLATER_SERVICE);
@@ -179,8 +232,10 @@ public class NotificationTestHelper {
entry.channel = new NotificationChannel(
notification.getChannelId(), notification.getChannelId(), IMPORTANCE_DEFAULT);
entry.channel.setBlockableSystem(true);
+ row.setEntry(entry);
+ row.getNotificationInflater().addInflationFlags(extraInflationFlags);
NotificationInflaterTest.runThenWaitForInflation(
- () -> row.updateNotification(entry),
+ () -> row.inflateViews(),
row.getNotificationInflater());
// This would be done as part of onAsyncInflationFinished, but we skip large amounts of
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 4e16b7f94283..f01ae7a23533 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationInflater;
import com.android.systemui.statusbar.notification.row.RowInflaterTask;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -134,8 +135,9 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
- super.onAsyncInflationFinished(entry);
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
+ super.onAsyncInflationFinished(entry, inflatedFlags);
mCountDownLatch.countDown();
}
@@ -428,7 +430,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow).updateNotification(eq(mEntry));
+ verify(mRow).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
@@ -443,7 +445,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
setSmartActions(mEntry.key, null);
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(0, mEntry.smartActions.size());
}
@@ -457,7 +459,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
@@ -472,7 +474,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase {
setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
mEntryManager.updateNotificationRanking(mRankingMap);
- verify(mRow, never()).updateNotification(eq(mEntry));
+ verify(mRow, never()).setEntry(eq(mEntry));
assertEquals(1, mEntry.smartActions.size());
assertEquals("action", mEntry.smartActions.get(0).title);
}
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 743b307d0666..cfc75261123a 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,8 +18,13 @@ package com.android.systemui.statusbar.notification.row;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+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;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -35,6 +40,7 @@ import android.app.AppOpsManager;
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;
@@ -134,6 +140,15 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
}
@Test
+ public void testFreeContentViewWhenSafe() throws Exception {
+ ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
+
+ row.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_HEADS_UP);
+
+ assertNull(row.getPrivateLayout().getHeadsUpChild());
+ }
+
+ @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 81e79d1490b9..150d9337d4a4 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,10 +16,13 @@
package com.android.systemui.statusbar.notification.row;
+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 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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -34,6 +37,7 @@ import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.ArrayMap;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RemoteViews;
@@ -82,7 +86,8 @@ public class NotificationInflaterTest extends SysuiTestCase {
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
}
});
}
@@ -91,7 +96,7 @@ public class NotificationInflaterTest extends SysuiTestCase {
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);
}
@@ -99,7 +104,7 @@ public class NotificationInflaterTest extends SysuiTestCase {
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);
}
@@ -111,14 +116,14 @@ public class NotificationInflaterTest extends SysuiTestCase {
}
@Test
- public void testInflationCallsOnlyRightMethod() throws Exception {
- mRow.getPrivateLayout().removeAllViews();
- mRow.getEntry().cachedBigContentView = null;
- runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(
- FLAG_REINFLATE_EXPANDED_VIEW), mNotificationInflater);
- assertTrue(mRow.getPrivateLayout().getChildCount() == 1);
- assertTrue(mRow.getPrivateLayout().getChildAt(0)
- == mRow.getPrivateLayout().getExpandedChild());
+ public void testInflationOnlyInflatesSetFlags() throws Exception {
+ mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
+ true /* shouldInflate */);
+ runThenWaitForInflation(() -> mNotificationInflater.inflateNotificationViews(),
+ mNotificationInflater);
+
+ assertNotNull(mRow.getPrivateLayout().getHeadsUpChild());
+ assertNull(mRow.getShowingLayout().getAmbientChild());
verify(mRow).onNotificationUpdated();
}
@@ -155,8 +160,9 @@ public class NotificationInflaterTest extends SysuiTestCase {
new NotificationInflater.InflationProgress();
result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1);
- NotificationInflater.applyRemoteView(result, FLAG_REINFLATE_EXPANDED_VIEW, 0, mRow,
- false /* redactAmbient */, true /* isNewView */, new RemoteViews.OnClickHandler(),
+ NotificationInflater.applyRemoteView(result, FLAG_CONTENT_VIEW_EXPANDED, 0,
+ new ArrayMap() /* cachedContentViews */, mRow, false /* redactAmbient */,
+ true /* isNewView */, new RemoteViews.OnClickHandler(),
new NotificationInflater.InflationCallback() {
@Override
public void handleInflationException(StatusBarNotification notification,
@@ -166,10 +172,11 @@ public class NotificationInflaterTest extends SysuiTestCase {
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
countDownLatch.countDown();
}
- }, mRow.getEntry(), mRow.getPrivateLayout(), null, null, new HashMap<>(),
+ }, mRow.getPrivateLayout(), null, null, new HashMap<>(),
new NotificationInflater.ApplyCallback() {
@Override
public void setResultView(View v) {
@@ -186,16 +193,19 @@ public class NotificationInflaterTest extends SysuiTestCase {
/* Cancelling requires us to be on the UI thread otherwise we might have a race */
@Test
- public void testSupersedesExistingTask() throws Exception {
+ public void testSupersedesExistingTask() {
+ mNotificationInflater.addInflationFlags(FLAG_CONTENT_VIEW_ALL);
mNotificationInflater.inflateNotificationViews();
+
+ // Trigger inflation of content and expanded only.
mNotificationInflater.setIsLowPriority(true);
mNotificationInflater.setIsChildInGroup(true);
+
InflationTask runningTask = mRow.getEntry().getRunningTask();
NotificationInflater.AsyncInflationTask asyncInflationTask =
(NotificationInflater.AsyncInflationTask) runningTask;
- Assert.assertSame("Successive inflations don't inherit the previous flags!",
- asyncInflationTask.getReInflateFlags(),
- NotificationInflater.FLAG_REINFLATE_ALL);
+ assertEquals("Successive inflations don't inherit the previous flags!",
+ asyncInflationTask.getReInflateFlags(), FLAG_CONTENT_VIEW_ALL);
runningTask.abort();
}
@@ -231,7 +241,8 @@ public class NotificationInflaterTest extends SysuiTestCase {
}
@Override
- public void onAsyncInflationFinished(NotificationData.Entry entry) {
+ public void onAsyncInflationFinished(NotificationData.Entry entry,
+ @NotificationInflater.InflationFlag int inflatedFlags) {
if (expectingException) {
exceptionHolder.setException(new RuntimeException(
"Inflation finished even though there should be an error"));