diff options
3 files changed, 226 insertions, 12 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index 92ceb604d2e0..c376fad1503f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -70,6 +70,8 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; @@ -106,6 +108,7 @@ import kotlinx.coroutines.test.TestScope; import org.mockito.ArgumentCaptor; +import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; @@ -338,6 +341,46 @@ public class NotificationTestHelper { } /** + * Returns an {@link GroupEntry} group with the given number of child + * notifications. + */ + public GroupEntry createGroupEntry(int numChildren, + @Nullable List<NotificationEntry> additionalChildren) { + Notification summary = new Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .setGroupSummary(true) + .setGroup(GROUP_KEY) + .build(); + + NotificationEntry summaryEntry = new NotificationEntryBuilder() + .setPkg(PKG) + .setOpPkg(PKG) + .setId(mId++) + .setUid(UID) + .setInitialPid(2000) + .setNotification(summary) + .setParent(GroupEntry.ROOT_ENTRY) + .build(); + GroupEntryBuilder groupEntry = new GroupEntryBuilder() + .setSummary(summaryEntry); + + for (int i = 0; i < numChildren; i++) { + NotificationEntry child = new NotificationEntryBuilder() + .setParent(GroupEntry.ROOT_ENTRY) + .setNotification(new Notification.Builder(mContext, "") + .setSmallIcon(R.drawable.ic_person) + .setGroup(GROUP_KEY) + .build()) + .build(); + groupEntry.addChild(child); + } + for (NotificationEntry entry : additionalChildren) { + groupEntry.addChild(entry); + } + return groupEntry.build(); + } + + /** * Returns an {@link ExpandableNotificationRow} group with the given number of child * notifications. */ @@ -411,6 +454,23 @@ public class NotificationTestHelper { } /** + * Returns an {@link NotificationEntry} that should be shown as a bubble and is part + * of a group of notifications. + */ + public NotificationEntry createBubbleEntryInGroup() throws Exception { + Notification n = createNotification(false /* isGroupSummary */, + GROUP_KEY /* groupKey */, + makeBubbleMetadata(null /* deleteIntent */, false /* autoExpand */)); + n.flags |= FLAG_BUBBLE; + NotificationEntry entry = generateEntry(n, PKG, UID, USER_HANDLE, + mDefaultInflationFlags, IMPORTANCE_HIGH); + modifyRanking(entry) + .setCanBubble(true) + .build(); + return entry; + } + + /** * Returns an {@link ExpandableNotificationRow} that should be shown as a bubble and is part * of a group of notifications. */ @@ -573,6 +633,41 @@ public class NotificationTestHelper { return mKeyguardBypassController; } + private NotificationEntry generateEntry( + Notification notification, + String pkg, + int uid, + UserHandle userHandle, + @InflationFlag int extraInflationFlags, + int importance) + throws Exception { + final NotificationChannel channel = + new NotificationChannel( + notification.getChannelId(), + notification.getChannelId(), + importance); + channel.setBlockable(true); + + NotificationEntry entry = new NotificationEntryBuilder() + .setPkg(pkg) + .setOpPkg(pkg) + .setId(mId++) + .setUid(uid) + .setInitialPid(2000) + .setNotification(notification) + .setUser(userHandle) + .setPostTime(System.currentTimeMillis()) + .setChannel(channel) + .updateRanking(rankingBuilder -> rankingBuilder.setIsConversation( + notification.isStyle(Notification.MessagingStyle.class) + )) + .build(); + + + return entry; + } + + private ExpandableNotificationRow generateRow( Notification notification, String pkg, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index fb2a66c94e09..c072ede250e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -589,25 +589,45 @@ public final class NotificationEntry extends ListEntry { /** * Get the children that are actually attached to this notification's row. * - * TODO: Seems like most callers here should probably be using - * {@link GroupMembershipManager#getChildren(PipelineEntry)} + * TODO: Seems like most callers here should be asking a PipelineEntry, not a NotificationEntry */ public @Nullable List<NotificationEntry> getAttachedNotifChildren() { - if (row == null) { - return null; - } + if (NotificationBundleUi.isEnabled()) { + if (isGroupSummary()) { + return ((GroupEntry) getParent()).getChildren(); + } + } else { + if (row == null) { + return null; + } - List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren(); - if (rowChildren == null) { - return null; + List<ExpandableNotificationRow> rowChildren = row.getAttachedChildren(); + if (rowChildren == null) { + return null; + } + + ArrayList<NotificationEntry> children = new ArrayList<>(); + for (ExpandableNotificationRow child : rowChildren) { + children.add(child.getEntry()); + } + + return children; } + return null; + } - ArrayList<NotificationEntry> children = new ArrayList<>(); - for (ExpandableNotificationRow child : rowChildren) { - children.add(child.getEntry()); + private boolean isGroupSummary() { + if (getParent() == null) { + // The entry is not attached, so it doesn't count. + return false; + } + PipelineEntry pipelineEntry = getParent(); + if (!(pipelineEntry instanceof GroupEntry groupEntry)) { + return false; } - return children; + // If entry is a summary, its parent is a GroupEntry with summary = entry. + return groupEntry.getSummary() == this; } public void notifyFullScreenIntentLaunched() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 5e9582593422..8281132e7502 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -135,6 +135,8 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.RankingBuilder; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; +import com.android.systemui.statusbar.notification.collection.GroupEntry; +import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; @@ -151,6 +153,7 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti import com.android.systemui.statusbar.notification.interruption.VisualInterruptionDecisionProviderTestUtil; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; +import com.android.systemui.statusbar.notification.shared.NotificationBundleUi; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.BatteryController; @@ -1229,8 +1232,36 @@ public class BubblesTest extends SysuiTestCase { } @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade() throws Exception { // GIVEN a group summary with a bubble child + NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup(); + GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry( + 0, List.of(groupedBubble)); + mEntryListener.onEntryAdded(groupedBubble); + when(mCommonNotifCollection.getEntry(groupedBubble.getKey())) + .thenReturn(groupedBubble); + assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey())); + + // WHEN the summary is dismissed + mBubblesManager.handleDismissalInterception(groupSummary.getSummary()); + + // THEN the summary and bubbled child are suppressed from the shade + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + groupedBubble.getKey(), + groupedBubble.getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getKey(), + groupedBubble.getSbn().getGroupKey())); + assertTrue(mBubbleData.isSummarySuppressed( + groupSummary.getSummary().getSbn().getGroupKey())); + } + + @Test + @DisableFlags(NotificationBundleUi.FLAG_NAME) + public void testBubbleSummaryDismissal_suppressesSummaryAndBubbleFromShade_rows() + throws Exception { + // GIVEN a group summary with a bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); @@ -1253,8 +1284,33 @@ public class BubblesTest extends SysuiTestCase { } @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) public void testAppRemovesSummary_removesAllBubbleChildren() throws Exception { // GIVEN a group summary with a bubble child + NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup(); + GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry( + 0, List.of(groupedBubble)); + mEntryListener.onEntryAdded(groupedBubble); + when(mCommonNotifCollection.getEntry(groupedBubble.getKey())) + .thenReturn(groupedBubble); + assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey())); + + // GIVEN the summary is dismissed + mBubblesManager.handleDismissalInterception(groupSummary.getSummary()); + + // WHEN the summary is cancelled by the app + mEntryListener.onEntryRemoved(groupSummary.getSummary(), REASON_APP_CANCEL); + + // THEN the summary and its children are removed from bubble data + assertFalse(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey())); + assertFalse(mBubbleData.isSummarySuppressed( + groupSummary.getSummary().getSbn().getGroupKey())); + } + + @Test + @DisableFlags(NotificationBundleUi.FLAG_NAME) + public void testAppRemovesSummary_removesAllBubbleChildren_rows() throws Exception { + // GIVEN a group summary with a bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(0); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); @@ -1276,9 +1332,52 @@ public class BubblesTest extends SysuiTestCase { } @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren() throws Exception { // GIVEN a group summary with two (non-bubble) children and one bubble child + NotificationEntry groupedBubble = mNotificationTestHelper.createBubbleEntryInGroup(); + GroupEntry groupSummary = mNotificationTestHelper.createGroupEntry( + 2, List.of(groupedBubble)); + mEntryListener.onEntryAdded(groupedBubble); + when(mCommonNotifCollection.getEntry(groupedBubble.getKey())) + .thenReturn(groupedBubble); + + // WHEN the summary is dismissed + mBubblesManager.handleDismissalInterception(groupSummary.getSummary()); + + // THEN only the NON-bubble children are dismissed + List<NotificationEntry> children = groupSummary.getChildren(); + verify(mNotifCallback, times(1)).removeNotification( + eq(children.get(0)), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); + verify(mNotifCallback, times(1)).removeNotification( + eq(children.get(1)), any(), eq(REASON_GROUP_SUMMARY_CANCELED)); + verify(mNotifCallback, never()).removeNotification(eq(groupedBubble), + any(), anyInt()); + + // THEN the bubble child still exists as a bubble and is suppressed from the shade + assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getKey())); + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + groupedBubble.getKey(), + groupedBubble.getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupedBubble.getKey(), + groupedBubble.getSbn().getGroupKey())); + + // THEN the summary is also suppressed from the shade + assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade( + groupSummary.getSummary().getKey(), + groupSummary.getSummary().getSbn().getGroupKey())); + assertTrue(mBubbleController.getImplCachedState().isBubbleNotificationSuppressedFromShade( + groupSummary.getSummary().getKey(), + groupSummary.getSummary().getSbn().getGroupKey())); + } + + @Test + @DisableFlags(NotificationBundleUi.FLAG_NAME) + public void testSummaryDismissalMarksBubblesHiddenFromShadeAndDismissesNonBubbledChildren_row() + throws Exception { + // GIVEN a group summary with two (non-bubble) children and one bubble child ExpandableNotificationRow groupSummary = mNotificationTestHelper.createGroup(2); ExpandableNotificationRow groupedBubble = mNotificationTestHelper.createBubbleInGroup(); mEntryListener.onEntryAdded(groupedBubble.getEntry()); |