Merge "Refactor parts of BubbleController"

This commit is contained in:
Beverly Tai
2020-01-29 19:29:21 +00:00
committed by Android (Google) Code Review
6 changed files with 346 additions and 226 deletions

View File

@@ -190,6 +190,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
private boolean mInflateSynchronously;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
private final List<NotifCallback> mCallbacks = new ArrayList<>();
/**
* Listener to be notified when some states of the bubbles change.
*/
@@ -231,7 +234,35 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
* Called when the notification suppression state of a bubble changes.
*/
void onBubbleNotificationSuppressionChange(Bubble bubble);
}
/**
* Callback for when the BubbleController wants to interact with the notification pipeline to:
* - Remove a previously bubbled notification
* - Update the notification shade since bubbled notification should/shouldn't be showing
*/
public interface NotifCallback {
/**
* Called when the BubbleController wants to remove an entry that it was previously hiding
* from the shade. See {@link BubbleController#isBubbleNotificationSuppressedFromShade}.
*/
void removeNotification(NotificationEntry entry);
/**
* Called when a bubbled notification has changed whether it should be
* filtered from the shade.
*/
void invalidateNotificationFilter(String reason);
/**
* Called on a bubbled entry that has been removed when there are no longer
* bubbled entries in its group.
*
* Checks whether its group has any other (non-bubbled) children. If it doesn't,
* removes all remnants of the group's summary from the notification pipeline.
* TODO: (b/145659174) Only old pipeline needs this - delete post-migration.
*/
void maybeCancelSummary(NotificationEntry entry);
}
/**
@@ -332,26 +363,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
});
mNotificationEntryManager = entryManager;
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
mNotificationEntryManager.setNotificationRemoveInterceptor(mRemoveInterceptor);
mNotificationGroupManager = groupManager;
mNotificationGroupManager.addOnGroupChangeListener(
new NotificationGroupManager.OnGroupChangeListener() {
@Override
public void onGroupSuppressionChanged(
NotificationGroupManager.NotificationGroup group,
boolean suppressed) {
// More notifications could be added causing summary to no longer
// be suppressed -- in this case need to remove the key.
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null
&& mBubbleData.isSummarySuppressed(groupKey)) {
mBubbleData.removeSuppressedSummary(groupKey);
}
}
});
setupNEM();
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarStateListener = new StatusBarStateListener();
@@ -390,6 +403,104 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mBubbleIconFactory = new BubbleIconFactory(context);
}
/**
* See {@link NotifCallback}.
*/
public void addNotifCallback(NotifCallback callback) {
mCallbacks.add(callback);
}
private void setupNEM() {
mNotificationEntryManager.addNotificationEntryListener(
new NotificationEntryListener() {
@Override
public void onNotificationAdded(NotificationEntry entry) {
onEntryAdded(entry);
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
onEntryUpdated(entry);
}
@Override
public void onNotificationRankingUpdated(RankingMap rankingMap) {
onRankingUpdated(rankingMap);
}
});
mNotificationEntryManager.setNotificationRemoveInterceptor(
new NotificationRemoveInterceptor() {
@Override
public boolean onNotificationRemoveRequested(String key, int reason) {
NotificationEntry entry =
mNotificationEntryManager.getActiveNotificationUnfiltered(key);
return shouldInterceptDismissal(entry, reason);
}
});
mNotificationGroupManager.addOnGroupChangeListener(
new NotificationGroupManager.OnGroupChangeListener() {
@Override
public void onGroupSuppressionChanged(
NotificationGroupManager.NotificationGroup group,
boolean suppressed) {
// More notifications could be added causing summary to no longer
// be suppressed -- in this case need to remove the key.
final String groupKey = group.summary != null
? group.summary.getSbn().getGroupKey()
: null;
if (!suppressed && groupKey != null
&& mBubbleData.isSummarySuppressed(groupKey)) {
mBubbleData.removeSuppressedSummary(groupKey);
}
}
});
addNotifCallback(new NotifCallback() {
@Override
public void removeNotification(NotificationEntry entry) {
mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
UNDEFINED_DISMISS_REASON);
}
@Override
public void invalidateNotificationFilter(String reason) {
mNotificationEntryManager.updateNotifications(reason);
}
@Override
public void maybeCancelSummary(NotificationEntry entry) {
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = entry.getSbn().getGroup();
if (mBubbleData.isSummarySuppressed(groupKey)) {
mBubbleData.removeSuppressedSummary(entry.getSbn().getGroupKey());
final NotificationEntry summary =
mNotificationEntryManager.getActiveNotificationUnfiltered(
mBubbleData.getSummaryKey(groupKey));
mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
UNDEFINED_DISMISS_REASON);
}
// Check if summary should be removed from NoManGroup
NotificationEntry summary =
mNotificationGroupManager.getLogicalGroupSummary(entry.getSbn());
if (summary != null) {
ArrayList<NotificationEntry> summaryChildren =
mNotificationGroupManager.getLogicalChildren(summary.getSbn());
boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
if (!isSummaryThisNotif && (summaryChildren == null
|| summaryChildren.isEmpty())) {
mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
UNDEFINED_DISMISS_REASON);
}
}
}
});
}
/**
* Sets whether to perform inflation on the same thread as the caller. This method should only
* be used in tests, not in production.
@@ -562,13 +673,15 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
*
* False otherwise.
*/
public boolean isBubbleNotificationSuppressedFromShade(String key) {
public boolean isBubbleNotificationSuppressedFromShade(NotificationEntry entry) {
String key = entry.getKey();
boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
&& !mBubbleData.getBubbleWithKey(key).showInShade();
NotificationEntry entry = mNotificationEntryManager.getActiveNotificationUnfiltered(key);
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
String groupKey = entry.getSbn().getGroupKey();
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
boolean isSummary = key.equals(mBubbleData.getSummaryKey(groupKey));
return (isSummary && isSuppressedSummary) || isBubbleAndSuppressed;
}
@@ -702,160 +815,46 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
@SuppressWarnings("FieldCanBeLocal")
private final NotificationRemoveInterceptor mRemoveInterceptor =
new NotificationRemoveInterceptor() {
@Override
public boolean onNotificationRemoveRequested(String key, int reason) {
NotificationEntry entry =
mNotificationEntryManager.getActiveNotificationUnfiltered(key);
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
private void onEntryAdded(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(key));
boolean isSummary = entry != null
&& entry.getSbn().getNotification().isGroupSummary();
boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
&& bubbleChildren != null && !bubbleChildren.isEmpty();
if (!inBubbleData && !isSummaryOfBubbles) {
return false;
}
final boolean isClearAll = reason == REASON_CANCEL_ALL;
final boolean isUserDimiss = reason == REASON_CANCEL || reason == REASON_CLICK;
final boolean isAppCancel = reason == REASON_APP_CANCEL
|| reason == REASON_APP_CANCEL_ALL;
final boolean isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED;
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
if (isSummaryOfBubbles) {
return handleSummaryRemovalInterception(entry, userRemovedNotif);
}
// The bubble notification sticks around in the data as long as the bubble is
// not dismissed and the app hasn't cancelled the notification.
Bubble bubble = mBubbleData.getBubbleWithKey(key);
boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */, true /* animate */);
mNotificationEntryManager.updateNotifications(
"BubbleController.onNotificationRemoveRequested");
return true;
} else if (!userRemovedNotif && entry != null
&& !isUserCreatedBubble(bubble.getKey())) {
// This wasn't a user removal so we should remove the bubble as well
mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
return false;
}
return false;
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
mUserCreatedBubbles.add(entry.getKey());
}
};
private boolean handleSummaryRemovalInterception(NotificationEntry summary,
boolean userRemovedNotif) {
String groupKey = summary.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
if (userRemovedNotif) {
// If it's a user dismiss we mark the children to be hidden from the shade.
for (int i = 0; i < bubbleChildren.size(); i++) {
Bubble bubbleChild = bubbleChildren.get(i);
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */, true /* animate */);
}
// And since all children are removed, remove the summary.
mNotificationGroupManager.onEntryRemoved(summary);
// If the summary was auto-generated we don't need to keep that notification around
// because apps can't cancel it; so we only intercept & suppress real summaries.
boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
& FLAG_AUTOGROUP_SUMMARY) != 0;
if (!isAutogroupSummary) {
mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
summary.getKey());
// Tell shade to update for the suppression
mNotificationEntryManager.updateNotifications(
"BubbleController.handleSummaryRemovalInterception");
}
return !isAutogroupSummary;
} else {
// If it's not a user dismiss it's a cancel.
for (int i = 0; i < bubbleChildren.size(); i++) {
// First check if any of these are user-created (i.e. experimental bubbles)
if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
// Experimental bubble! Intercept the removal.
return true;
}
}
// Not an experimental bubble, safe to remove.
mBubbleData.removeSuppressedSummary(groupKey);
// Remove any associated bubble children with the summary.
for (int i = 0; i < bubbleChildren.size(); i++) {
Bubble bubbleChild = bubbleChildren.get(i);
mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
DISMISS_GROUP_CANCELLED);
}
return false;
updateBubble(entry);
}
}
@SuppressWarnings("FieldCanBeLocal")
private final NotificationEntryListener mEntryListener = new NotificationEntryListener() {
@Override
public void onNotificationAdded(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
private void onEntryUpdated(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
mUserCreatedBubbles.add(entry.getKey());
}
updateBubble(entry);
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
mUserCreatedBubbles.add(entry.getKey());
}
updateBubble(entry);
}
}
@Override
public void onPreEntryUpdated(NotificationEntry entry) {
boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
boolean userBlocked = mUserBlockedBubbles.contains(entry.getKey());
boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
mContext, entry, previouslyUserCreated, userBlocked);
boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
&& (canLaunchInActivityView(mContext, entry) || wasAdjusted);
if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
// It was previously a bubble but no longer a bubble -- lets remove it
removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
} else if (shouldBubble) {
if (wasAdjusted && !previouslyUserCreated) {
// Gotta treat the auto-bubbled / whitelisted packaged bubbles as usercreated
mUserCreatedBubbles.add(entry.getKey());
}
updateBubble(entry);
}
}
@Override
public void onNotificationRankingUpdated(RankingMap rankingMap) {
// Forward to BubbleData to block any bubbles which should no longer be shown
mBubbleData.notificationRankingUpdated(rankingMap);
}
};
private void onRankingUpdated(RankingMap rankingMap) {
// Forward to BubbleData to block any bubbles which should no longer be shown
mBubbleData.notificationRankingUpdated(rankingMap);
}
@SuppressWarnings("FieldCanBeLocal")
private final BubbleData.Listener mBubbleDataListener = new BubbleData.Listener() {
@@ -888,9 +887,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
if (reason != DISMISS_USER_CHANGED) {
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
&& !bubble.showInShade()) {
// The bubble is gone & the notification is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(
bubble.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
// The bubble is now gone & the notification is hidden from the shade, so
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
cb.removeNotification(bubble.getEntry());
}
} else {
// Update the flag for SysUI
bubble.getEntry().getSbn().getNotification().flags &= ~FLAG_BUBBLE;
@@ -905,32 +906,11 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
}
// Check if removed bubble has an associated suppressed group summary that needs
// to be removed now.
final String groupKey = bubble.getEntry().getSbn().getGroupKey();
if (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
// Time to actually remove the summary.
String notifKey = mBubbleData.getSummaryKey(groupKey);
mBubbleData.removeSuppressedSummary(groupKey);
NotificationEntry entry =
mNotificationEntryManager.getActiveNotificationUnfiltered(notifKey);
mNotificationEntryManager.performRemoveNotification(
entry.getSbn(), UNDEFINED_DISMISS_REASON);
}
// Check if summary should be removed from NoManGroup
NotificationEntry summary = mNotificationGroupManager.getLogicalGroupSummary(
bubble.getEntry().getSbn());
if (summary != null) {
ArrayList<NotificationEntry> summaryChildren =
mNotificationGroupManager.getLogicalChildren(summary.getSbn());
boolean isSummaryThisNotif = summary.getKey().equals(
bubble.getEntry().getKey());
if (!isSummaryThisNotif
&& (summaryChildren == null || summaryChildren.isEmpty())) {
mNotificationEntryManager.performRemoveNotification(
summary.getSbn(), UNDEFINED_DISMISS_REASON);
if (mBubbleData.getBubblesInGroup(groupKey).isEmpty()) {
// Time to potentially remove the summary
for (NotifCallback cb : mCallbacks) {
cb.maybeCancelSummary(bubble.getEntry());
}
}
}
@@ -959,8 +939,9 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
mStackView.setExpanded(true);
}
mNotificationEntryManager.updateNotifications(
"BubbleData.Listener.applyUpdate");
for (NotifCallback cb : mCallbacks) {
cb.invalidateNotificationFilter("BubbleData.Listener.applyUpdate");
}
updateStack();
if (DEBUG_BUBBLE_CONTROLLER) {
@@ -980,6 +961,127 @@ public class BubbleController implements ConfigurationController.ConfigurationLi
}
};
/**
* We intercept notification entries cancelled by the user (i.e. dismissed) when there is an
* active bubble associated with it. We do this so that developers can still cancel it
* (and hence the bubbles associated with it). However, these intercepted notifications
* should then be hidden from the shade since the user has cancelled them, so we update
* {@link Bubble#showInShade}.
*
* The cancellation of summaries with children associated with bubbles are also handled in this
* method. User-cancelled summaries are tracked by {@link BubbleData#addSummaryToSuppress}.
*
* @return true if we want to intercept the dismissal of the entry, else false
*/
public boolean shouldInterceptDismissal(NotificationEntry entry, int dismissReason) {
if (entry == null) {
return false;
}
String key = entry.getKey();
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
boolean inBubbleData = mBubbleData.hasBubbleWithKey(key);
boolean isSuppressedSummary = (mBubbleData.isSummarySuppressed(groupKey)
&& mBubbleData.getSummaryKey(groupKey).equals(key));
boolean isSummary = entry != null
&& entry.getSbn().getNotification().isGroupSummary();
boolean isSummaryOfBubbles = (isSuppressedSummary || isSummary)
&& bubbleChildren != null && !bubbleChildren.isEmpty();
if (!inBubbleData && !isSummaryOfBubbles) {
return false;
}
final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
final boolean isUserDimiss = dismissReason == REASON_CANCEL
|| dismissReason == REASON_CLICK;
final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
|| dismissReason == REASON_APP_CANCEL_ALL;
final boolean isSummaryCancel = dismissReason == REASON_GROUP_SUMMARY_CANCELED;
// Need to check for !appCancel here because the notification may have
// previously been dismissed & entry.isRowDismissed would still be true
boolean userRemovedNotif = (entry != null && entry.isRowDismissed() && !isAppCancel)
|| isClearAll || isUserDimiss || isSummaryCancel;
if (isSummaryOfBubbles) {
return handleSummaryRemovalInterception(entry, userRemovedNotif);
}
// The bubble notification sticks around in the data as long as the bubble is
// not dismissed and the app hasn't cancelled the notification.
Bubble bubble = mBubbleData.getBubbleWithKey(key);
boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */, true /* animate */);
for (NotifCallback cb : mCallbacks) {
cb.invalidateNotificationFilter("BubbleController"
+ ".shouldInterceptDismissal");
}
return true;
} else if (!userRemovedNotif && entry != null
&& !isUserCreatedBubble(bubble.getKey())) {
// This wasn't a user removal so we should remove the bubble as well
mBubbleData.notificationEntryRemoved(entry, DISMISS_NOTIF_CANCEL);
return false;
}
return false;
}
private boolean handleSummaryRemovalInterception(NotificationEntry summary,
boolean userRemovedNotif) {
String groupKey = summary.getSbn().getGroupKey();
ArrayList<Bubble> bubbleChildren = mBubbleData.getBubblesInGroup(groupKey);
if (userRemovedNotif) {
// If it's a user dismiss we mark the children to be hidden from the shade.
for (int i = 0; i < bubbleChildren.size(); i++) {
Bubble bubbleChild = bubbleChildren.get(i);
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */, true /* animate */);
}
// And since all children are removed, remove the summary.
mNotificationGroupManager.onEntryRemoved(summary);
// If the summary was auto-generated we don't need to keep that notification around
// because apps can't cancel it; so we only intercept & suppress real summaries.
boolean isAutogroupSummary = (summary.getSbn().getNotification().flags
& FLAG_AUTOGROUP_SUMMARY) != 0;
if (!isAutogroupSummary) {
// TODO: (b/145659174) remove references to mSuppressedGroupKeys once fully migrated
mBubbleData.addSummaryToSuppress(summary.getSbn().getGroupKey(),
summary.getKey());
// Tell shade to update for the suppression
mNotificationEntryManager.updateNotifications("BubbleController"
+ ".handleSummaryRemovalInterception");
}
return !isAutogroupSummary;
} else {
// If it's not a user dismiss it's a cancel.
for (int i = 0; i < bubbleChildren.size(); i++) {
// First check if any of these are user-created (i.e. experimental bubbles)
if (mUserCreatedBubbles.contains(bubbleChildren.get(i).getKey())) {
// Experimental bubble! Intercept the removal.
return true;
}
}
// Not an experimental bubble, safe to remove.
mBubbleData.removeSuppressedSummary(groupKey);
// Remove any associated bubble children with the summary.
for (int i = 0; i < bubbleChildren.size(); i++) {
Bubble bubbleChild = bubbleChildren.get(i);
mBubbleData.notificationEntryRemoved(bubbleChild.getEntry(),
DISMISS_GROUP_CANCELLED);
}
return false;
}
}
/**
* Lets any listeners know if bubble state has changed.
* Updates the visibility of the bubbles based on current state.

View File

@@ -140,7 +140,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle
boolean hideMedia = Utils.useQsMediaPlayer(mContext);
if (ent.isRowDismissed() || ent.isRowRemoved()
|| (ent.isMediaNotification() && hideMedia)
|| mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) {
|| mBubbleController.isBubbleNotificationSuppressedFromShade(ent)) {
// we don't want to update removed notifications because they could
// temporarily become children if they were isolated before.
continue;

View File

@@ -214,6 +214,7 @@ public class NotifCollection implements Dumpable {
private void onNotificationRankingUpdate(RankingMap rankingMap) {
Assert.isMainThread();
applyRanking(rankingMap);
dispatchNotificationRankingUpdate(rankingMap);
rebuildList();
}
@@ -393,6 +394,14 @@ public class NotifCollection implements Dumpable {
mAmDispatchingToOtherCode = false;
}
private void dispatchNotificationRankingUpdate(RankingMap map) {
mAmDispatchingToOtherCode = true;
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingUpdate(map);
}
mAmDispatchingToOtherCode = false;
}
private void dispatchOnEntryRemoved(
NotificationEntry entry,
@CancellationReason int reason,

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.notifcollection;
import android.service.notification.NotificationListenerService;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -47,4 +49,11 @@ public interface NotifCollectionListener {
@CancellationReason int reason,
boolean removedByUser) {
}
/**
* Called whenever the RankingMap is updated by system server. By the time this listener is
* called, the Rankings of all entries will have been updated.
*/
default void onRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
}
}

View File

@@ -204,8 +204,8 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener, State
}
int childCount = 0;
boolean hasBubbles = false;
for (String key : group.children.keySet()) {
if (!getBubbleController().isBubbleNotificationSuppressedFromShade(key)) {
for (NotificationEntry entry : group.children.values()) {
if (!getBubbleController().isBubbleNotificationSuppressedFromShade(entry)) {
childCount++;
} else {
hasBubbles = true;

View File

@@ -279,7 +279,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Make it look like dismissed notif
mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
@@ -323,7 +323,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
assertFalse(mNotificationShadeWindowController.getBubbleExpanded());
// Expand the stack
@@ -335,7 +335,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Make sure the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Collapse
mBubbleController.collapseStack();
@@ -355,9 +355,9 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry().getKey()));
mRow2.getEntry()));
// Expand
BubbleStackView stackView = mBubbleController.getStackView();
@@ -368,14 +368,14 @@ public class BubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), mBubbleData.getSelectedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry().getKey()));
mRow2.getEntry()));
// Switch which bubble is expanded
mBubbleController.selectBubble(mRow.getEntry().getKey());
mBubbleData.setExpanded(true);
assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// collapse for previous bubble
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().getKey());
@@ -396,7 +396,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
@@ -408,7 +408,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -422,7 +422,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// We should have bubbles & their notifs should not be suppressed
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
@@ -434,7 +434,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
@@ -444,7 +444,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Nothing should have changed
// Notif is suppressed after expansion
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Notif shouldn't show dot after expansion
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
}
@@ -468,7 +468,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Last added is the one that is expanded
assertEquals(mRow2.getEntry(), stackView.getExpandedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow2.getEntry().getKey()));
mRow2.getEntry()));
// Dismiss currently expanded
mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
@@ -537,7 +537,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif should be suppressed because we were foreground
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -552,7 +552,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Should not be suppressed
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Should show dot
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
@@ -563,7 +563,7 @@ public class BubbleControllerTest extends SysuiTestCase {
// Notif should be suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
assertFalse(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showFlyout());
@@ -590,7 +590,7 @@ public class BubbleControllerTest extends SysuiTestCase {
public void testMarkNewNotificationAsShowInShade() {
mEntryListener.onNotificationAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
mTestableLooper.processAllMessages();
assertTrue(mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).showDot());
@@ -663,7 +663,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), REASON_CANCEL_ALL);
@@ -672,7 +672,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
verify(mNotificationEntryManager, never()).performRemoveNotification(
any(), anyInt());
@@ -686,7 +686,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
boolean intercepted = mRemoveInterceptor.onNotificationRemoveRequested(
mRow.getEntry().getKey(), REASON_CANCEL);
@@ -695,7 +695,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(intercepted);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
verify(mNotificationEntryManager, never()).performRemoveNotification(
any(), anyInt());
@@ -709,7 +709,7 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Dismiss the bubble
mBubbleController.removeBubble(
@@ -734,13 +734,13 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().getKey(), REASON_CANCEL);
// Should update show in shade state
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(
@@ -757,13 +757,13 @@ public class BubbleControllerTest extends SysuiTestCase {
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
mBubbleData.setExpanded(true);
// Once a bubble is expanded the notif is suppressed
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
mRow.getEntry()));
// Should notify delegate that shade state changed
verify(listener).onBubbleNotificationSuppressionChange(