Merge "Bubble API: post update when bubble notification suppression flag changes"
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b346e8f..cdb49f0 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -8644,17 +8644,23 @@
public static final int FLAG_AUTO_EXPAND_BUBBLE = 0x00000001;
/**
- * If set and the app posting the bubble is in the foreground, the bubble will
- * be posted <b>without</b> the associated notification in the notification shade.
+ * Indicates whether the notification associated with the bubble is being visually
+ * suppressed from the notification shade. When <code>true</code> the notification is
+ * hidden, when <code>false</code> the notification shows as normal.
*
- * <p>This flag has no effect if the app posting the bubble is not in the foreground.
- * The app is considered foreground if it is visible and on the screen, note that
- * a foreground service does not qualify.
- * </p>
+ * <p>Apps sending bubbles may set this flag so that the bubble is posted <b>without</b>
+ * the associated notification in the notification shade.</p>
*
- * <p>Generally this flag should only be set if the user has performed an action to request
- * or create a bubble, or if the user has seen the content in the notification and the
- * notification is no longer relevant.</p>
+ * <p>Apps sending bubbles can only apply this flag when the app is in the foreground,
+ * otherwise the flag is not respected. The app is considered foreground if it is visible
+ * and on the screen, note that a foreground service does not qualify.</p>
+ *
+ * <p>Generally this flag should only be set by the app if the user has performed an
+ * action to request or create a bubble, or if the user has seen the content in the
+ * notification and the notification is no longer relevant. </p>
+ *
+ * <p>The system will also update this flag with <code>true</code> to hide the notification
+ * from the user once the bubble has been expanded. </p>
*
* @hide
*/
@@ -8772,6 +8778,24 @@
}
/**
+ * Indicates whether the notification associated with the bubble is being visually
+ * suppressed from the notification shade. When <code>true</code> the notification is
+ * hidden, when <code>false</code> the notification shows as normal.
+ *
+ * <p>Apps sending bubbles may set this flag so that the bubble is posted <b>without</b>
+ * the associated notification in the notification shade.</p>
+ *
+ * <p>Apps sending bubbles can only apply this flag when the app is in the foreground,
+ * otherwise the flag is not respected. The app is considered foreground if it is visible
+ * and on the screen, note that a foreground service does not qualify.</p>
+ *
+ * <p>Generally the app should only set this flag if the user has performed an
+ * action to request or create a bubble, or if the user has seen the content in the
+ * notification and the notification is no longer relevant. </p>
+ *
+ * <p>The system will update this flag with <code>true</code> to hide the notification
+ * from the user once the bubble has been expanded.</p>
+ *
* @return whether this bubble should suppress the notification when it is posted.
*
* @see BubbleMetadata.Builder#setSuppressNotification(boolean)
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a9f7b84..7622883 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -78,6 +78,7 @@
in int notificationLocation, boolean modifiedBeforeSending);
void onNotificationSettingsViewed(String key);
void onNotificationBubbleChanged(String key, boolean isBubble);
+ void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
void grantInlineReplyUriPermission(String key, in Uri uri, in UserHandle user, String packageName);
void clearInlineReplyUriPermissions(String key);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ccce85c..45705b7 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -60,6 +60,14 @@
private long mLastUpdated;
private long mLastAccessed;
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
+ /** Whether the bubble should show a dot for the notification indicating updated content. */
+ private boolean mShowBubbleUpdateDot = true;
+
+ /** Whether flyout text should be suppressed, regardless of any other flags or state. */
+ private boolean mSuppressFlyout;
+
// Items that are typically loaded later
private String mAppName;
private ShortcutInfo mShortcutInfo;
@@ -71,20 +79,6 @@
private boolean mInflateSynchronously;
/**
- * Whether this notification should be shown in the shade when it is also displayed as a bubble.
- *
- * <p>When a notification is a bubble we don't show it in the shade once the bubble has been
- * expanded</p>
- */
- private boolean mShowInShadeWhenBubble = true;
-
- /** Whether the bubble should show a dot for the notification indicating updated content. */
- private boolean mShowBubbleUpdateDot = true;
-
- /** Whether flyout text should be suppressed, regardless of any other flags or state. */
- private boolean mSuppressFlyout;
-
- /**
* Presentational info about the flyout.
*/
public static class FlyoutMessage {
@@ -106,11 +100,13 @@
/** Used in tests when no UI is required. */
@VisibleForTesting(visibility = PRIVATE)
- Bubble(NotificationEntry e) {
+ Bubble(NotificationEntry e,
+ BubbleController.NotificationSuppressionChangedListener listener) {
mEntry = e;
mKey = e.getKey();
mLastUpdated = e.getSbn().getPostTime();
mGroupId = groupId(e);
+ mSuppressionListener = listener;
}
public String getKey() {
@@ -278,7 +274,7 @@
*/
void markAsAccessedAt(long lastAccessedMillis) {
mLastAccessed = lastAccessedMillis;
- setShowInShade(false);
+ setSuppressNotification(true);
setShowDot(false /* show */, true /* animate */);
}
@@ -290,20 +286,30 @@
}
/**
- * Whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Whether this notification should be shown in the shade.
*/
boolean showInShade() {
- return !mEntry.isRowDismissed() && !shouldSuppressNotification()
- && (!mEntry.isClearable() || mShowInShadeWhenBubble);
+ return !shouldSuppressNotification() || !mEntry.isClearable();
}
/**
- * Sets whether this notification should be shown in the shade when it is also displayed as a
- * bubble.
+ * Sets whether this notification should be suppressed in the shade.
*/
- void setShowInShade(boolean showInShade) {
- mShowInShadeWhenBubble = showInShade;
+ void setSuppressNotification(boolean suppressNotification) {
+ boolean prevShowInShade = showInShade();
+
+ Notification.BubbleMetadata data = mEntry.getBubbleMetadata();
+ int flags = data.getFlags();
+ if (suppressNotification) {
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ } else {
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ }
+ data.setFlags(flags);
+
+ if (showInShade() != prevShowInShade && mSuppressionListener != null) {
+ mSuppressionListener.onBubbleNotificationSuppressionChange(this);
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 8c9946f..2ed5b10 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -224,6 +224,17 @@
}
/**
+ * Listener to be notified when a bubbles' notification suppression state changes.
+ */
+ public interface NotificationSuppressionChangedListener {
+ /**
+ * Called when the notification suppression state of a bubble changes.
+ */
+ void onBubbleNotificationSuppressionChange(Bubble bubble);
+
+ }
+
+ /**
* Listens for the current state of the status bar and updates the visibility state
* of bubbles as needed.
*/
@@ -303,9 +314,22 @@
configurationController.addCallback(this /* configurationListener */);
+ mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
mBubbleData = data;
mBubbleData.setListener(mBubbleDataListener);
- mMaxBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_rendered);
+ mBubbleData.setSuppressionChangedListener(new NotificationSuppressionChangedListener() {
+ @Override
+ public void onBubbleNotificationSuppressionChange(Bubble bubble) {
+ // Make sure NoMan knows it's not showing in the shade anymore so anyone querying it
+ // can tell.
+ try {
+ mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(),
+ !bubble.showInShade());
+ } catch (RemoteException e) {
+ // Bad things have happened
+ }
+ }
+ });
mNotificationEntryManager = entryManager;
mNotificationEntryManager.addNotificationEntryListener(mEntryListener);
@@ -720,7 +744,7 @@
Bubble bubble = mBubbleData.getBubbleWithKey(key);
boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
- bubble.setShowInShade(false);
+ bubble.setSuppressNotification(true);
bubble.setShowDot(false /* show */, true /* animate */);
mNotificationEntryManager.updateNotifications(
"BubbleController.onNotificationRemoveRequested");
@@ -747,7 +771,7 @@
// 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.setShowInShade(false);
+ bubbleChild.setSuppressNotification(true);
bubbleChild.setShowDot(false /* show */, true /* animate */);
}
// And since all children are removed, remove the summary.
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 8b687e7..673121f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -134,6 +134,9 @@
@Nullable
private Listener mListener;
+ @Nullable
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
/**
* We track groups with summaries that aren't visibly displayed but still kept around because
* the bubble(s) associated with the summary still exist.
@@ -158,6 +161,11 @@
mMaxOverflowBubbles = mContext.getResources().getInteger(R.integer.bubbles_max_overflow);
}
+ public void setSuppressionChangedListener(
+ BubbleController.NotificationSuppressionChangedListener listener) {
+ mSuppressionListener = listener;
+ }
+
public boolean hasBubbles() {
return !mBubbles.isEmpty();
}
@@ -219,7 +227,7 @@
return b;
}
}
- bubble = new Bubble(entry);
+ bubble = new Bubble(entry, mSuppressionListener);
mPendingBubbles.add(bubble);
} else {
bubble.setEntry(entry);
@@ -258,11 +266,13 @@
} else if (mSelectedBubble == null) {
setSelectedBubbleInternal(bubble);
}
- boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
- bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade);
- bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
- dispatchPendingChanges();
+ boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade();
+ bubble.setSuppressNotification(suppress);
+ bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
+
+ dispatchPendingChanges();
}
public void notificationEntryRemoved(NotificationEntry entry, @DismissReason int reason) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 2c9058a..28f01da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -282,7 +282,7 @@
mRow.getEntry().getKey()));
// Make it look like dismissed notif
- mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setShowInShade(false);
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()).setSuppressNotification(true);
// Now remove the bubble
mBubbleController.removeBubble(
@@ -372,7 +372,8 @@
// Switch which bubble is expanded
mBubbleController.selectBubble(mRow.getEntry().getKey());
- mBubbleController.expandStack();
+ mBubbleData.setExpanded(true);
+ assertEquals(mRow.getEntry(), stackView.getExpandedBubble().getEntry());
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry().getKey()));
@@ -571,7 +572,6 @@
verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */);
}
-
@Test
public void testExpandStackAndSelectBubble_removedFirst() {
final String key = mRow.getEntry().getKey();
@@ -724,6 +724,52 @@
assertFalse(intercepted);
}
+ @Test
+ public void testNotifyShadeSuppressionChange_notificationDismiss() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onNotificationAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ mRemoveInterceptor.onNotificationRemoveRequested(mRow.getEntry().getKey(), REASON_CANCEL);
+
+ // Should update show in shade state
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
+ @Test
+ public void testNotifyShadeSuppressionChange_bubbleExpanded() {
+ BubbleController.NotificationSuppressionChangedListener listener =
+ mock(BubbleController.NotificationSuppressionChangedListener.class);
+ mBubbleData.setSuppressionChangedListener(listener);
+
+ mEntryListener.onNotificationAdded(mRow.getEntry());
+
+ assertTrue(mBubbleController.hasBubbles());
+ assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ mBubbleData.setExpanded(true);
+
+ // Once a bubble is expanded the notif is suppressed
+ assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
+ mRow.getEntry().getKey()));
+
+ // Should notify delegate that shade state changed
+ verify(listener).onBubbleNotificationSuppressionChange(
+ mBubbleData.getBubbleWithKey(mRow.getEntry().getKey()));
+ }
+
static class TestableBubbleController extends BubbleController {
// Let's assume surfaces can be synchronized immediately.
TestableBubbleController(Context context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index 1a2e237..c9f5b40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -104,6 +104,9 @@
@Captor
private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
+ @Mock
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+
@Before
public void setUp() throws Exception {
mNotificationTestHelper = new NotificationTestHelper(mContext, mDependency);
@@ -121,20 +124,20 @@
modifyRanking(mEntryInterruptive)
.setVisuallyInterruptive(true)
.build();
- mBubbleInterruptive = new Bubble(mEntryInterruptive);
+ mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
mEntryDismissed.setRow(row);
- mBubbleDismissed = new Bubble(mEntryDismissed);
+ mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
- mBubbleA1 = new Bubble(mEntryA1);
- mBubbleA2 = new Bubble(mEntryA2);
- mBubbleA3 = new Bubble(mEntryA3);
- mBubbleB1 = new Bubble(mEntryB1);
- mBubbleB2 = new Bubble(mEntryB2);
- mBubbleB3 = new Bubble(mEntryB3);
- mBubbleC1 = new Bubble(mEntryC1);
+ mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
+ mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
+ mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
+ mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
+ mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
+ mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
+ mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
mBubbleData = new BubbleData(getContext());
@@ -237,9 +240,8 @@
true /* showInShade */);
verifyUpdateReceived();
- // Make it look like user swiped away row
- mEntryDismissed.getRow().dismiss(false /* refocusOnDismiss */);
- assertThat(mBubbleData.getBubbleWithKey(mBubbleDismissed.getKey()).showInShade()).isFalse();
+ // Suppress the notif / make it look dismissed
+ mBubbleDismissed.setSuppressNotification(true);
mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
true /* showInShade */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index 02f721c..7f67657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -16,11 +16,19 @@
package com.android.systemui.bubbles;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -30,6 +38,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.tests.R;
import org.junit.Before;
import org.junit.Test;
@@ -46,6 +55,10 @@
private NotificationEntry mEntry;
private Bundle mExtras;
+ private Bubble mBubble;
+
+ @Mock
+ private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
@Before
public void setUp() {
@@ -57,6 +70,15 @@
mEntry = new NotificationEntryBuilder()
.setNotification(mNotif)
.build();
+
+ mBubble = new Bubble(mEntry, mSuppressionListener);
+
+ Intent target = new Intent(mContext, BubblesTestActivity.class);
+ Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder()
+ .createIntentBubble(PendingIntent.getActivity(mContext, 0, target, 0),
+ Icon.createWithResource(mContext, R.drawable.android))
+ .build();
+ mEntry.setBubbleMetadata(metadata);
}
@Test
@@ -123,4 +145,24 @@
BubbleViewInfoTask.extractFlyoutMessage(mContext,
mEntry).senderName);
}
+
+ @Test
+ public void testSuppressionListener_change_notified() {
+ assertThat(mBubble.showInShade()).isTrue();
+
+ mBubble.setSuppressNotification(true);
+
+ assertThat(mBubble.showInShade()).isFalse();
+
+ verify(mSuppressionListener).onBubbleNotificationSuppressionChange(mBubble);
+ }
+
+ @Test
+ public void testSuppressionListener_noChange_doesntNotify() {
+ assertThat(mBubble.showInShade()).isTrue();
+
+ mBubble.setSuppressNotification(false);
+
+ verify(mSuppressionListener, never()).onBubbleNotificationSuppressionChange(any());
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 88fc072..feb4f0e 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -48,7 +48,15 @@
int notificationLocation);
void onNotificationDirectReplied(String key);
void onNotificationSettingsViewed(String key);
+ /**
+ * Called when the state of {@link Notification#FLAG_BUBBLE} is changed.
+ */
void onNotificationBubbleChanged(String key, boolean isBubble);
+ /**
+ * Called when the state of {@link Notification.BubbleMetadata#FLAG_SUPPRESS_NOTIFICATION}
+ * changes.
+ */
+ void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed);
/**
* Grant permission to read the specified URI to the package associated with the
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index eea59ca..38ed677 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1175,6 +1175,34 @@
}
@Override
+ public void onBubbleNotificationSuppressionChanged(String key, boolean isSuppressed) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ Notification.BubbleMetadata data = r.getNotification().getBubbleMetadata();
+ if (data == null) {
+ // No data, do nothing
+ return;
+ }
+ boolean currentlySuppressed = data.isNotificationSuppressed();
+ if (currentlySuppressed == isSuppressed) {
+ // No changes, do nothing
+ return;
+ }
+ int flags = data.getFlags();
+ if (isSuppressed) {
+ flags |= Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ } else {
+ flags &= ~Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ }
+ data.setFlags(flags);
+ mHandler.post(new EnqueueNotificationRunnable(r.getUser().getIdentifier(), r,
+ true /* isAppForeground */));
+ }
+ }
+ }
+
+ @Override
/**
* Grant permission to read the specified URI to the package specified in the
* NotificationRecord associated with the given key. The callingUid represents the UID of
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3f7d373..57a6776 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1364,6 +1364,17 @@
}
@Override
+ public void onBubbleNotificationSuppressionChanged(String key, boolean isNotifSuppressed) {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onBubbleNotificationSuppressionChanged(key, isNotifSuppressed);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void grantInlineReplyUriPermission(String key, Uri uri, UserHandle user,
String packageName) {
enforceStatusBarService();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 1b92abe..768b472 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -5438,6 +5438,46 @@
}
@Test
+ public void testOnBubbleNotificationSuppressionChanged() throws Exception {
+ // Bubble notification
+ NotificationRecord nr = generateMessageBubbleNotifRecord(mTestNotificationChannel, "tag");
+
+ // Bubbles are allowed!
+ setUpPrefsForBubbles(PKG, nr.sbn.getUserId(), true /* global */,
+ true /* app */, true /* channel */);
+
+ mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
+ nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+ waitForIdle();
+
+ // NOT suppressed
+ Notification n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertFalse(n.getBubbleMetadata().isNotificationSuppressed());
+
+ // Reset as this is called when the notif is first sent
+ reset(mListeners);
+
+ // Test: update suppression to true
+ mService.mNotificationDelegate.onBubbleNotificationSuppressionChanged(nr.getKey(), true);
+ waitForIdle();
+
+ // Check
+ n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertTrue(n.getBubbleMetadata().isNotificationSuppressed());
+
+ // Reset to check again
+ reset(mListeners);
+
+ // Test: update suppression to false
+ mService.mNotificationDelegate.onBubbleNotificationSuppressionChanged(nr.getKey(), false);
+ waitForIdle();
+
+ // Check
+ n = mBinderService.getActiveNotifications(PKG)[0].getNotification();
+ assertFalse(n.getBubbleMetadata().isNotificationSuppressed());
+ }
+
+ @Test
public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",