Merge changes from topic "mm-bubble-updates" into tm-qpr-dev am: 7e347d7e82

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19505611

Change-Id: Iffc084116f049f24e8fdd4aa3f1dad7cafb5695c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mady Mellor
2022-08-08 17:55:06 +00:00
committed by Automerger Merge Worker
5 changed files with 145 additions and 18 deletions

View File

@@ -452,6 +452,7 @@ public class Bubble implements BubbleViewProvider {
*/ */
void setEntry(@NonNull final BubbleEntry entry) { void setEntry(@NonNull final BubbleEntry entry) {
Objects.requireNonNull(entry); Objects.requireNonNull(entry);
boolean showingDotPreviously = showDot();
mLastUpdated = entry.getStatusBarNotification().getPostTime(); mLastUpdated = entry.getStatusBarNotification().getPostTime();
mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification(); mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
mPackageName = entry.getStatusBarNotification().getPackageName(); mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@ public class Bubble implements BubbleViewProvider {
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot(); mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList(); mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek(); mShouldSuppressPeek = entry.shouldSuppressPeek();
if (showingDotPreviously != showDot()) {
// This will update the UI if needed
setShowDot(showDot());
}
} }
@Nullable @Nullable

View File

@@ -1055,18 +1055,28 @@ public class BubbleController implements ConfigurationChangeListener {
public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) { public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
// If this is an interruptive notif, mark that it's interrupted // If this is an interruptive notif, mark that it's interrupted
mSysuiProxy.setNotificationInterruption(notif.getKey()); mSysuiProxy.setNotificationInterruption(notif.getKey());
if (!notif.getRanking().isTextChanged() boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
&& (notif.getBubbleMetadata() != null && (notif.getBubbleMetadata() != null
&& !notif.getBubbleMetadata().getAutoExpandBubble()) && !notif.getBubbleMetadata().getAutoExpandBubble());
if (isNonInterruptiveNotExpanding
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) { && mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow // Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey()); Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
b.setEntry(notif); if (notif.isBubble()) {
notif.setFlagBubble(false);
}
updateNotNotifyingEntry(b, notif, showInShade);
} else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
&& isNonInterruptiveNotExpanding) {
Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
if (b != null) {
updateNotNotifyingEntry(b, notif, showInShade);
}
} else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) { } else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
// Update the bubble but don't promote it out of overflow // Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey()); Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
if (b != null) { if (b != null) {
b.setEntry(notif); updateNotNotifyingEntry(b, notif, showInShade);
} }
} else { } else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */); Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@ public class BubbleController implements ConfigurationChangeListener {
if (bubble.shouldAutoExpand()) { if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false); bubble.setShouldAutoExpand(false);
} }
mImpl.mCachedState.updateBubbleSuppressedState(bubble);
} else { } else {
inflateAndAdd(bubble, suppressFlyout, showInShade); inflateAndAdd(bubble, suppressFlyout, showInShade);
} }
} }
} }
void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) { void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
b.setEntry(entry);
boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
b.setSuppressNotification(suppress);
b.setShowDot(!isBubbleExpandedAndSelected);
mImpl.mCachedState.updateBubbleSuppressedState(b);
}
@VisibleForTesting
public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
// Lazy init stack view when a bubble is created // Lazy init stack view when a bubble is created
ensureStackViewCreated(); ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously); bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@ public class BubbleController implements ConfigurationChangeListener {
} }
@VisibleForTesting @VisibleForTesting
public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) { public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
if (!fromSystem) {
return;
}
// shouldBubbleUp checks canBubble & for bubble metadata // shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry); boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) { if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@ public class BubbleController implements ConfigurationChangeListener {
// notification, so that the bubble will be re-created if shouldBubbleUp returns // notification, so that the bubble will be re-created if shouldBubbleUp returns
// true. // true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP); mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
} else if (entry != null && mTmpRanking.isBubble() && !isActive) { } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
entry.setFlagBubble(true); entry.setFlagBubble(true);
onEntryUpdated(entry, shouldBubbleUp); onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
} }
} }
} }
@@ -1773,9 +1798,9 @@ public class BubbleController implements ConfigurationChangeListener {
} }
@Override @Override
public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) { public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
mMainExecutor.execute(() -> { mMainExecutor.execute(() -> {
BubbleController.this.onEntryUpdated(entry, shouldBubbleUp); BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
}); });
} }

View File

@@ -171,8 +171,9 @@ public interface Bubbles {
* *
* @param entry the {@link BubbleEntry} by the notification. * @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up. * @param shouldBubbleUp {@code true} if this notification should bubble up.
* @param fromSystem {@code true} if this update is from NotificationManagerService.
*/ */
void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp); void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
/** /**
* Called when new notification entry removed. * Called when new notification entry removed.

View File

@@ -405,8 +405,8 @@ public class BubblesManager implements Dumpable {
} }
@Override @Override
public void onEntryUpdated(NotificationEntry entry) { public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
BubblesManager.this.onEntryUpdated(entry); BubblesManager.this.onEntryUpdated(entry, fromSystem);
} }
@Override @Override
@@ -444,9 +444,10 @@ public class BubblesManager implements Dumpable {
} }
} }
void onEntryUpdated(NotificationEntry entry) { void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
mBubbles.onEntryUpdated(notifToBubbleEntry(entry), mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
mNotificationInterruptStateProvider.shouldBubbleUp(entry)); shouldBubble, fromSystem);
} }
void onEntryRemoved(NotificationEntry entry) { void onEntryRemoved(NotificationEntry entry) {

View File

@@ -33,6 +33,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@ public class BubblesTest extends SysuiTestCase {
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot()); assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update // Send update
mEntryListener.onEntryUpdated(mRow); mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
// Nothing should have changed // Nothing should have changed
// Notif is suppressed after expansion // Notif is suppressed after expansion
@@ -789,7 +790,7 @@ public class BubblesTest extends SysuiTestCase {
@Test @Test
public void testAddNotif_notBubble() { public void testAddNotif_notBubble() {
mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry()); mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry()); mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
assertThat(mBubbleController.hasBubbles()).isFalse(); assertThat(mBubbleController.hasBubbles()).isFalse();
} }
@@ -827,7 +828,7 @@ public class BubblesTest extends SysuiTestCase {
NotificationListenerService.Ranking ranking = new RankingBuilder( NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build(); mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking); mRow.setRanking(ranking);
mEntryListener.onEntryUpdated(mRow); mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertFalse(mBubbleController.hasBubbles()); assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send(); verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@ public class BubblesTest extends SysuiTestCase {
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse(); assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
} }
/**
* Verifies that if a bubble is in the overflow and a non-interruptive notification update
* comes in for it, it stays in the overflow but the entry is updated.
*/
@Test
public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
mEntryListener.onEntryAdded(mRow);
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble so it's in the overflow
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
// Update the entry to not show in shade
setMetadataFlags(mRow,
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
mBubbleController.updateBubble(mBubbleEntry,
/* suppressFlyout= */ false, /* showInShade= */ true);
// Check that the update was applied - shouldn't be show in shade
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
verify(mBubbleController, times(1)).inflateAndAdd(
any(Bubble.class), anyBoolean(), anyBoolean());
}
/**
* Verifies that if a bubble is active, and a non-interruptive notification update comes in for
* it, it doesn't trigger a new inflate and add for that bubble.
*/
@Test
public void testNonInterruptiveUpdate_doesntTriggerInflate() {
mEntryListener.onEntryAdded(mRow);
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Update the entry to not show in shade
setMetadataFlags(mRow,
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
mBubbleController.updateBubble(mBubbleEntry,
/* suppressFlyout= */ false, /* showInShade= */ true);
// Check that the update was applied - shouldn't be show in shade
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
// Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
verify(mBubbleController, times(1)).inflateAndAdd(
any(Bubble.class), anyBoolean(), anyBoolean());
}
/**
* Verifies that if a bubble is in the overflow and a non-interruptive notification update
* comes in for it with FLAG_BUBBLE that the flag is removed.
*/
@Test
public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
mEntryListener.onEntryAdded(mRow);
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
// Dismiss the bubble so it's in the overflow
mBubbleController.removeBubble(
mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
// Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
assertThat(b.isBubble()).isFalse();
// Send a non-notifying update that has FLAG_BUBBLE
mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
mBubbleController.updateBubble(mBubbleEntry,
/* suppressFlyout= */ false, /* showInShade= */ true);
// Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
assertThat(b.isBubble()).isFalse();
}
@Test
public void testNonSystemUpdatesIgnored() {
mEntryListener.onEntryAdded(mRow);
assertThat(mBubbleController.hasBubbles()).isTrue();
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
// Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
verify(mBubbleController, times(1)).inflateAndAdd(
any(Bubble.class), anyBoolean(), anyBoolean());
}
/** Creates a bubble using the userId and package. */ /** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) { private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId); final UserHandle userHandle = new UserHandle(userId);