diff options
12 files changed, 296 insertions, 54 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 1af275fedd74..16898a08851a 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8634,17 +8634,23 @@ public class Notification implements Parcelable 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 */ @@ -8762,6 +8768,24 @@ public class Notification implements Parcelable } /** + * 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 a9f7b8455807..7622883326a0 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -78,6 +78,7 @@ interface IStatusBarService 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 ccce85ca74fb..45705b76f09c 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 @@ class Bubble { 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 @@ class Bubble { 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 @@ class Bubble { /** 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 @@ class Bubble { */ void markAsAccessedAt(long lastAccessedMillis) { mLastAccessed = lastAccessedMillis; - setShowInShade(false); + setSuppressNotification(true); setShowDot(false /* show */, true /* animate */); } @@ -290,20 +286,30 @@ class Bubble { } /** - * 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 8c9946fcfc7b..2ed5b10fd564 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi } /** + * 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi 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 @@ public class BubbleController implements ConfigurationController.ConfigurationLi // 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 8b687e7114db..673121f92716 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 @@ public class BubbleData { @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 @@ public class BubbleData { 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 @@ public class BubbleData { return b; } } - bubble = new Bubble(entry); + bubble = new Bubble(entry, mSuppressionListener); mPendingBubbles.add(bubble); } else { bubble.setEntry(entry); @@ -258,11 +266,13 @@ public class BubbleData { } else if (mSelectedBubble == null) { setSelectedBubbleInternal(bubble); } + boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble; - bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade); + boolean suppress = isBubbleExpandedAndSelected || !showInShade || !bubble.showInShade(); + bubble.setSuppressNotification(suppress); bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */); - dispatchPendingChanges(); + 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 2c9058afd7ab..28f01da56f06 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 @@ public class BubbleControllerTest extends SysuiTestCase { 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 @@ public class BubbleControllerTest extends SysuiTestCase { // 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 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mBubbleStateChangeListener).onHasBubblesChanged(true /* hasBubbles */); } - @Test public void testExpandStackAndSelectBubble_removedFirst() { final String key = mRow.getEntry().getKey(); @@ -724,6 +724,52 @@ public class BubbleControllerTest extends SysuiTestCase { 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 1a2e23796c78..c9f5b40e8f9f 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 @@ public class BubbleDataTest extends SysuiTestCase { @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 @@ public class BubbleDataTest extends SysuiTestCase { 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 @@ public class BubbleDataTest extends SysuiTestCase { 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 02f721ce58a7..7f67657e1109 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 androidx.test.filters.SmallTest; 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 @@ public class BubbleTest extends SysuiTestCase { private NotificationEntry mEntry; private Bundle mExtras; + private Bubble mBubble; + + @Mock + private BubbleController.NotificationSuppressionChangedListener mSuppressionListener; @Before public void setUp() { @@ -57,6 +70,15 @@ public class BubbleTest extends SysuiTestCase { 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 @@ public class BubbleTest extends SysuiTestCase { 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 88fc072e2481..feb4f0edcc0d 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 @@ public interface NotificationDelegate { 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 eea59ca26f34..38ed67731ab0 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 @@ public class NotificationManagerService extends SystemService { } @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 3f7d373c1848..57a6776ebc6c 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @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 1b92abef7c94..768b4721a1ee 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 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @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", |