If something is suppressed from the notification list don't bubble it

Also set it to not auto-expand since it's unexpected to have bubbles
expand when DND turns off.

Test: BubblesTest NewNotifPipelineBubblesTest
Bug: 226316876
Change-Id: Ieb77b211f2b11fea83a2ff6e34e6f31b0f96919e
This commit is contained in:
Mady Mellor
2022-03-24 22:51:25 +00:00
parent e4d1834578
commit d0ac45a71f
4 changed files with 135 additions and 10 deletions

View File

@@ -1037,7 +1037,15 @@ public class BubbleController {
}
} else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
inflateAndAdd(bubble, suppressFlyout, showInShade);
if (notif.shouldSuppressNotificationList()) {
// If we're suppressing notifs for DND, we don't want the bubbles to randomly
// expand when DND turns off so flip the flag.
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
}
} else {
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
}
@@ -1069,7 +1077,8 @@ public class BubbleController {
}
}
private void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
@VisibleForTesting
public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1095,7 +1104,8 @@ public class BubbleController {
}
}
private void onRankingUpdated(RankingMap rankingMap,
@VisibleForTesting
public void onRankingUpdated(RankingMap rankingMap,
HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey) {
if (mTmpRanking == null) {
mTmpRanking = new NotificationListenerService.Ranking();
@@ -1106,19 +1116,22 @@ public class BubbleController {
Pair<BubbleEntry, Boolean> entryData = entryDataByKey.get(key);
BubbleEntry entry = entryData.first;
boolean shouldBubbleUp = entryData.second;
if (entry != null && !isCurrentProfile(
entry.getStatusBarNotification().getUser().getIdentifier())) {
return;
}
if (entry != null && (entry.shouldSuppressNotificationList()
|| entry.getRanking().isSuspended())) {
shouldBubbleUp = false;
}
rankingMap.getRanking(key, mTmpRanking);
boolean isActiveBubble = mBubbleData.hasAnyBubbleWithKey(key);
if (isActiveBubble && !mTmpRanking.canBubble()) {
boolean isActiveOrInOverflow = mBubbleData.hasAnyBubbleWithKey(key);
boolean isActive = mBubbleData.hasBubbleInStackWithKey(key);
if (isActiveOrInOverflow && !mTmpRanking.canBubble()) {
// If this entry is no longer allowed to bubble, dismiss with the BLOCKED reason.
// This means that the app or channel's ability to bubble has been revoked.
mBubbleData.dismissBubbleWithKey(key, DISMISS_BLOCKED);
} else if (isActiveBubble && (!shouldBubbleUp || entry.getRanking().isSuspended())) {
} else if (isActiveOrInOverflow && !shouldBubbleUp) {
// If this entry is allowed to bubble, but cannot currently bubble up or is
// suspended, dismiss it. This happens when DND is enabled and configured to hide
// bubbles, or focus mode is enabled and the app is designated as distracting.
@@ -1126,9 +1139,9 @@ public class BubbleController {
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
} else if (entry != null && mTmpRanking.isBubble() && !isActiveBubble) {
} else if (entry != null && mTmpRanking.isBubble() && !isActive) {
entry.setFlagBubble(true);
onEntryUpdated(entry, shouldBubbleUp && !entry.getRanking().isSuspended());
onEntryUpdated(entry, shouldBubbleUp);
}
}
}

View File

@@ -1057,6 +1057,22 @@ public class BubbleData {
return null;
}
/**
* Get a pending bubble with given notification <code>key</code>
*
* @param key notification key
* @return bubble that matches or null
*/
@VisibleForTesting(visibility = PRIVATE)
public Bubble getPendingBubbleWithKey(String key) {
for (Bubble b : mPendingBubbles.values()) {
if (b.getKey().equals(key)) {
return b;
}
}
return null;
}
@VisibleForTesting(visibility = PRIVATE)
void setTimeSource(TimeSource timeSource) {
mTimeSource = timeSource;

View File

@@ -57,6 +57,7 @@ import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -70,6 +71,8 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;
@@ -147,6 +150,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@@ -1466,6 +1470,50 @@ public class BubblesTest extends SysuiTestCase {
verify(mBubbleController).onBubbleMetadataFlagChanged(b);
}
@Test
public void testUpdateBubble_skipsDndSuppressListNotifs() {
mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
mRow.shouldSuppressPeek());
mBubbleEntry.getBubbleMetadata().setFlags(
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
mBubbleController.updateBubble(mBubbleEntry);
Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
assertThat(b.shouldAutoExpand()).isFalse();
assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
}
@Test
public void testOnRankingUpdate_DndSuppressListNotif() {
// It's in the stack
mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();
// Set current user profile
SparseArray<UserInfo> userInfos = new SparseArray<>();
userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
mock(UserInfo.class));
mBubbleController.onCurrentProfilesChanged(userInfos);
// Send ranking update that the notif is suppressed from the list.
HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
mRow.shouldSuppressPeek());
Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
entryDataByKey.put(mBubbleEntry.getKey(), pair);
NotificationListenerService.RankingMap rankingMap =
mock(NotificationListenerService.RankingMap.class);
when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);
// Should no longer be in the stack
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
}
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);

View File

@@ -50,6 +50,7 @@ import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps;
import android.content.pm.UserInfo;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.Handler;
import android.os.PowerManager;
@@ -59,6 +60,8 @@ import android.service.notification.NotificationListenerService;
import android.service.notification.ZenModeConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Pair;
import android.util.SparseArray;
import android.view.View;
import android.view.WindowManager;
@@ -128,6 +131,7 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@@ -1286,6 +1290,50 @@ public class NewNotifPipelineBubblesTest extends SysuiTestCase {
verify(mBubbleController).onBubbleMetadataFlagChanged(b);
}
@Test
public void testUpdateBubble_skipsDndSuppressListNotifs() {
mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
mRow.shouldSuppressPeek());
mBubbleEntry.getBubbleMetadata().setFlags(
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE);
mBubbleController.updateBubble(mBubbleEntry);
Bubble b = mBubbleData.getPendingBubbleWithKey(mBubbleEntry.getKey());
assertThat(b.shouldAutoExpand()).isFalse();
assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleEntry.getKey())).isNull();
}
@Test
public void testOnRankingUpdate_DndSuppressListNotif() {
// It's in the stack
mBubbleController.updateBubble(mBubbleEntry);
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isTrue();
// Set current user profile
SparseArray<UserInfo> userInfos = new SparseArray<>();
userInfos.put(mBubbleEntry.getStatusBarNotification().getUser().getIdentifier(),
mock(UserInfo.class));
mBubbleController.onCurrentProfilesChanged(userInfos);
// Send ranking update that the notif is suppressed from the list.
HashMap<String, Pair<BubbleEntry, Boolean>> entryDataByKey = new HashMap<>();
mBubbleEntry = new BubbleEntry(mRow.getSbn(), mRow.getRanking(), mRow.isDismissable(),
mRow.shouldSuppressNotificationDot(), true /* DndSuppressNotifFromList */,
mRow.shouldSuppressPeek());
Pair<BubbleEntry, Boolean> pair = new Pair(mBubbleEntry, true);
entryDataByKey.put(mBubbleEntry.getKey(), pair);
NotificationListenerService.RankingMap rankingMap =
mock(NotificationListenerService.RankingMap.class);
when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);
// Should no longer be in the stack
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
}
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not