diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index c6d1286319302..b71e3adae8acf 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -285,6 +285,15 @@ class Bubble implements BubbleViewProvider { mInflateSynchronously = inflateSynchronously; } + /** + * Sets whether this bubble is considered visually interruptive. Normally pulled from the + * {@link NotificationEntry}, this method is purely for testing. + */ + @VisibleForTesting + void setVisuallyInterruptiveForTest(boolean visuallyInterruptive) { + mIsVisuallyInterruptive = visuallyInterruptive; + } + /** * Starts a task to inflate & load any necessary information to display a bubble. * @@ -411,6 +420,7 @@ class Bubble implements BubbleViewProvider { } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) { // Was an intent bubble now it's a shortcut bubble... still unregister the listener mIntent.unregisterCancelListener(mIntentCancelListener); + mIntentActive = false; mIntent = null; } mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 2948b47103bfc..5deae925ba30e 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -82,7 +82,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; -import com.android.systemui.bubbles.animation.StackAnimationController; import com.android.systemui.bubbles.dagger.BubbleModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; @@ -407,7 +406,8 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (bubble.getBubbleIntent() == null) { return; } - if (bubble.isIntentActive()) { + if (bubble.isIntentActive() + || mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) { bubble.setPendingIntentCanceled(); return; } @@ -1120,8 +1120,17 @@ public class BubbleController implements ConfigurationController.ConfigurationLi if (notif.getImportance() >= NotificationManager.IMPORTANCE_HIGH) { notif.setInterruption(); } - Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); - inflateAndAdd(bubble, suppressFlyout, showInShade); + if (!notif.getRanking().visuallyInterruptive() + && (notif.getBubbleMetadata() != null + && !notif.getBubbleMetadata().getAutoExpandBubble()) + && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { + // Update the bubble but don't promote it out of overflow + Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); + b.setEntry(notif); + } else { + Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); + inflateAndAdd(bubble, suppressFlyout, showInShade); + } } void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) { diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index d2dc506c8e5c2..85ea8bc914841 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -277,7 +277,8 @@ public class BubbleData { } else { // Updates an existing bubble bubble.setSuppressFlyout(suppressFlyout); - doUpdate(bubble); + // If there is no flyout, we probably shouldn't show the bubble at the top + doUpdate(bubble, !suppressFlyout /* reorder */); } if (bubble.shouldAutoExpand()) { @@ -431,12 +432,12 @@ public class BubbleData { } } - private void doUpdate(Bubble bubble) { + private void doUpdate(Bubble bubble, boolean reorder) { if (DEBUG_BUBBLE_DATA) { Log.d(TAG, "doUpdate: " + bubble); } mStateChange.updatedBubble = bubble; - if (!isExpanded()) { + if (!isExpanded() && reorder) { int prevPos = mBubbles.indexOf(bubble); mBubbles.remove(bubble); mBubbles.add(0, bubble); 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 15828b41cc7c9..1ad88560bf9c7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -999,6 +999,29 @@ public class BubbleControllerTest extends SysuiTestCase { verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry()); } + + /** + * Verifies that when a non visually interruptive update occurs for a bubble in the overflow, + * the that bubble does not get promoted from the overflow. + */ + @Test + public void test_notVisuallyInterruptive_updateOverflowBubble_notAdded() { + // Setup + mBubbleController.updateBubble(mRow.getEntry()); + mBubbleController.updateBubble(mRow2.getEntry()); + assertTrue(mBubbleController.hasBubbles()); + + // Overflow it + mBubbleData.dismissBubbleWithKey(mRow.getEntry().getKey(), + BubbleController.DISMISS_USER_GESTURE); + assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().getKey())).isFalse(); + assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey())).isTrue(); + + // Test + mBubbleController.updateBubble(mRow.getEntry()); + assertThat(mBubbleData.hasBubbleInStackWithKey(mRow.getEntry().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 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 315caeebe0e92..4bbc41e517b0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java @@ -512,6 +512,26 @@ public class BubbleDataTest extends SysuiTestCase { assertSelectionChangedTo(mBubbleA1); } + /** + * Verifies that when a non visually interruptive update occurs, that the selection does not + * change. + */ + @Test + public void test_notVisuallyInterruptive_updateBubble_selectionDoesntChange() { + // Setup + sendUpdatedEntryAtTime(mEntryA1, 1000); + sendUpdatedEntryAtTime(mEntryB1, 2000); + sendUpdatedEntryAtTime(mEntryB2, 3000); + sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1] + mBubbleData.setListener(mListener); + + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2); + + // Test + sendUpdatedEntryAtTime(mEntryB1, 5000, false /* isVisuallyInterruptive */); + assertThat(mBubbleData.getSelectedBubble()).isEqualTo(mBubbleA2); + } + /** * Verifies that a request to expand the stack has no effect if there are no bubbles. */ @@ -883,9 +903,15 @@ public class BubbleDataTest extends SysuiTestCase { } private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) { + sendUpdatedEntryAtTime(entry, postTime, true /* visuallyInterruptive */); + } + + private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime, + boolean visuallyInterruptive) { setPostTime(entry, postTime); // BubbleController calls this: Bubble b = mBubbleData.getOrCreateBubble(entry, null /* persistedBubble */); + b.setVisuallyInterruptiveForTest(visuallyInterruptive); // And then this mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/, true /* showInShade */);