summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2018-11-20 22:33:17 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2018-11-20 22:33:17 +0000
commit8053ac5abdc7694bdde4cc6f640cba934208d12b (patch)
treede6eb08cc4b86f9c82997b1ea4c052541408bcf2
parent0a91bc975e73fa1999a57a3ad419cb0099a2f584 (diff)
parent4b8bbda21b67498c0199e88aca33ddb3b590dd5d (diff)
Merge changes from topic "alert-group-transfer-fix"
* changes: Fix GroupAlertTransferHelper logic on update. Fix heads up/ambient content inflation w/ groups.
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIFactory.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInflater.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java490
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java390
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInflaterTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java268
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java182
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java12
18 files changed, 1079 insertions, 484 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 314a3c32060c..417d5168641d 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.phone.LockIcon;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -138,6 +139,8 @@ public class SystemUIFactory {
() -> new NotificationLockscreenUserManagerImpl(context));
providers.put(VisualStabilityManager.class, VisualStabilityManager::new);
providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
+ providers.put(NotificationGroupAlertTransferHelper.class,
+ NotificationGroupAlertTransferHelper::new);
providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
providers.put(AmbientPulseManager.class, () -> new AmbientPulseManager(context));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index b6e88d6dfc11..3da6d2e877ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.notification.NotificationData.Entry;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
@@ -27,7 +29,7 @@ import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.util.stream.Stream;
@@ -46,8 +48,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* NotificationManagerService side, but we keep it to prevent the UI from looking weird and
* will remove when possible. See {@link NotificationLifetimeExtender}
*/
- protected final ArraySet<NotificationData.Entry> mExtendedLifetimeAlertEntries =
- new ArraySet<>();
+ protected final ArraySet<Entry> mExtendedLifetimeAlertEntries = new ArraySet<>();
protected NotificationSafeToRemoveCallback mNotificationLifetimeFinishedCallback;
protected int mMinimumDisplayTime;
@@ -60,7 +61,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* Adds the notification to be managed.
* @param entry entry to show
*/
- public void showNotification(@NonNull NotificationData.Entry entry) {
+ public void showNotification(@NonNull Entry entry) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "showNotification");
}
@@ -139,7 +140,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* @return the entry
*/
@Nullable
- public NotificationData.Entry getEntry(@NonNull String key) {
+ public Entry getEntry(@NonNull String key) {
AlertEntry entry = mAlertEntries.get(key);
return entry != null ? entry.mEntry : null;
}
@@ -149,7 +150,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
* @return all entries
*/
@NonNull
- public Stream<NotificationData.Entry> getAllEntries() {
+ public Stream<Entry> getAllEntries() {
return mAlertEntries.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
}
@@ -170,10 +171,17 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
}
/**
+ * Gets the flag corresponding to the notification content view this alert manager will show.
+ *
+ * @return flag corresponding to the content view
+ */
+ public abstract @InflationFlag int getContentFlag();
+
+ /**
* Add a new entry and begin managing it.
* @param entry the entry to add
*/
- protected final void addAlertEntry(@NonNull NotificationData.Entry entry) {
+ protected final void addAlertEntry(@NonNull Entry entry) {
AlertEntry alertEntry = createAlertEntry();
alertEntry.setEntry(entry);
mAlertEntries.put(entry.key, alertEntry);
@@ -196,7 +204,7 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
if (alertEntry == null) {
return;
}
- NotificationData.Entry entry = alertEntry.mEntry;
+ Entry entry = alertEntry.mEntry;
mAlertEntries.remove(key);
onAlertEntryRemoved(alertEntry);
entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
@@ -243,12 +251,12 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
}
@Override
- public boolean shouldExtendLifetime(NotificationData.Entry entry) {
+ public boolean shouldExtendLifetime(Entry entry) {
return !canRemoveImmediately(entry.key);
}
@Override
- public void setShouldManageLifetime(NotificationData.Entry entry, boolean shouldExtend) {
+ public void setShouldManageLifetime(Entry entry, boolean shouldExtend) {
if (shouldExtend) {
mExtendedLifetimeAlertEntries.add(entry);
} else {
@@ -258,17 +266,17 @@ public abstract class AlertingNotificationManager implements NotificationLifetim
///////////////////////////////////////////////////////////////////////////////////////////////
protected class AlertEntry implements Comparable<AlertEntry> {
- @Nullable public NotificationData.Entry mEntry;
+ @Nullable public Entry mEntry;
public long mPostTime;
public long mEarliestRemovaltime;
@Nullable protected Runnable mRemoveAlertRunnable;
- public void setEntry(@NonNull final NotificationData.Entry entry) {
+ public void setEntry(@NonNull final Entry entry) {
setEntry(entry, () -> removeAlertEntry(entry.key));
}
- public void setEntry(@NonNull final NotificationData.Entry entry,
+ public void setEntry(@NonNull final Entry entry,
@Nullable Runnable removeAlertRunnable) {
mEntry = entry;
mRemoveAlertRunnable = removeAlertRunnable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
index 21a33b0271db..f1c03049202f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AmbientPulseManager.java
@@ -26,6 +26,7 @@ import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
/**
* Manager which handles high priority notifications that should "pulse" in when the device is
@@ -71,6 +72,10 @@ public final class AmbientPulseManager extends AlertingNotificationManager {
topEntry.extendPulse();
}
+ public @InflationFlag int getContentFlag() {
+ return FLAG_CONTENT_VIEW_AMBIENT;
+ }
+
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
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 ac0fe6efbc6e..0818513faf41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -84,6 +84,7 @@ 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.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -117,6 +118,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
private final NotificationGroupManager mGroupManager =
Dependency.get(NotificationGroupManager.class);
+ private final NotificationGroupAlertTransferHelper mGroupAlertTransferHelper =
+ Dependency.get(NotificationGroupAlertTransferHelper.class);
private final NotificationGutsManager mGutsManager =
Dependency.get(NotificationGutsManager.class);
private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -247,7 +250,8 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
}
private void onPostInit() {
- mGroupManager.setPendingEntries(mPendingNotifications);
+ mGroupAlertTransferHelper.setPendingEntries(mPendingNotifications);
+ mGroupManager.addOnGroupChangeListener(mGroupAlertTransferHelper);
}
/**
@@ -554,13 +558,18 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
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);
- mPresenter.updateNotificationViews();
+ if (!entry.row.isRemoved()) {
+ boolean isNew = mNotificationData.get(entry.key) == null;
+ if (isNew) {
+ showAlertingView(entry, inflatedFlags);
+ addEntry(entry);
+ } else {
+ if (entry.row.hasLowPriorityStateUpdated()) {
+ mVisualStabilityManager.onLowPriorityUpdated(entry);
+ mPresenter.updateNotificationViews();
+ }
+ mGroupAlertTransferHelper.onInflationFinished(entry);
+ }
}
entry.row.setLowPriorityStateUpdated(false);
}
@@ -573,6 +582,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
private void removeNotificationInternal(String key,
@Nullable NotificationListenerService.RankingMap ranking, boolean forceRemove) {
abortExistingInflation(key);
+ mGroupAlertTransferHelper.cleanUpPendingAlertInfo(key);
// Attempt to remove notifications from their alert managers (heads up, ambient pulse).
// Though the remove itself may fail, it lets the manager know to remove as soon as
@@ -734,8 +744,12 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
row.setEntry(entry);
- row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, shouldHeadsUp(entry));
- row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, shouldPulse(entry));
+ if (shouldHeadsUp(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP, true /* shouldInflate */);
+ }
+ if (shouldPulse(entry)) {
+ row.updateInflationFlag(FLAG_CONTENT_VIEW_AMBIENT, true /* shouldInflate */);
+ }
row.setNeedsRedaction(
Dependency.get(NotificationLockscreenUserManager.class).needsRedaction(entry));
row.inflateViews();
@@ -823,7 +837,7 @@ public class NotificationEntryManager implements Dumpable, NotificationInflater.
mNotificationData.getImportance(key));
mPendingNotifications.put(key, shadeEntry);
- mGroupManager.onPendingEntryAdded(shadeEntry);
+ mGroupAlertTransferHelper.onPendingEntryAdded(shadeEntry);
}
@VisibleForTesting
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 b7bdc2eb5238..b6d99b245466 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
@@ -464,7 +464,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
- * Inflate views based off the inflation flags set. Inflation happens asynchronously.
+ * Inflate views based off the inflation flags set. Inflation happens asynchronously.
*/
public void inflateViews() {
mNotificationInflater.inflateNotificationViews();
@@ -511,6 +511,16 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
+ * Whether or not a content view should be inflated.
+ *
+ * @param flag the flag corresponding to the content view
+ * @return true if the flag is set, false otherwise
+ */
+ public boolean isInflationFlagSet(@InflationFlag int flag) {
+ return mNotificationInflater.isInflationFlagSet(flag);
+ }
+
+ /**
* Caches whether or not this row contains a system notification. Note, this is only cached
* once per notification as the packageInfo can't technically change for a notification row.
*/
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 e1c2f7359ce3..7086025836cb 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
@@ -100,17 +100,13 @@ public class NotificationInflater {
public static final int FLAG_CONTENT_VIEW_ALL = ~0;
- // TODO: Heads up and ambient are always inflated as a temporary workaround.
- // See http://b/117933032 and http://b/117894786
/**
* 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_HEADS_UP
- | FLAG_CONTENT_VIEW_AMBIENT;
+ | FLAG_CONTENT_VIEW_EXPANDED;
/**
* The set of content views to inflate.
@@ -201,11 +197,12 @@ public class NotificationInflater {
}
/**
- * Add flags for which content views should be inflated in addition to those already set.
+ * Convenience method for setting multiple flags at once.
*
* @param flags a set of {@link InflationFlag} corresponding to content views that should be
* inflated
*/
+ @VisibleForTesting
public void addInflationFlags(@InflationFlag int flags) {
mInflationFlags |= flags;
}
@@ -216,13 +213,12 @@ public class NotificationInflater {
* @param flag the {@link InflationFlag} corresponding to the view
* @return true if the flag is set and view will be inflated, false o/w
*/
- @VisibleForTesting
public boolean isInflationFlagSet(@InflationFlag int flag) {
return ((mInflationFlags & flag) != 0);
}
/**
- * Inflate all views of this notification on a background thread. This is asynchronous and will
+ * Inflate views for set flags on a background thread. This is asynchronous and will
* notify the callback once it's finished.
*/
public void inflateNotificationViews() {
@@ -234,7 +230,7 @@ public class NotificationInflater {
* 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 inflated. Should be a subset of
+ * @param reInflateFlags flags which views should be inflated. Should be a subset of
* {@link NotificationInflater#mInflationFlags} as only those will be
* inflated/reinflated.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7bee1b8a24d7..5153b119bba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -4540,7 +4540,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements ScrollAd
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setGroupManager(NotificationGroupManager groupManager) {
this.mGroupManager = groupManager;
- mGroupManager.setOnGroupChangeListener(mOnGroupChangeListener);
+ mGroupManager.addOnGroupChangeListener(mOnGroupChangeListener);
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
new file mode 100644
index 000000000000..c74514e05b20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.NonNull;
+import android.app.Notification;
+import android.os.SystemClock;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.statusbar.AlertingNotificationManager;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
+import com.android.systemui.statusbar.InflationTask;
+import com.android.systemui.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateController.StateListener;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.AsyncInflationTask;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
+import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
+import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Objects;
+
+/**
+ * A helper class dealing with the alert interactions between {@link NotificationGroupManager},
+ * {@link HeadsUpManager}, {@link AmbientPulseManager}. In particular, this class deals with keeping
+ * the correct notification in a group alerting based off the group suppression.
+ */
+public class NotificationGroupAlertTransferHelper implements OnGroupChangeListener,
+ OnHeadsUpChangedListener, OnAmbientChangedListener, StateListener {
+
+ private static final long ALERT_TRANSFER_TIMEOUT = 300;
+
+ /**
+ * The list of entries containing group alert metadata for each group. Keyed by group key.
+ */
+ private final ArrayMap<String, GroupAlertEntry> mGroupAlertEntries = new ArrayMap<>();
+
+ /**
+ * The list of entries currently inflating that should alert after inflation. Keyed by
+ * notification key.
+ */
+ private final ArrayMap<String, PendingAlertInfo> mPendingAlerts = new ArrayMap<>();
+
+ private HeadsUpManager mHeadsUpManager;
+ private final AmbientPulseManager mAmbientPulseManager =
+ Dependency.get(AmbientPulseManager.class);
+ private final NotificationGroupManager mGroupManager =
+ Dependency.get(NotificationGroupManager.class);
+
+ // TODO(b/119637830): It would be good if GroupManager already had all pending notifications as
+ // normal children (i.e. add notifications to GroupManager before inflation) so that we don't
+ // have to have this dependency. We'd also have to worry less about the suppression not being up
+ // to date.
+ /**
+ * Notifications that are currently inflating for the first time. Used to remove an incorrectly
+ * alerting notification faster.
+ */
+ private HashMap<String, Entry> mPendingNotifications;
+
+ private boolean mIsDozing;
+
+ public NotificationGroupAlertTransferHelper() {
+ Dependency.get(StatusBarStateController.class).addListener(this);
+ }
+
+ /**
+ * Whether or not a notification has transferred its alert state to the notification and
+ * the notification should alert after inflating.
+ *
+ * @param entry notification to check
+ * @return true if the entry was transferred to and should inflate + alert
+ */
+ public boolean isAlertTransferPending(@NonNull Entry entry) {
+ PendingAlertInfo alertInfo = mPendingAlerts.get(entry.key);
+ return alertInfo != null && alertInfo.isStillValid();
+ }
+
+ /**
+ * Removes any alerts pending on this entry. Note that this will not stop any inflation tasks
+ * started by a transfer, so this should only be used as clean-up for when inflation is stopped
+ * and the pending alert no longer needs to happen.
+ *
+ * @param key notification key that may have info that needs to be cleaned up
+ */
+ public void cleanUpPendingAlertInfo(@NonNull String key) {
+ mPendingAlerts.remove(key);
+ }
+
+ public void setHeadsUpManager(HeadsUpManager headsUpManager) {
+ mHeadsUpManager = headsUpManager;
+ }
+
+ public void setPendingEntries(HashMap<String, Entry> pendingNotifications) {
+ mPendingNotifications = pendingNotifications;
+ }
+
+ @Override
+ public void onStateChanged(int newState) {}
+
+ @Override
+ public void onDozingChanged(boolean isDozing) {
+ if (mIsDozing != isDozing) {
+ for (GroupAlertEntry groupAlertEntry : mGroupAlertEntries.values()) {
+ groupAlertEntry.mLastAlertTransferTime = 0;
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
+ }
+ }
+ mIsDozing = isDozing;
+ }
+
+ @Override
+ public void onGroupCreated(NotificationGroup group, String groupKey) {
+ mGroupAlertEntries.put(groupKey, new GroupAlertEntry(group));
+ }
+
+ @Override
+ public void onGroupRemoved(NotificationGroup group, String groupKey) {
+ mGroupAlertEntries.remove(groupKey);
+ }
+
+ @Override
+ public void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {
+ AlertingNotificationManager alertManager = getActiveAlertManager();
+ if (suppressed) {
+ if (alertManager.isAlerting(group.summary.key)) {
+ handleSuppressedSummaryAlerted(group.summary, alertManager);
+ }
+ } else {
+ // Group summary can be null if we are no longer suppressed because the summary was
+ // removed. In that case, we don't need to alert the summary.
+ if (group.summary == null) {
+ return;
+ }
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(mGroupManager.getGroupKey(
+ group.summary.notification));
+ // Group is no longer suppressed. We should check if we need to transfer the alert
+ // back to the summary now that it's no longer suppressed.
+ if (groupAlertEntry.mAlertSummaryOnNextAddition) {
+ if (!alertManager.isAlerting(group.summary.key)) {
+ alertNotificationWhenPossible(group.summary, alertManager);
+ }
+ groupAlertEntry.mAlertSummaryOnNextAddition = false;
+ } else {
+ checkShouldTransferBack(groupAlertEntry);
+ }
+ }
+ }
+
+ @Override
+ public void onAmbientStateChanged(Entry entry, boolean isAmbient) {
+ onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+ }
+
+ @Override
+ public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) {
+ onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ }
+
+ private void onAlertStateChanged(Entry entry, boolean isAlerting,
+ AlertingNotificationManager alertManager) {
+ if (isAlerting && mGroupManager.isSummaryOfSuppressedGroup(entry.notification)) {
+ handleSuppressedSummaryAlerted(entry, alertManager);
+ }
+ }
+
+ /**
+ * Called when the entry's reinflation has finished. If there is an alert pending, we then
+ * show the alert.
+ *
+ * @param entry entry whose inflation has finished
+ */
+ public void onInflationFinished(@NonNull Entry entry) {
+ PendingAlertInfo alertInfo = mPendingAlerts.remove(entry.key);
+ if (alertInfo != null) {
+ if (alertInfo.isStillValid()) {
+ alertNotificationWhenPossible(entry, getActiveAlertManager());
+ } else {
+ // The transfer is no longer valid. Free the content.
+ entry.row.freeContentViewWhenSafe(alertInfo.mAlertManager.getContentFlag());
+ }
+ }
+ }
+
+ /**
+ * Called when a new notification has been posted but is not inflated yet. We use this to see
+ * as early as we can if we need to abort a transfer.
+ *
+ * @param entry entry that has been added
+ */
+ public void onPendingEntryAdded(@NonNull Entry entry) {
+ String groupKey = mGroupManager.getGroupKey(entry.notification);
+ GroupAlertEntry groupAlertEntry = mGroupAlertEntries.get(groupKey);
+ if (groupAlertEntry != null) {
+ checkShouldTransferBack(groupAlertEntry);
+ }
+ }
+
+ /**
+ * Gets the number of new notifications pending inflation that will be added to the group
+ * but currently aren't and should not alert.
+ *
+ * @param group group to check
+ * @return the number of new notifications that will be added to the group
+ */
+ private int getPendingChildrenNotAlerting(@NonNull NotificationGroup group) {
+ if (mPendingNotifications == null) {
+ return 0;
+ }
+ int number = 0;
+ Collection<Entry> values = mPendingNotifications.values();
+ for (Entry entry : values) {
+ if (isPendingNotificationInGroup(entry, group) && onlySummaryAlerts(entry)) {
+ number++;
+ }
+ }
+ return number;
+ }
+
+ /**
+ * Checks if the pending inflations will add children to this group.
+ *
+ * @param group group to check
+ * @return true if a pending notification will add to this group
+ */
+ private boolean pendingInflationsWillAddChildren(@NonNull NotificationGroup group) {
+ if (mPendingNotifications == null) {
+ return false;
+ }
+ Collection<Entry> values = mPendingNotifications.values();
+ for (Entry entry : values) {
+ if (isPendingNotificationInGroup(entry, group)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if a new pending notification will be added to the group.
+ *
+ * @param entry pending notification
+ * @param group group to check
+ * @return true if the notification will add to the group, false o/w
+ */
+ private boolean isPendingNotificationInGroup(@NonNull Entry entry,
+ @NonNull NotificationGroup group) {
+ String groupKey = mGroupManager.getGroupKey(group.summary.notification);
+ return mGroupManager.isGroupChild(entry.notification)
+ && Objects.equals(mGroupManager.getGroupKey(entry.notification), groupKey)
+ && !group.children.containsKey(entry.key);
+ }
+
+ /**
+ * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
+ * summary should for all intents and purposes be invisible to the user and as a result should
+ * not alert. When this is the case, it is our responsibility to pass the alert to the
+ * appropriate child which will be the representative notification alerting for the group.
+ *
+ * @param summary the summary that is suppressed and alerting
+ * @param alertManager the alert manager that manages the alerting summary
+ */
+ private void handleSuppressedSummaryAlerted(@NonNull Entry summary,
+ @NonNull AlertingNotificationManager alertManager) {
+ StatusBarNotification sbn = summary.notification;
+ GroupAlertEntry groupAlertEntry =
+ mGroupAlertEntries.get(mGroupManager.getGroupKey(sbn));
+ if (!mGroupManager.isSummaryOfSuppressedGroup(summary.notification)
+ || !alertManager.isAlerting(sbn.getKey())
+ || groupAlertEntry == null) {
+ return;
+ }
+
+ if (pendingInflationsWillAddChildren(groupAlertEntry.mGroup)) {
+ // New children will actually be added to this group, let's not transfer the alert.
+ return;
+ }
+
+ Entry child = mGroupManager.getLogicalChildren(summary.notification).iterator().next();
+ if (child != null) {
+ if (child.row.keepInParent()
+ || child.row.isRemoved()
+ || child.row.isDismissed()) {
+ // The notification is actually already removed. No need to alert it.
+ return;
+ }
+ if (!alertManager.isAlerting(child.key) && onlySummaryAlerts(summary)) {
+ groupAlertEntry.mLastAlertTransferTime = SystemClock.elapsedRealtime();
+ }
+ transferAlertState(summary, child, alertManager);
+ }
+ }
+
+ /**
+ * Transfers the alert state one entry to another. We remove the alert from the first entry
+ * immediately to have the incorrect one up as short as possible. The second should alert
+ * when possible.
+ *
+ * @param fromEntry entry to transfer alert from
+ * @param toEntry entry to transfer to
+ * @param alertManager alert manager for the alert type
+ */
+ private void transferAlertState(@NonNull Entry fromEntry, @NonNull Entry toEntry,
+ @NonNull AlertingNotificationManager alertManager) {
+ alertManager.removeNotification(fromEntry.key, true /* releaseImmediately */);
+ alertNotificationWhenPossible(toEntry, alertManager);
+ }
+
+ /**
+ * Determines if we need to transfer the alert back to the summary from the child and does
+ * so if needed.
+ *
+ * This can happen since notification groups are not delivered as a whole unit and it is
+ * possible we erroneously transfer the alert from the summary to the child even though
+ * more children are coming. Thus, if a child is added within a certain timeframe after we
+ * transfer, we back out and alert the summary again.
+ *
+ * @param groupAlertEntry group alert entry to check
+ */
+ private void checkShouldTransferBack(@NonNull GroupAlertEntry groupAlertEntry) {
+ if (SystemClock.elapsedRealtime() - groupAlertEntry.mLastAlertTransferTime
+ < ALERT_TRANSFER_TIMEOUT) {
+ Entry summary = groupAlertEntry.mGroup.summary;
+ AlertingNotificationManager alertManager = getActiveAlertManager();
+
+ if (!onlySummaryAlerts(summary)) {
+ return;
+ }
+ ArrayList<Entry> children = mGroupManager.getLogicalChildren(summary.notification);
+ int numChildren = children.size();
+ int numPendingChildren = getPendingChildrenNotAlerting(groupAlertEntry.mGroup);
+ numChildren += numPendingChildren;
+ if (numChildren <= 1) {
+ return;
+ }
+ boolean releasedChild = false;
+ for (int i = 0; i < children.size(); i++) {
+ Entry entry = children.get(i);
+ if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
+ releasedChild = true;
+ alertManager.removeNotification(entry.key, true /* releaseImmediately */);
+ }
+ if (mPendingAlerts.containsKey(entry.key)) {
+ // This is the child that would've been removed if it was inflated.
+ releasedChild = true;
+ mPendingAlerts.get(entry.key).mAbortOnInflation = true;
+ }
+ }
+ if (releasedChild && !alertManager.isAlerting(summary.key)) {
+ boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
+ if (notifyImmediately) {
+ alertNotificationWhenPossible(summary, alertManager);
+ } else {
+ // Should wait until the pending child inflates before alerting.
+ groupAlertEntry.mAlertSummaryOnNextAddition = true;
+ }
+ groupAlertEntry.mLastAlertTransferTime = 0;
+ }
+ }
+ }
+
+ /**
+ * Tries to alert the notification. If its content view is not inflated, we inflate and continue
+ * when the entry finishes inflating the view.
+ *
+ * @param entry entry to show
+ * @param alertManager alert manager for the alert type
+ */
+ private void alertNotificationWhenPossible(@NonNull Entry entry,
+ @NonNull AlertingNotificationManager alertManager) {
+ @InflationFlag int contentFlag = alertManager.getContentFlag();
+ if (!entry.row.isInflationFlagSet(contentFlag)) {
+ mPendingAlerts.put(entry.key, new PendingAlertInfo(entry, alertManager));
+ entry.row.updateInflationFlag(contentFlag, true /* shouldInflate */);
+ entry.row.inflateViews();
+ return;
+ }
+ if (alertManager.isAlerting(entry.key)) {
+ alertManager.updateNotification(entry.key, true /* alert */);
+ } else {
+ alertManager.showNotification(entry);
+ }
+ }
+
+ private AlertingNotificationManager getActiveAlertManager() {
+ return mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
+ }
+
+ private boolean onlySummaryAlerts(Entry entry) {
+ return entry.notification.getNotification().getGroupAlertBehavior()
+ == Notification.GROUP_ALERT_SUMMARY;
+ }
+
+ /**
+ * Information about a pending alert used to determine if the alert is still needed when
+ * inflation completes.
+ */
+ private class PendingAlertInfo {
+ /**
+ * The alert manager when the transfer is initiated.
+ */
+ final AlertingNotificationManager mAlertManager;
+
+ /**
+ * The original notification when the transfer is initiated. This is used to determine if
+ * the transfer is still valid if the notification is updated.
+ */
+ final StatusBarNotification mOriginalNotification;
+ final Entry mEntry;
+
+ /**
+ * The notification is still pending inflation but we've decided that we no longer need
+ * the content view (e.g. suppression might have changed and we decided we need to transfer
+ * back). However, there is no way to abort just this inflation if other inflation requests
+ * have started (see {@link AsyncInflationTask#supersedeTask(InflationTask)}). So instead
+ * we just flag it as aborted and free when it's inflated.
+ */
+ boolean mAbortOnInflation;
+
+ PendingAlertInfo(Entry entry, AlertingNotificationManager alertManager) {
+ mOriginalNotification = entry.notification;
+ mEntry = entry;
+ mAlertManager = alertManager;
+ }
+
+ /**
+ * Whether or not the pending alert is still valid and should still alert after inflation.
+ *
+ * @return true if the pending alert should still occur, false o/w
+ */
+ private boolean isStillValid() {
+ if (mAbortOnInflation) {
+ // Notification is aborted due to the transfer being explicitly cancelled
+ return false;
+ }
+ if (mAlertManager != getActiveAlertManager()) {
+ // Alert manager has changed
+ return false;
+ }
+ if (mEntry.notification.getGroupKey() != mOriginalNotification.getGroupKey()) {
+ // Groups have changed
+ return false;
+ }
+ if (mEntry.notification.getNotification().isGroupSummary()
+ != mOriginalNotification.getNotification().isGroupSummary()) {
+ // Notification has changed from group summary to not or vice versa
+ return false;
+ }
+ return true;
+ }
+ }
+
+ /**
+ * Contains alert metadata for the notification group used to determine when/how the alert
+ * should be transferred.
+ */
+ private static class GroupAlertEntry {
+ /**
+ * The time when the last alert transfer from summary to child happened.
+ */
+ long mLastAlertTransferTime;
+ boolean mAlertSummaryOnNextAddition;
+ final NotificationGroup mGroup;
+
+ GroupAlertEntry(NotificationGroup group) {
+ this.mGroup = group;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index 6b12dd9519e4..8ceabf809df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -16,17 +16,12 @@
package com.android.systemui.statusbar.phone;
-import android.app.Notification;
-import android.os.SystemClock;
+import android.annotation.Nullable;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.Log;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.Dependency;
-import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.AmbientPulseManager.OnAmbientChangedListener;
import com.android.systemui.statusbar.StatusBarState;
@@ -40,9 +35,7 @@ import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
@@ -53,23 +46,25 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
OnAmbientChangedListener, StateListener {
private static final String TAG = "NotificationGroupManager";
- private static final long ALERT_TRANSFER_TIMEOUT = 300;
private final HashMap<String, NotificationGroup> mGroupMap = new HashMap<>();
- private OnGroupChangeListener mListener;
+ private final ArraySet<OnGroupChangeListener> mListeners = new ArraySet<>();
private int mBarState = -1;
private HashMap<String, StatusBarNotification> mIsolatedEntries = new HashMap<>();
private HeadsUpManager mHeadsUpManager;
private AmbientPulseManager mAmbientPulseManager = Dependency.get(AmbientPulseManager.class);
- private boolean mIsDozing;
private boolean mIsUpdatingUnchangedGroup;
- private HashMap<String, NotificationData.Entry> mPendingNotifications;
public NotificationGroupManager() {
Dependency.get(StatusBarStateController.class).addListener(this);
}
- public void setOnGroupChangeListener(OnGroupChangeListener listener) {
- mListener = listener;
+ /**
+ * Add a listener for changes to groups.
+ *
+ * @param listener listener to add
+ */
+ public void addOnGroupChangeListener(OnGroupChangeListener listener) {
+ mListeners.add(listener);
}
public boolean isGroupExpanded(StatusBarNotification sbn) {
@@ -91,7 +86,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
private void setGroupExpanded(NotificationGroup group, boolean expanded) {
group.expanded = expanded;
if (group.summary != null) {
- mListener.onGroupExpansionChanged(group.summary.row, expanded);
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupExpansionChanged(group.summary.row,
+ expanded);
+ }
}
}
@@ -127,6 +125,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
if (group.children.isEmpty()) {
if (group.summary == null) {
mGroupMap.remove(groupKey);
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupRemoved(group, groupKey);
+ }
}
}
}
@@ -142,6 +143,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
if (group == null) {
group = new NotificationGroup();
mGroupMap.put(groupKey, group);
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupCreated(group, groupKey);
+ }
}
if (isGroupChild) {
NotificationData.Entry existing = group.children.get(added.key);
@@ -166,125 +170,11 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
for (NotificationData.Entry child : childrenCopy) {
onEntryBecomingChild(child);
}
- mListener.onGroupCreatedFromChildren(group);
- }
- }
- cleanUpAlertStatesOnAdd(group, false /* addIsPending */);
- }
-
- public void onPendingEntryAdded(NotificationData.Entry shadeEntry) {
- String groupKey = getGroupKey(shadeEntry.notification);
- NotificationGroup group = mGroupMap.get(groupKey);
- if (group != null) {
- cleanUpAlertStatesOnAdd(group, true /* addIsPending */);
- }
- }
-
- /**
- * Set whether or not the device is dozing. This allows the group manager to reset some
- * specific alert state logic based off when the state changes.
- * @param isDozing if the device is dozing.
- */
- @VisibleForTesting
- public void setDozing(boolean isDozing) {
- if (mIsDozing != isDozing) {
- for (NotificationGroup group : mGroupMap.values()) {
- group.lastAlertTransfer = 0;
- group.alertSummaryOnNextAddition = false;
- }
- }
- mIsDozing = isDozing;
- }
-
- /**
- * Clean up the alert states when a new child was added.
- * @param group The group where a view was added or will be added.
- * @param addIsPending True if is the addition still pending or false has it already been added.
- */
- private void cleanUpAlertStatesOnAdd(NotificationGroup group, boolean addIsPending) {
-
- AlertingNotificationManager alertManager =
- mIsDozing ? mAmbientPulseManager : mHeadsUpManager;
- if (!addIsPending && group.alertSummaryOnNextAddition) {
- if (!alertManager.isAlerting(group.summary.key)) {
- alertManager.showNotification(group.summary);
- }
- group.alertSummaryOnNextAddition = false;
- }
- // Because notification groups are not delivered as a whole unit, it may happen that a
- // group child gets added quite a bit after the summary got posted. Our guidance is, that
- // apps should always post the group summary as well and we'll hide it for them if the child
- // is the only child in a group. Because of this, we also have to transfer alert to the
- // child, otherwise the invisible summary would be alerted.
- // This transfer to the child is not always correct in case the app has just posted another
- // child in addition to the existing one, but it hasn't arrived in systemUI yet. In such
- // a scenario we would transfer the alert to the old child and the wrong notification
- // would be alerted. In order to avoid this, we'll recover from this issue and alert the
- // summary again instead of the old child if it's within a certain timeout.
- if (SystemClock.elapsedRealtime() - group.lastAlertTransfer < ALERT_TRANSFER_TIMEOUT) {
- if (!onlySummaryAlerts(group.summary)) {
- return;
- }
- int numChildren = group.children.size();
- NotificationData.Entry isolatedChild = getIsolatedChild(getGroupKey(
- group.summary.notification));
- int numPendingChildren = getPendingChildrenNotAlerting(group);
- numChildren += numPendingChildren;
- if (isolatedChild != null) {
- numChildren++;
- }
- if (numChildren <= 1) {
- return;
- }
- boolean releasedChild = false;
- ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
- int size = children.size();
- for (int i = 0; i < size; i++) {
- NotificationData.Entry entry = children.get(i);
- if (onlySummaryAlerts(entry) && alertManager.isAlerting(entry.key)) {
- releasedChild = true;
- alertManager.removeNotification(entry.key, true /* releaseImmediately */);
- }
- }
- if (isolatedChild != null && onlySummaryAlerts(isolatedChild)
- && alertManager.isAlerting(isolatedChild.key)) {
- releasedChild = true;
- alertManager.removeNotification(isolatedChild.key, true /* releaseImmediately */);
- }
- if (releasedChild && !alertManager.isAlerting(group.summary.key)) {
- boolean notifyImmediately = (numChildren - numPendingChildren) > 1;
- if (notifyImmediately) {
- alertManager.showNotification(group.summary);
- } else {
- group.alertSummaryOnNextAddition = true;
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupCreatedFromChildren(group);
}
- group.lastAlertTransfer = 0;
- }
- }
- }
-
- private int getPendingChildrenNotAlerting(NotificationGroup group) {
- if (mPendingNotifications == null) {
- return 0;
- }
- int number = 0;
- String groupKey = getGroupKey(group.summary.notification);
- Collection<NotificationData.Entry> values = mPendingNotifications.values();
- for (NotificationData.Entry entry : values) {
- if (!isGroupChild(entry.notification)) {
- continue;
- }
- if (!Objects.equals(getGroupKey(entry.notification), groupKey)) {
- continue;
- }
- if (group.children.containsKey(entry.key)) {
- continue;
- }
- if (onlySummaryAlerts(entry)) {
- number++;
}
}
- return number;
}
private void onEntryBecomingChild(NotificationData.Entry entry) {
@@ -304,16 +194,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
&& group.summary.notification.getNotification().isGroupSummary()
&& hasIsolatedChildren(group)));
if (prevSuppressed != group.suppressed) {
- if (group.suppressed) {
- if (mHeadsUpManager.isAlerting(group.summary.key)) {
- handleSuppressedSummaryAlerted(group.summary, mHeadsUpManager);
- } else if (mAmbientPulseManager.isAlerting(group.summary.key)) {
- handleSuppressedSummaryAlerted(group.summary, mAmbientPulseManager);
+ for (OnGroupChangeListener listener : mListeners) {
+ if (!mIsUpdatingUnchangedGroup) {
+ listener.onGroupSuppressionChanged(group, group.suppressed);
+ listener.onGroupsChanged();
}
}
- if (!mIsUpdatingUnchangedGroup && mListener != null) {
- mListener.onGroupsChanged();
- }
}
}
@@ -462,8 +348,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
* but the logical summary, i.e when a child is isolated, it still returns the summary as if
* it wasn't isolated.
*/
- public ExpandableNotificationRow getLogicalGroupSummary(
- StatusBarNotification sbn) {
+ public ExpandableNotificationRow getLogicalGroupSummary(StatusBarNotification sbn) {
return getGroupSummary(sbn.getGroupKey());
}
@@ -475,6 +360,39 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
: group.summary.row;
}
+ /**
+ * Get the children that are logically in the summary's group, whether or not they are isolated.
+ *
+ * @param summary summary of a group
+ * @return list of the children
+ */
+ public ArrayList<NotificationData.Entry> getLogicalChildren(StatusBarNotification summary) {
+ NotificationGroup group = mGroupMap.get(summary.getGroupKey());
+ if (group == null) {
+ return null;
+ }
+ ArrayList<NotificationData.Entry> children = new ArrayList<>(group.children.values());
+ NotificationData.Entry isolatedChild = getIsolatedChild(summary.getGroupKey());
+ if (isolatedChild != null) {
+ children.add(isolatedChild);
+ }
+ return children;
+ }
+
+ /**
+ * Get the group key. May differ from the one in the notification due to the notification
+ * being temporarily isolated.
+ *
+ * @param sbn notification to check
+ * @return the key of the notification
+ */
+ public String getGroupKey(StatusBarNotification sbn) {
+ if (isIsolated(sbn)) {
+ return sbn.getKey();
+ }
+ return sbn.getGroupKey();
+ }
+
/** @return group expansion state after toggling. */
public boolean toggleGroupExpansion(StatusBarNotification sbn) {
NotificationGroup group = mGroupMap.get(getGroupKey(sbn));
@@ -489,27 +407,32 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
return mIsolatedEntries.containsKey(sbn.getKey());
}
- private boolean isGroupSummary(StatusBarNotification sbn) {
+ /**
+ * Whether a notification is visually a group summary.
+ *
+ * @param sbn notification to check
+ * @return true if it is visually a group summary
+ */
+ public boolean isGroupSummary(StatusBarNotification sbn) {
if (isIsolated(sbn)) {
return true;
}
return sbn.getNotification().isGroupSummary();
}
- private boolean isGroupChild(StatusBarNotification sbn) {
+ /**
+ * Whether a notification is visually a group child.
+ *
+ * @param sbn notification to check
+ * @return true if it is visually a group child
+ */
+ public boolean isGroupChild(StatusBarNotification sbn) {
if (isIsolated(sbn)) {
return false;
}
return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
}
- private String getGroupKey(StatusBarNotification sbn) {
- if (isIsolated(sbn)) {
- return sbn.getKey();
- }
- return sbn.getGroupKey();
- }
-
@Override
public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
}
@@ -524,23 +447,18 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
@Override
public void onAmbientStateChanged(NotificationData.Entry entry, boolean isAmbient) {
- onAlertStateChanged(entry, isAmbient, mAmbientPulseManager);
+ onAlertStateChanged(entry, isAmbient);
}
@Override
public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) {
- onAlertStateChanged(entry, isHeadsUp, mHeadsUpManager);
+ onAlertStateChanged(entry, isHeadsUp);
}
- private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting,
- AlertingNotificationManager alertManager) {
- final StatusBarNotification sbn = entry.notification;
+ private void onAlertStateChanged(NotificationData.Entry entry, boolean isAlerting) {
if (isAlerting) {
if (shouldIsolate(entry)) {
isolateNotification(entry);
- } else if (sbn.getNotification().isGroupSummary()
- && isGroupSuppressed(sbn.getGroupKey())){
- handleSuppressedSummaryAlerted(entry, alertManager);
}
} else {
stopIsolatingNotification(entry);
@@ -548,100 +466,6 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
}
/**
- * Handles the scenario where a summary that has been suppressed is alerted. A suppressed
- * summary should for all intents and purposes be invisible to the user and as a result should
- * not alert. When this is the case, it is our responsibility to pass the alert to the
- * appropriate child which will be the representative notification alerting for the group.
- * @param summary the summary that is suppressed and alerting
- * @param alertManager the alert manager that manages the alerting summary
- */
- private void handleSuppressedSummaryAlerted(@NonNull NotificationData.Entry summary,
- @NonNull AlertingNotificationManager alertManager) {
- StatusBarNotification sbn = summary.notification;
- if (!isGroupSuppressed(sbn.getGroupKey())
- || !sbn.getNotification().isGroupSummary()
- || !alertManager.isAlerting(sbn.getKey())) {
- return;
- }
-
- // The parent of a suppressed group got alerted, lets alert the child!
- NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
-
- if (notificationGroup != null) {
- if (pendingInflationsWillAddChildren(notificationGroup)) {
- // New children will actually be added to this group, let's not transfer the alert.
- return;
- }
-
- Iterator<NotificationData.Entry> iterator
- = notificationGroup.children.values().iterator();
- NotificationData.Entry child = iterator.hasNext() ? iterator.next() : null;
- if (child == null) {
- child = getIsolatedChild(sbn.getGroupKey());
- }
- if (child != null) {
- if (child.row.keepInParent() || child.row.isRemoved() || child.row.isDismissed()) {
- // the notification is actually already removed, no need to do alert on it.
- return;
- }
- transferAlertStateToChild(summary, child, alertManager);
- }
- }
- }
-
- /**
- * Transfers the alert state from a given summary notification to the specified child. The
- * result is the child will now alert while the summary does not.
- *
- * @param summary the currently alerting summary notification
- * @param child the child that should receive the alert
- * @param alertManager the manager for the alert
- */
- private void transferAlertStateToChild(@NonNull NotificationData.Entry summary,
- @NonNull NotificationData.Entry child,
- @NonNull AlertingNotificationManager alertManager) {
- NotificationGroup notificationGroup = mGroupMap.get(summary.notification.getGroupKey());
- if (alertManager.isAlerting(child.key)) {
- alertManager.updateNotification(child.key, true /* alert */);
- } else {
- if (onlySummaryAlerts(summary)) {
- notificationGroup.lastAlertTransfer = SystemClock.elapsedRealtime();
- }
- alertManager.showNotification(child);
- }
- alertManager.removeNotification(summary.key, true /* releaseImmediately */);
- }
-
- private boolean onlySummaryAlerts(NotificationData.Entry entry) {
- return entry.notification.getNotification().getGroupAlertBehavior()
- == Notification.GROUP_ALERT_SUMMARY;
- }
-
- /**
- * Check if the pending inflations will add children to this group.
- * @param group The group to check.
- */
- private boolean pendingInflationsWillAddChildren(NotificationGroup group) {
- if (mPendingNotifications == null) {
- return false;
- }
- Collection<NotificationData.Entry> values = mPendingNotifications.values();
- String groupKey = getGroupKey(group.summary.notification);
- for (NotificationData.Entry entry : values) {
- if (!isGroupChild(entry.notification)) {
- continue;
- }
- if (!Objects.equals(getGroupKey(entry.notification), groupKey)) {
- continue;
- }
- if (!group.children.containsKey(entry.key)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Whether a notification that is normally part of a group should be temporarily isolated from
* the group and put in their own group visually. This generally happens when the notification
* is alerting.
@@ -656,10 +480,7 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
if (!sbn.isGroup() || sbn.getNotification().isGroupSummary()) {
return false;
}
- if (!mIsDozing && !mHeadsUpManager.isAlerting(entry.key)) {
- return false;
- }
- if (mIsDozing && !mAmbientPulseManager.isAlerting(entry.key)) {
+ if (!mHeadsUpManager.isAlerting(entry.key) && !mAmbientPulseManager.isAlerting(entry.key)) {
return false;
}
return (sbn.getNotification().fullScreenIntent != null
@@ -687,7 +508,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
// When the notification gets added afterwards it is already isolated and therefore
// it doesn't lead to an update.
updateSuppression(mGroupMap.get(entry.notification.getGroupKey()));
- mListener.onGroupsChanged();
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupsChanged();
+ }
}
/**
@@ -702,7 +525,9 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
onEntryRemovedInternal(entry, entry.notification);
mIsolatedEntries.remove(sbn.getKey());
onEntryAdded(entry);
- mListener.onGroupsChanged();
+ for (OnGroupChangeListener listener : mListeners) {
+ listener.onGroupsChanged();
+ }
}
}
@@ -729,20 +554,11 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
}
}
- public void setPendingEntries(HashMap<String, NotificationData.Entry> pendingNotifications) {
- mPendingNotifications = pendingNotifications;
- }
-
@Override
public void onStateChanged(int newState) {
setStatusBarState(newState);
}
- @Override
- public void onDozingChanged(boolean isDozing) {
- setDozing(isDozing);
- }
-
public static class NotificationGroup {
public final HashMap<String, NotificationData.Entry> children = new HashMap<>();
public NotificationData.Entry summary;
@@ -751,12 +567,6 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
* Is this notification group suppressed, i.e its summary is hidden
*/
public boolean suppressed;
- /**
- * The time when the last alert transfer from group to child happened, while the summary
- * has the flags to alert up on its own.
- */
- public long lastAlertTransfer;
- public boolean alertSummaryOnNextAddition;
@Override
public String toString() {
@@ -777,13 +587,39 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
}
public interface OnGroupChangeListener {
+
+ /**
+ * A new group has been created.
+ *
+ * @param group the group that was created
+ * @param groupKey the group's key
+ */
+ default void onGroupCreated(NotificationGroup group, String groupKey) {}
+
+ /**
+ * A group has been removed.
+ *
+ * @param group the group that was removed
+ * @param groupKey the group's key
+ */
+ default void onGroupRemoved(NotificationGroup group, String groupKey) {}
+
+ /**
+ * The suppression of a group has changed.
+ *
+ * @param group the group that has changed
+ * @param suppressed true if the group is now suppressed, false o/w
+ */
+ default void onGroupSuppressionChanged(NotificationGroup group, boolean suppressed) {}
+
/**
* The expansion of a group has changed.
*
* @param changedRow the row for which the expansion has changed, which is also the summary
* @param expanded a boolean indicating the new expanded state
*/
- void onGroupExpansionChanged(ExpandableNotificationRow changedRow, boolean expanded);
+ default void onGroupExpansionChanged(ExpandableNotificationRow changedRow,
+ boolean expanded) {}
/**
* A group of children just received a summary notification and should therefore become
@@ -791,12 +627,12 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener,
*
* @param group the group created
*/
- void onGroupCreatedFromChildren(NotificationGroup group);
+ default void onGroupCreatedFromChildren(NotificationGroup group) {}
/**
* The groups have changed. This can happen if the isolation of a child has changes or if a
* group became suppressed / unsuppressed
*/
- void onGroupsChanged();
+ default void onGroupsChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 8e6136692ea1..45e924fa4321 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -596,6 +596,7 @@ public class StatusBar extends SystemUI implements DemoMode,
@Override
public void start() {
mGroupManager = Dependency.get(NotificationGroupManager.class);
+ mGroupAlertTransferHelper = Dependency.get(NotificationGroupAlertTransferHelper.class);
mVisualStabilityManager = Dependency.get(VisualStabilityManager.class);
mNotificationLogger = Dependency.get(NotificationLogger.class);
mRemoteInputManager = Dependency.get(NotificationRemoteInputManager.class);
@@ -832,11 +833,14 @@ public class StatusBar extends SystemUI implements DemoMode,
mHeadsUpManager.addListener(this);
mHeadsUpManager.addListener(mNotificationPanel);
mHeadsUpManager.addListener(mGroupManager);
+ mHeadsUpManager.addListener(mGroupAlertTransferHelper);
mHeadsUpManager.addListener(mVisualStabilityManager);
mAmbientPulseManager.addListener(this);
mAmbientPulseManager.addListener(mGroupManager);
+ mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
mNotificationPanel.setHeadsUpManager(mHeadsUpManager);
mGroupManager.setHeadsUpManager(mHeadsUpManager);
+ mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
putComponent(HeadsUpManager.class, mHeadsUpManager);
@@ -4105,6 +4109,8 @@ public class StatusBar extends SystemUI implements DemoMode,
protected NotificationGroupManager mGroupManager;
+ protected NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
+
// for heads up notifications
protected HeadsUpManagerPhone mHeadsUpManager;
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 6b83b70b0ca5..fdab6168d05a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -33,6 +33,7 @@ import com.android.systemui.R;
import com.android.systemui.statusbar.AlertingNotificationManager;
import com.android.systemui.statusbar.notification.NotificationData;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.NotificationInflater.InflationFlag;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -136,6 +137,10 @@ public abstract class HeadsUpManager extends AlertingNotificationManager {
}
}
+ public @InflationFlag int getContentFlag() {
+ return FLAG_CONTENT_VIEW_HEADS_UP;
+ }
+
@Override
protected void onAlertEntryAdded(AlertEntry alertEntry) {
NotificationData.Entry entry = alertEntry.mEntry;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
index 8b41516044f2..f49c5b47deda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/AlertingNotificationManagerTest.java
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.notification.row.NotificationInflater.FLAG_CONTENT_VIEW_CONTRACTED;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -84,6 +86,11 @@ public class AlertingNotificationManagerTest extends SysuiTestCase {
@Override
protected void onAlertEntryRemoved(AlertEntry alertEntry) {}
+
+ @Override
+ public int getContentFlag() {
+ return FLAG_CONTENT_VIEW_CONTRACTED;
+ }
}
protected AlertingNotificationManager createAlertingNotificationManager() {
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 bf8eb6216d7d..2da72e7858c8 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
@@ -53,7 +53,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationChildrenCon
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -140,10 +139,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
verify(row).updateShelfIconColor();
}
- // TODO: Ignoring as a temporary workaround until heads up views can be safely freed.
- // See http://b/117933032
@Test
- @Ignore
public void testFreeContentViewWhenSafe() throws Exception {
ExpandableNotificationRow row = mNotificationTestHelper.createRow(FLAG_CONTENT_VIEW_ALL);
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 0d2d3451b90c..d6b706d73827 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
@@ -117,10 +117,7 @@ public class NotificationInflaterTest extends SysuiTestCase {
verify(mRow).onNotificationUpdated();
}
- // TODO: Ignoring as a temporary workaround until ambient views can be safely freed.
- // See http://b/117894786
@Test
- @Ignore
public void testInflationOnlyInflatesSetFlags() throws Exception {
mNotificationInflater.updateInflationFlag(FLAG_CONTENT_VIEW_HEADS_UP,
true /* shouldInflate */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
new file mode 100644
index 000000000000..96c57f2b7991
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelperTest.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.service.notification.StatusBarNotification;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.AmbientPulseManager;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.NotificationData.Entry;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.HashMap;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationGroupAlertTransferHelperTest extends SysuiTestCase {
+ @Rule
+ public MockitoRule rule = MockitoJUnit.rule();
+
+ private NotificationGroupAlertTransferHelper mGroupAlertTransferHelper;
+ private NotificationGroupManager mGroupManager;
+ private AmbientPulseManager mAmbientPulseManager;
+ private HeadsUpManager mHeadsUpManager;
+ private final HashMap<String, Entry> mPendingEntries = new HashMap<>();
+ private final NotificationGroupTestHelper mGroupTestHelper =
+ new NotificationGroupTestHelper(mContext);
+
+
+ @Before
+ public void setup() {
+ mAmbientPulseManager = new AmbientPulseManager(mContext);
+ mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
+ mHeadsUpManager = new HeadsUpManager(mContext) {};
+
+ mGroupManager = new NotificationGroupManager();
+ mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
+ mGroupManager.setHeadsUpManager(mHeadsUpManager);
+
+ mGroupAlertTransferHelper = new NotificationGroupAlertTransferHelper();
+ mGroupAlertTransferHelper.setHeadsUpManager(mHeadsUpManager);
+ mGroupAlertTransferHelper.setPendingEntries(mPendingEntries);
+
+ mGroupManager.addOnGroupChangeListener(mGroupAlertTransferHelper);
+ mHeadsUpManager.addListener(mGroupAlertTransferHelper);
+ mAmbientPulseManager.addListener(mGroupAlertTransferHelper);
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransfersToChild() {
+ Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ mHeadsUpManager.showNotification(summaryEntry);
+ Entry childEntry = mGroupTestHelper.createChildNotification();
+
+ // Summary will be suppressed because there is only one child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // A suppressed summary should transfer its alert state to the child.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mPendingEntries.put(childEntry2.key, childEntry2);
+ mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // The alert state should transfer back to the summary as there is now more than one
+ // child and the summary should no longer be suppressed.
+ assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpDoesntTransferBackOnDozingChanged() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry2 =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Set dozing to true.
+ mGroupAlertTransferHelper.onDozingChanged(true);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mPendingEntries.put(childEntry2.key, childEntry2);
+ mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // Dozing changed so no reason to re-alert summary.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransferDoesNotAlertChildIfUninflated() {
+ Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ mHeadsUpManager.showNotification(summaryEntry);
+ Entry childEntry = mGroupTestHelper.createChildNotification();
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Alert is immediately removed from summary, but we do not show child yet either as its
+ // content is not inflated.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ assertTrue(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransferAlertsChildOnInflation() {
+ Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
+ mHeadsUpManager.showNotification(summaryEntry);
+ Entry childEntry = mGroupTestHelper.createChildNotification();
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ mGroupAlertTransferHelper.onInflationFinished(childEntry);
+
+ // Alert is immediately removed from summary, and we show child as its content is inflated.
+ assertFalse(mHeadsUpManager.isAlerting(summaryEntry.key));
+ assertTrue(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testSuppressedSummaryHeadsUpTransferBackAbortsChildInflation() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ NotificationData.Entry childEntry2 =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Add second child notification so that summary is no longer suppressed.
+ mPendingEntries.put(childEntry2.key, childEntry2);
+ mGroupAlertTransferHelper.onPendingEntryAdded(childEntry2);
+ mGroupManager.onEntryAdded(childEntry2);
+
+ // Child entry finishes its inflation.
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(true);
+ mGroupAlertTransferHelper.onInflationFinished(childEntry);
+
+ verify(childEntry.row, times(1)).freeContentViewWhenSafe(mHeadsUpManager.getContentFlag());
+ assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
+ }
+
+ @Test
+ public void testCleanUpPendingAlertInfo() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ mGroupAlertTransferHelper.cleanUpPendingAlertInfo(childEntry.key);
+
+ assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
+ }
+
+ @Test
+ public void testUpdateGroupChangeDoesNotTransfer() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Notify that entry changed groups.
+ StatusBarNotification oldNotification = childEntry.notification;
+ StatusBarNotification newSbn = spy(childEntry.notification.clone());
+ doReturn("other_group").when(newSbn).getGroupKey();
+ childEntry.notification = newSbn;
+ mGroupManager.onEntryUpdated(childEntry, oldNotification);
+
+ assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
+ }
+
+ @Test
+ public void testUpdateChildToSummaryDoesNotTransfer() {
+ NotificationData.Entry summaryEntry =
+ mGroupTestHelper.createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
+ NotificationData.Entry childEntry =
+ mGroupTestHelper.createChildNotification(Notification.GROUP_ALERT_SUMMARY);
+ when(childEntry.row.isInflationFlagSet(mHeadsUpManager.getContentFlag())).thenReturn(false);
+ mHeadsUpManager.showNotification(summaryEntry);
+ // Trigger a transfer of alert state from summary to child.
+ mGroupManager.onEntryAdded(summaryEntry);
+ mGroupManager.onEntryAdded(childEntry);
+
+ // Update that child to a summary.
+ StatusBarNotification oldNotification = childEntry.notification;
+ childEntry.notification = mGroupTestHelper.createSummaryNotification(
+ Notification.GROUP_ALERT_SUMMARY).notification;
+ mGroupManager.onEntryUpdated(childEntry, oldNotification);
+
+ assertFalse(mGroupAlertTransferHelper.isAlertTransferPending(childEntry));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
index 464f74b3aa36..1483ae5f2a12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java
@@ -21,25 +21,15 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.verify;
-import android.app.ActivityManager;
-import android.app.Notification;
-import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.AmbientPulseManager;
import com.android.systemui.statusbar.notification.NotificationData;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import org.junit.Before;
@@ -58,11 +48,9 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
- private static final String TEST_CHANNEL_ID = "test_channel";
- private static final String TEST_GROUP_ID = "test_group";
- private static final String TEST_PACKAGE_NAME = "test_pkg";
private NotificationGroupManager mGroupManager;
- private int mId = 0;
+ private final NotificationGroupTestHelper mGroupTestHelper =
+ new NotificationGroupTestHelper(mContext);
@Mock HeadsUpManager mHeadsUpManager;
@Mock AmbientPulseManager mAmbientPulseManager;
@@ -77,13 +65,12 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
private void initializeGroupManager() {
mGroupManager = new NotificationGroupManager();
mGroupManager.setHeadsUpManager(mHeadsUpManager);
- mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class));
}
@Test
public void testIsOnlyChildInGroup() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
@@ -93,24 +80,24 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Test
public void testIsChildInGroupWithSummary() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.notification));
}
@Test
public void testIsSummaryOfGroupWithChildren() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification));
assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification));
@@ -118,11 +105,11 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Test
public void testRemoveChildFromGroupWithSummary() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
mGroupManager.onEntryRemoved(childEntry);
@@ -131,11 +118,11 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Test
public void testRemoveSummaryFromGroupWithSummary() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
mGroupManager.onEntryRemoved(summaryEntry);
@@ -145,11 +132,11 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Test
public void testHeadsUpEntryIsIsolated() {
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
when(mHeadsUpManager.isAlerting(childEntry.key)).thenReturn(true);
mGroupManager.onHeadsUpStateChanged(childEntry, true);
@@ -163,12 +150,11 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
@Test
public void testAmbientPulseEntryIsIsolated() {
- mGroupManager.setDozing(true);
- NotificationData.Entry childEntry = createChildNotification();
- NotificationData.Entry summaryEntry = createSummaryNotification();
+ NotificationData.Entry childEntry = mGroupTestHelper.createChildNotification();
+ NotificationData.Entry summaryEntry = mGroupTestHelper.createSummaryNotification();
mGroupManager.onEntryAdded(summaryEntry);
mGroupManager.onEntryAdded(childEntry);
- mGroupManager.onEntryAdded(createChildNotification());
+ mGroupManager.onEntryAdded(mGroupTestHelper.createChildNotification());
when(mAmbientPulseManager.isAlerting(childEntry.key)).thenReturn(true);
mGroupManager.onAmbientStateChanged(childEntry, true);
@@ -179,128 +165,4 @@ public class NotificationGroupManagerTest extends SysuiTestCase {
assertEquals(summaryEntry.row,
mGroupManager.getLogicalGroupSummary(childEntry.notification));
}
-
- @Test
- public void testSuppressedSummaryHeadsUpTransfersToChild() {
- NotificationData.Entry summaryEntry = createSummaryNotification();
- when(mHeadsUpManager.isAlerting(summaryEntry.key)).thenReturn(true);
- NotificationData.Entry childEntry = createChildNotification();
-
- // Summary will be suppressed because there is only one child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // A suppressed summary should transfer its heads up state to the child.
- verify(mHeadsUpManager, never()).showNotification(summaryEntry);
- verify(mHeadsUpManager).showNotification(childEntry);
- }
-
- @Test
- public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() {
- mHeadsUpManager = new HeadsUpManager(mContext) {};
- mGroupManager.setHeadsUpManager(mHeadsUpManager);
- NotificationData.Entry summaryEntry =
- createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
- createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry2 =
- createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mHeadsUpManager.showNotification(summaryEntry);
- // Trigger a transfer of heads up state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Add second child notification so that summary is no longer suppressed.
- mGroupManager.onEntryAdded(childEntry2);
-
- // The heads up state should transfer back to the summary as there is now more than one
- // child and the summary should no longer be suppressed.
- assertTrue(mHeadsUpManager.isAlerting(summaryEntry.key));
- assertFalse(mHeadsUpManager.isAlerting(childEntry.key));
- }
-
- @Test
- public void testSuppressedSummaryAmbientPulseTransfersToChild() {
- mGroupManager.setDozing(true);
- NotificationData.Entry summaryEntry = createSummaryNotification();
- when(mAmbientPulseManager.isAlerting(summaryEntry.key)).thenReturn(true);
- NotificationData.Entry childEntry = createChildNotification();
-
- // Summary will be suppressed because there is only one child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // A suppressed summary should transfer its ambient state to the child.
- verify(mAmbientPulseManager, never()).showNotification(summaryEntry);
- verify(mAmbientPulseManager).showNotification(childEntry);
- }
-
- @Test
- public void testSuppressedSummaryAmbientPulseTransfersToChildButBackAgain() {
- mGroupManager.setDozing(true);
- mAmbientPulseManager = new AmbientPulseManager(mContext);
- mDependency.injectTestDependency(AmbientPulseManager.class, mAmbientPulseManager);
- initializeGroupManager();
- NotificationData.Entry summaryEntry =
- createSummaryNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry =
- createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- NotificationData.Entry childEntry2 =
- createChildNotification(Notification.GROUP_ALERT_SUMMARY);
- mAmbientPulseManager.showNotification(summaryEntry);
- // Trigger a transfer of ambient state from summary to child.
- mGroupManager.onEntryAdded(summaryEntry);
- mGroupManager.onEntryAdded(childEntry);
-
- // Add second child notification so that summary is no longer suppressed.
- mGroupManager.onEntryAdded(childEntry2);
-
- // The ambient state should transfer back to the summary as there is now more than one
- // child and the summary should no longer be suppressed.
- assertTrue(mAmbientPulseManager.isAlerting(summaryEntry.key));
- assertFalse(mAmbientPulseManager.isAlerting(childEntry.key));
- }
-
- private NotificationData.Entry createSummaryNotification() {
- return createSummaryNotification(Notification.GROUP_ALERT_ALL);
- }
-
- private NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
- return createEntry(true, groupAlertBehavior);
- }
-
- private NotificationData.Entry createChildNotification() {
- return createChildNotification(Notification.GROUP_ALERT_ALL);
- }
-
- private NotificationData.Entry createChildNotification(int groupAlertBehavior) {
- return createEntry(false, groupAlertBehavior);
- }
-
- private NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
- Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
- .setContentTitle("Title")
- .setSmallIcon(R.drawable.ic_person)
- .setGroupAlertBehavior(groupAlertBehavior)
- .setGroupSummary(isSummary)
- .setGroup(TEST_GROUP_ID)
- .build();
- StatusBarNotification sbn = new StatusBarNotification(
- TEST_PACKAGE_NAME /* pkg */,
- TEST_PACKAGE_NAME,
- mId++,
- null /* tag */,
- 0, /* uid */
- 0 /* initialPid */,
- notif,
- new UserHandle(ActivityManager.getCurrentUser()),
- null /* overrideGroupKey */,
- 0 /* postTime */);
- NotificationData.Entry entry = new NotificationData.Entry(sbn);
- ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
- entry.row = row;
- when(row.getEntry()).thenReturn(entry);
- when(row.getStatusBarNotification()).thenReturn(sbn);
- return entry;
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
new file mode 100644
index 000000000000..01f44fd4d0cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupTestHelper.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.content.Context;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationData;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+
+/**
+ * Helper class for creating groups/summaries without having to inflate them.
+ */
+public final class NotificationGroupTestHelper {
+ private static final String TEST_CHANNEL_ID = "test_channel";
+ private static final String TEST_GROUP_ID = "test_group";
+ private static final String TEST_PACKAGE_NAME = "test_pkg";
+ private int mId = 0;
+ private final Context mContext;
+
+ public NotificationGroupTestHelper(Context context) {
+ mContext = context;
+ }
+
+ public NotificationData.Entry createSummaryNotification() {
+ return createSummaryNotification(Notification.GROUP_ALERT_ALL);
+ }
+
+ public NotificationData.Entry createSummaryNotification(int groupAlertBehavior) {
+ return createEntry(true, groupAlertBehavior);
+ }
+
+ public NotificationData.Entry createChildNotification() {
+ return createChildNotification(Notification.GROUP_ALERT_ALL);
+ }
+
+ public NotificationData.Entry createChildNotification(int groupAlertBehavior) {
+ return createEntry(false, groupAlertBehavior);
+ }
+
+ public NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) {
+ Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setContentTitle("Title")
+ .setSmallIcon(R.drawable.ic_person)
+ .setGroupAlertBehavior(groupAlertBehavior)
+ .setGroupSummary(isSummary)
+ .setGroup(TEST_GROUP_ID)
+ .build();
+ StatusBarNotification sbn = new StatusBarNotification(
+ TEST_PACKAGE_NAME /* pkg */,
+ TEST_PACKAGE_NAME,
+ mId++,
+ null /* tag */,
+ 0, /* uid */
+ 0 /* initialPid */,
+ notif,
+ new UserHandle(ActivityManager.getCurrentUser()),
+ null /* overrideGroupKey */,
+ 0 /* postTime */);
+ NotificationData.Entry entry = new NotificationData.Entry(sbn);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ entry.row = row;
+ when(row.getEntry()).thenReturn(entry);
+ when(row.getStatusBarNotification()).thenReturn(sbn);
+ when(row.isInflationFlagSet(anyInt())).thenReturn(true);
+ return entry;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 7dc858ff9308..e9e8eb785d1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -209,11 +209,11 @@ public class StatusBarTest extends SysuiTestCase {
mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
mEntryManager, mScrimController, mBiometricUnlockController,
mKeyguardViewMediator, mRemoteInputManager, mock(NotificationGroupManager.class),
- mock(FalsingManager.class), mock(StatusBarWindowController.class),
- mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
- mock(NotificationShelf.class), mLockscreenUserManager,
- mCommandQueue,
- mNotificationPresenter, mock(BubbleController.class));
+ mock(NotificationGroupAlertTransferHelper.class), mock(FalsingManager.class),
+ mock(StatusBarWindowController.class), mock(NotificationIconAreaController.class),
+ mock(DozeScrimController.class), mock(NotificationShelf.class),
+ mLockscreenUserManager, mCommandQueue, mNotificationPresenter,
+ mock(BubbleController.class));
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mContext.getComponents();
mStatusBar.putComponent(StatusBar.class, mStatusBar);
@@ -634,6 +634,7 @@ public class StatusBarTest extends SysuiTestCase {
KeyguardViewMediator keyguardViewMediator,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationGroupManager notificationGroupManager,
+ NotificationGroupAlertTransferHelper notificationGroupAlertTransferHelper,
FalsingManager falsingManager,
StatusBarWindowController statusBarWindowController,
NotificationIconAreaController notificationIconAreaController,
@@ -662,6 +663,7 @@ public class StatusBarTest extends SysuiTestCase {
mKeyguardViewMediator = keyguardViewMediator;
mRemoteInputManager = notificationRemoteInputManager;
mGroupManager = notificationGroupManager;
+ mGroupAlertTransferHelper = notificationGroupAlertTransferHelper;
mFalsingManager = falsingManager;
mStatusBarWindowController = statusBarWindowController;
mNotificationIconAreaController = notificationIconAreaController;