Loading services/core/java/com/android/server/notification/GroupHelper.java +37 −20 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.app.Notification.FLAG_GROUP_SUMMARY; import static android.app.Notification.FLAG_LOCAL_ONLY; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.Notification.VISIBILITY_PUBLIC; import android.annotation.NonNull; import android.app.Notification; Loading Loading @@ -145,7 +147,8 @@ public class GroupHelper { mUngroupedNotifications.getOrDefault(key, new ArrayMap<>()); NotificationAttributes attr = new NotificationAttributes(sbn.getNotification().flags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); sbn.getNotification().getSmallIcon(), sbn.getNotification().color, sbn.getNotification().visibility); children.put(sbn.getKey(), attr); mUngroupedNotifications.put(key, children); Loading @@ -158,25 +161,29 @@ public class GroupHelper { if (notificationsToGroup.size() > 0) { if (autogroupSummaryExists) { NotificationAttributes attr = new NotificationAttributes(flags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); sbn.getNotification().getSmallIcon(), sbn.getNotification().color, VISIBILITY_PRIVATE); if (Flags.autogroupSummaryIconUpdate()) { attr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttr, attr); attr = updateAutobundledSummaryAttributes(sbn.getPackageName(), childrenAttr, attr); } mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), attr); } else { Icon summaryIcon = sbn.getNotification().getSmallIcon(); int summaryIconColor = sbn.getNotification().color; int summaryVisibility = VISIBILITY_PRIVATE; if (Flags.autogroupSummaryIconUpdate()) { // Calculate the initial summary icon and icon color NotificationAttributes iconAttr = getAutobundledSummaryIconAndColor( // Calculate the initial summary icon, icon color and visibility NotificationAttributes iconAttr = getAutobundledSummaryAttributes( sbn.getPackageName(), childrenAttr); summaryIcon = iconAttr.icon; summaryIconColor = iconAttr.iconColor; summaryVisibility = iconAttr.visibility; } NotificationAttributes attr = new NotificationAttributes(flags, summaryIcon, summaryIconColor); summaryIconColor, summaryVisibility); mCallback.addAutoGroupSummary(sbn.getUserId(), sbn.getPackageName(), sbn.getKey(), attr); } Loading Loading @@ -238,18 +245,19 @@ public class GroupHelper { mCallback.removeAutoGroupSummary(userId, sbn.getPackageName()); } else { NotificationAttributes attr = new NotificationAttributes(summaryFlags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); boolean iconUpdated = false; sbn.getNotification().getSmallIcon(), sbn.getNotification().color, VISIBILITY_PRIVATE); boolean attributesUpdated = false; if (Flags.autogroupSummaryIconUpdate()) { NotificationAttributes newAttr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttrs, attr); NotificationAttributes newAttr = updateAutobundledSummaryAttributes( sbn.getPackageName(), childrenAttrs, attr); if (!newAttr.equals(attr)) { iconUpdated = true; attributesUpdated = true; attr = newAttr; } } if (updateSummaryFlags || iconUpdated) { if (updateSummaryFlags || attributesUpdated) { mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), attr); } } Loading @@ -268,12 +276,13 @@ public class GroupHelper { } } NotificationAttributes getAutobundledSummaryIconAndColor(@NonNull String packageName, NotificationAttributes getAutobundledSummaryAttributes(@NonNull String packageName, @NonNull List<NotificationAttributes> childrenAttr) { Icon newIcon = null; boolean childrenHaveSameIcon = true; int newColor = Notification.COLOR_INVALID; boolean childrenHaveSameColor = true; int newVisibility = VISIBILITY_PRIVATE; // Both the icon drawable and the icon background color are updated according to this rule: // - if all child icons are identical => use the common icon Loading @@ -296,6 +305,10 @@ public class GroupHelper { childrenHaveSameColor = false; } } // Check for visibility. If at least one child is public, then set to public if (state.visibility == VISIBILITY_PUBLIC) { newVisibility = VISIBILITY_PUBLIC; } } if (!childrenHaveSameIcon) { newIcon = getMonochromeAppIcon(packageName); Loading @@ -304,13 +317,13 @@ public class GroupHelper { newColor = COLOR_DEFAULT; } return new NotificationAttributes(0, newIcon, newColor); return new NotificationAttributes(0, newIcon, newColor, newVisibility); } NotificationAttributes updateAutobundledSummaryIcon(@NonNull String packageName, NotificationAttributes updateAutobundledSummaryAttributes(@NonNull String packageName, @NonNull List<NotificationAttributes> childrenAttr, @NonNull NotificationAttributes oldAttr) { NotificationAttributes newAttr = getAutobundledSummaryIconAndColor(packageName, NotificationAttributes newAttr = getAutobundledSummaryAttributes(packageName, childrenAttr); Icon newIcon = newAttr.icon; int newColor = newAttr.iconColor; Loading @@ -321,7 +334,7 @@ public class GroupHelper { newColor = oldAttr.iconColor; } return new NotificationAttributes(oldAttr.flags, newIcon, newColor); return new NotificationAttributes(oldAttr.flags, newIcon, newColor, newAttr.visibility); } /** Loading Loading @@ -358,17 +371,20 @@ public class GroupHelper { public final int flags; public final int iconColor; public final Icon icon; public final int visibility; public NotificationAttributes(int flags, Icon icon, int iconColor) { public NotificationAttributes(int flags, Icon icon, int iconColor, int visibility) { this.flags = flags; this.icon = icon; this.iconColor = iconColor; this.visibility = visibility; } public NotificationAttributes(@NonNull NotificationAttributes attr) { this.flags = attr.flags; this.icon = attr.icon; this.iconColor = attr.iconColor; this.visibility = attr.visibility; } @Override Loading @@ -379,12 +395,13 @@ public class GroupHelper { if (!(o instanceof NotificationAttributes that)) { return false; } return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon); return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon) && visibility == that.visibility; } @Override public int hashCode() { return Objects.hash(flags, iconColor, icon); return Objects.hash(flags, iconColor, icon, visibility); } } Loading services/core/java/com/android/server/notification/NotificationManagerService.java +9 −5 Original line number Diff line number Diff line Loading @@ -1038,15 +1038,17 @@ public class NotificationManagerService extends SystemService { } int oldFlags = summary.getSbn().getNotification().flags; boolean iconUpdated = boolean attributesUpdated = !summaryAttr.icon.sameAs(summary.getSbn().getNotification().getSmallIcon()) || summaryAttr.iconColor != summary.getSbn().getNotification().color; || summaryAttr.iconColor != summary.getSbn().getNotification().color || summaryAttr.visibility != summary.getSbn().getNotification().visibility; if (oldFlags != summaryAttr.flags || iconUpdated) { if (oldFlags != summaryAttr.flags || attributesUpdated) { summary.getSbn().getNotification().flags = summaryAttr.flags != GroupHelper.FLAG_INVALID ? summaryAttr.flags : oldFlags; summary.getSbn().getNotification().setSmallIcon(summaryAttr.icon); summary.getSbn().getNotification().color = summaryAttr.iconColor; summary.getSbn().getNotification().visibility = summaryAttr.visibility; mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground, mPostNotificationTrackerFactory.newTracker(null))); } Loading Loading @@ -2939,7 +2941,8 @@ public class NotificationManagerService extends SystemService { public void addAutoGroupSummary(int userId, String pkg, String triggeringKey, NotificationAttributes summaryAttr) { NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey, summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor); summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor, summaryAttr.visibility); if (r != null) { final boolean isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; Loading Loading @@ -6725,7 +6728,7 @@ public class NotificationManagerService extends SystemService { // Creates a 'fake' summary for a package that has exceeded the solo-notification limit. NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey, int flagsToSet, Icon summaryIcon, int summaryIconColor) { int flagsToSet, Icon summaryIcon, int summaryIconColor, int summaryVisibilty) { NotificationRecord summaryRecord = null; boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId); synchronized (mNotificationLock) { Loading Loading @@ -6760,6 +6763,7 @@ public class NotificationManagerService extends SystemService { .setGroup(GroupHelper.AUTOGROUP_KEY) .setFlag(flagsToSet, true) .setColor(summaryIconColor) .setVisibility(summaryVisibilty) .build(); summaryNotification.extras.putAll(extras); Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg); Loading services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +102 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ import static android.app.Notification.FLAG_CAN_COLORIZE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.Notification.VISIBILITY_PUBLIC; import static android.app.Notification.VISIBILITY_SECRET; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.server.notification.GroupHelper.BASE_FLAGS; Loading Loading @@ -81,6 +84,8 @@ public class GroupHelperTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private final int DEFAULT_VISIBILITY = VISIBILITY_PRIVATE; private @Mock GroupHelper.Callback mCallback; private @Mock PackageManager mPackageManager; Loading Loading @@ -127,7 +132,7 @@ public class GroupHelperTest extends UiServiceTestCase { } private NotificationAttributes getNotificationAttributes(int flags) { return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT); return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY); } @Test Loading Loading @@ -704,7 +709,8 @@ public class GroupHelperTest extends UiServiceTestCase { final Icon icon = mock(Icon.class); when(icon.sameAs(icon)).thenReturn(true); final int iconColor = Color.BLUE; final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor); final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, DEFAULT_VISIBILITY); // Add notifications with same icon and color for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { Loading Loading @@ -744,7 +750,7 @@ public class GroupHelperTest extends UiServiceTestCase { doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg)); final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS, initialIcon, initialIconColor); initialIcon, initialIconColor, DEFAULT_VISIBILITY); // Add notifications with same icon and color for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { Loading @@ -769,7 +775,42 @@ public class GroupHelperTest extends UiServiceTestCase { // Summary should be updated to the default color and the icon to the monochrome icon NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon, COLOR_DEFAULT); COLOR_DEFAULT, DEFAULT_VISIBILITY); verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr)); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAddSummary_diffVisibility() { final String pkg = "package"; final Icon icon = mock(Icon.class); when(icon.sameAs(icon)).thenReturn(true); final int iconColor = Color.BLUE; final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, VISIBILITY_PRIVATE); // Add notifications with same icon and color and default visibility (private) for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null, icon, iconColor); mGroupHelper.onNotificationPosted(sbn, false); } // Check that the summary has private visibility verify(mCallback, times(1)).addAutoGroupSummary( anyInt(), eq(pkg), anyString(), eq(attr)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); // After auto-grouping, add new notification with public visibility StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT, String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor); sbn.getNotification().visibility = VISIBILITY_PUBLIC; mGroupHelper.onNotificationPosted(sbn, true); // Check that the summary visibility was updated NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, VISIBILITY_PUBLIC); verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr)); } Loading @@ -781,7 +822,7 @@ public class GroupHelperTest extends UiServiceTestCase { when(initialIcon.sameAs(initialIcon)).thenReturn(true); final int initialIconColor = Color.BLUE; final NotificationAttributes initialAttr = new NotificationAttributes( GroupHelper.FLAG_INVALID, initialIcon, initialIconColor); GroupHelper.FLAG_INVALID, initialIcon, initialIconColor, DEFAULT_VISIBILITY); // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color ArrayList<StatusBarNotification> notifications = new ArrayList<>(); Loading Loading @@ -817,11 +858,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with the same icon List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT)); childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT, DEFAULT_VISIBILITY)); } //Check that the generated summary icon is the same as the child notifications' Icon summaryIcon = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon; Icon summaryIcon = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon; assertThat(summaryIcon).isEqualTo(icon); } Loading @@ -837,11 +879,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with different icons List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT, DEFAULT_VISIBILITY)); } // Check that the generated summary icon is the monochrome icon Icon summaryIcon = groupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon; Icon summaryIcon = groupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon; assertThat(summaryIcon).isEqualTo(monochromeIcon); } Loading @@ -853,11 +896,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with the same icon color List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, DEFAULT_VISIBILITY)); } // Check that the generated summary icon color is the same as the child notifications' int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).iconColor; assertThat(summaryIconColor).isEqualTo(iconColor); } Loading @@ -869,15 +913,60 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with different icon colors List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i, DEFAULT_VISIBILITY)); } // Check that the generated summary icon color is the default color int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).iconColor; assertThat(summaryIconColor).isEqualTo(Notification.COLOR_DEFAULT); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAutobundledSummaryVisibility_hasPublicChildren() { final String pkg = "package"; final int iconColor = Color.BLUE; // Create notifications with private and public visibility List<NotificationAttributes> childrenAttr = new ArrayList<>(); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, VISIBILITY_PUBLIC)); for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, VISIBILITY_PRIVATE)); } // Check that the generated summary visibility is public int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).visibility; assertThat(summaryVisibility).isEqualTo(VISIBILITY_PUBLIC); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAutobundledSummaryVisibility_noPublicChildren() { final String pkg = "package"; final int iconColor = Color.BLUE; int visibility = VISIBILITY_PRIVATE; // Create notifications with either private or secret visibility List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { if (i % 2 == 0) { visibility = VISIBILITY_PRIVATE; } else { visibility = VISIBILITY_SECRET; } childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, visibility)); } // Check that the generated summary visibility is private int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).visibility; assertThat(summaryVisibility).isEqualTo(VISIBILITY_PRIVATE); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testMonochromeAppIcon_adaptiveIconExists() throws Exception { Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +11 −8 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.Notification.FLAG_USER_INITIATED_JOB; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; Loading Loading @@ -2338,7 +2339,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.updateAutobundledSummaryLocked(0, "pkg", new NotificationAttributes(GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0), false); mock(Icon.class), 0, VISIBILITY_PRIVATE), false); waitForIdle(); assertTrue(summary.getSbn().isOngoing()); Loading @@ -2357,7 +2358,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.updateAutobundledSummaryLocked(0, "pkg", new NotificationAttributes(GroupHelper.BASE_FLAGS, mock(Icon.class), 0), false); mock(Icon.class), 0, VISIBILITY_PRIVATE), false); waitForIdle(); assertFalse(summary.getSbn().isOngoing()); Loading Loading @@ -3479,7 +3480,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true); NotificationRecord r = mService.createAutoGroupSummary(temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0); temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0, VISIBILITY_PRIVATE); assertThat(r.isImportanceFixed()).isTrue(); } Loading Loading @@ -12215,9 +12217,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // grouphelper is a mock here, so make the calls it would make // add summary mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0)); mService.addNotification( mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // cancel both children mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(), Loading Loading @@ -12246,7 +12249,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(nr1); mService.addNotification( mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0)); nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // add notifications + summary for USER_ALL NotificationRecord nr0_all = Loading @@ -12259,7 +12262,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification( mService.createAutoGroupSummary(nr0_all.getUserId(), nr0_all.getSbn().getPackageName(), nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0)); nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // cancel both children for USER_ALL mBinderService.cancelNotificationWithTag(PKG, PKG, nr0_all.getSbn().getTag(), Loading
services/core/java/com/android/server/notification/GroupHelper.java +37 −20 Original line number Diff line number Diff line Loading @@ -22,6 +22,8 @@ import static android.app.Notification.FLAG_GROUP_SUMMARY; import static android.app.Notification.FLAG_LOCAL_ONLY; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.Notification.VISIBILITY_PUBLIC; import android.annotation.NonNull; import android.app.Notification; Loading Loading @@ -145,7 +147,8 @@ public class GroupHelper { mUngroupedNotifications.getOrDefault(key, new ArrayMap<>()); NotificationAttributes attr = new NotificationAttributes(sbn.getNotification().flags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); sbn.getNotification().getSmallIcon(), sbn.getNotification().color, sbn.getNotification().visibility); children.put(sbn.getKey(), attr); mUngroupedNotifications.put(key, children); Loading @@ -158,25 +161,29 @@ public class GroupHelper { if (notificationsToGroup.size() > 0) { if (autogroupSummaryExists) { NotificationAttributes attr = new NotificationAttributes(flags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); sbn.getNotification().getSmallIcon(), sbn.getNotification().color, VISIBILITY_PRIVATE); if (Flags.autogroupSummaryIconUpdate()) { attr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttr, attr); attr = updateAutobundledSummaryAttributes(sbn.getPackageName(), childrenAttr, attr); } mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), attr); } else { Icon summaryIcon = sbn.getNotification().getSmallIcon(); int summaryIconColor = sbn.getNotification().color; int summaryVisibility = VISIBILITY_PRIVATE; if (Flags.autogroupSummaryIconUpdate()) { // Calculate the initial summary icon and icon color NotificationAttributes iconAttr = getAutobundledSummaryIconAndColor( // Calculate the initial summary icon, icon color and visibility NotificationAttributes iconAttr = getAutobundledSummaryAttributes( sbn.getPackageName(), childrenAttr); summaryIcon = iconAttr.icon; summaryIconColor = iconAttr.iconColor; summaryVisibility = iconAttr.visibility; } NotificationAttributes attr = new NotificationAttributes(flags, summaryIcon, summaryIconColor); summaryIconColor, summaryVisibility); mCallback.addAutoGroupSummary(sbn.getUserId(), sbn.getPackageName(), sbn.getKey(), attr); } Loading Loading @@ -238,18 +245,19 @@ public class GroupHelper { mCallback.removeAutoGroupSummary(userId, sbn.getPackageName()); } else { NotificationAttributes attr = new NotificationAttributes(summaryFlags, sbn.getNotification().getSmallIcon(), sbn.getNotification().color); boolean iconUpdated = false; sbn.getNotification().getSmallIcon(), sbn.getNotification().color, VISIBILITY_PRIVATE); boolean attributesUpdated = false; if (Flags.autogroupSummaryIconUpdate()) { NotificationAttributes newAttr = updateAutobundledSummaryIcon(sbn.getPackageName(), childrenAttrs, attr); NotificationAttributes newAttr = updateAutobundledSummaryAttributes( sbn.getPackageName(), childrenAttrs, attr); if (!newAttr.equals(attr)) { iconUpdated = true; attributesUpdated = true; attr = newAttr; } } if (updateSummaryFlags || iconUpdated) { if (updateSummaryFlags || attributesUpdated) { mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), attr); } } Loading @@ -268,12 +276,13 @@ public class GroupHelper { } } NotificationAttributes getAutobundledSummaryIconAndColor(@NonNull String packageName, NotificationAttributes getAutobundledSummaryAttributes(@NonNull String packageName, @NonNull List<NotificationAttributes> childrenAttr) { Icon newIcon = null; boolean childrenHaveSameIcon = true; int newColor = Notification.COLOR_INVALID; boolean childrenHaveSameColor = true; int newVisibility = VISIBILITY_PRIVATE; // Both the icon drawable and the icon background color are updated according to this rule: // - if all child icons are identical => use the common icon Loading @@ -296,6 +305,10 @@ public class GroupHelper { childrenHaveSameColor = false; } } // Check for visibility. If at least one child is public, then set to public if (state.visibility == VISIBILITY_PUBLIC) { newVisibility = VISIBILITY_PUBLIC; } } if (!childrenHaveSameIcon) { newIcon = getMonochromeAppIcon(packageName); Loading @@ -304,13 +317,13 @@ public class GroupHelper { newColor = COLOR_DEFAULT; } return new NotificationAttributes(0, newIcon, newColor); return new NotificationAttributes(0, newIcon, newColor, newVisibility); } NotificationAttributes updateAutobundledSummaryIcon(@NonNull String packageName, NotificationAttributes updateAutobundledSummaryAttributes(@NonNull String packageName, @NonNull List<NotificationAttributes> childrenAttr, @NonNull NotificationAttributes oldAttr) { NotificationAttributes newAttr = getAutobundledSummaryIconAndColor(packageName, NotificationAttributes newAttr = getAutobundledSummaryAttributes(packageName, childrenAttr); Icon newIcon = newAttr.icon; int newColor = newAttr.iconColor; Loading @@ -321,7 +334,7 @@ public class GroupHelper { newColor = oldAttr.iconColor; } return new NotificationAttributes(oldAttr.flags, newIcon, newColor); return new NotificationAttributes(oldAttr.flags, newIcon, newColor, newAttr.visibility); } /** Loading Loading @@ -358,17 +371,20 @@ public class GroupHelper { public final int flags; public final int iconColor; public final Icon icon; public final int visibility; public NotificationAttributes(int flags, Icon icon, int iconColor) { public NotificationAttributes(int flags, Icon icon, int iconColor, int visibility) { this.flags = flags; this.icon = icon; this.iconColor = iconColor; this.visibility = visibility; } public NotificationAttributes(@NonNull NotificationAttributes attr) { this.flags = attr.flags; this.icon = attr.icon; this.iconColor = attr.iconColor; this.visibility = attr.visibility; } @Override Loading @@ -379,12 +395,13 @@ public class GroupHelper { if (!(o instanceof NotificationAttributes that)) { return false; } return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon); return flags == that.flags && iconColor == that.iconColor && icon.sameAs(that.icon) && visibility == that.visibility; } @Override public int hashCode() { return Objects.hash(flags, iconColor, icon); return Objects.hash(flags, iconColor, icon, visibility); } } Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +9 −5 Original line number Diff line number Diff line Loading @@ -1038,15 +1038,17 @@ public class NotificationManagerService extends SystemService { } int oldFlags = summary.getSbn().getNotification().flags; boolean iconUpdated = boolean attributesUpdated = !summaryAttr.icon.sameAs(summary.getSbn().getNotification().getSmallIcon()) || summaryAttr.iconColor != summary.getSbn().getNotification().color; || summaryAttr.iconColor != summary.getSbn().getNotification().color || summaryAttr.visibility != summary.getSbn().getNotification().visibility; if (oldFlags != summaryAttr.flags || iconUpdated) { if (oldFlags != summaryAttr.flags || attributesUpdated) { summary.getSbn().getNotification().flags = summaryAttr.flags != GroupHelper.FLAG_INVALID ? summaryAttr.flags : oldFlags; summary.getSbn().getNotification().setSmallIcon(summaryAttr.icon); summary.getSbn().getNotification().color = summaryAttr.iconColor; summary.getSbn().getNotification().visibility = summaryAttr.visibility; mHandler.post(new EnqueueNotificationRunnable(userId, summary, isAppForeground, mPostNotificationTrackerFactory.newTracker(null))); } Loading Loading @@ -2939,7 +2941,8 @@ public class NotificationManagerService extends SystemService { public void addAutoGroupSummary(int userId, String pkg, String triggeringKey, NotificationAttributes summaryAttr) { NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey, summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor); summaryAttr.flags, summaryAttr.icon, summaryAttr.iconColor, summaryAttr.visibility); if (r != null) { final boolean isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND; Loading Loading @@ -6725,7 +6728,7 @@ public class NotificationManagerService extends SystemService { // Creates a 'fake' summary for a package that has exceeded the solo-notification limit. NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey, int flagsToSet, Icon summaryIcon, int summaryIconColor) { int flagsToSet, Icon summaryIcon, int summaryIconColor, int summaryVisibilty) { NotificationRecord summaryRecord = null; boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId); synchronized (mNotificationLock) { Loading Loading @@ -6760,6 +6763,7 @@ public class NotificationManagerService extends SystemService { .setGroup(GroupHelper.AUTOGROUP_KEY) .setFlag(flagsToSet, true) .setColor(summaryIconColor) .setVisibility(summaryVisibilty) .build(); summaryNotification.extras.putAll(extras); Intent appIntent = getContext().getPackageManager().getLaunchIntentForPackage(pkg); Loading
services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java +102 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,9 @@ import static android.app.Notification.FLAG_CAN_COLORIZE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.Notification.VISIBILITY_PUBLIC; import static android.app.Notification.VISIBILITY_SECRET; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static com.android.server.notification.GroupHelper.BASE_FLAGS; Loading Loading @@ -81,6 +84,8 @@ public class GroupHelperTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private final int DEFAULT_VISIBILITY = VISIBILITY_PRIVATE; private @Mock GroupHelper.Callback mCallback; private @Mock PackageManager mPackageManager; Loading Loading @@ -127,7 +132,7 @@ public class GroupHelperTest extends UiServiceTestCase { } private NotificationAttributes getNotificationAttributes(int flags) { return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT); return new NotificationAttributes(flags, mSmallIcon, COLOR_DEFAULT, DEFAULT_VISIBILITY); } @Test Loading Loading @@ -704,7 +709,8 @@ public class GroupHelperTest extends UiServiceTestCase { final Icon icon = mock(Icon.class); when(icon.sameAs(icon)).thenReturn(true); final int iconColor = Color.BLUE; final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor); final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, DEFAULT_VISIBILITY); // Add notifications with same icon and color for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { Loading Loading @@ -744,7 +750,7 @@ public class GroupHelperTest extends UiServiceTestCase { doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg)); final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS, initialIcon, initialIconColor); initialIcon, initialIconColor, DEFAULT_VISIBILITY); // Add notifications with same icon and color for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { Loading @@ -769,7 +775,42 @@ public class GroupHelperTest extends UiServiceTestCase { // Summary should be updated to the default color and the icon to the monochrome icon NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon, COLOR_DEFAULT); COLOR_DEFAULT, DEFAULT_VISIBILITY); verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr)); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAddSummary_diffVisibility() { final String pkg = "package"; final Icon icon = mock(Icon.class); when(icon.sameAs(icon)).thenReturn(true); final int iconColor = Color.BLUE; final NotificationAttributes attr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, VISIBILITY_PRIVATE); // Add notifications with same icon and color and default visibility (private) for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null, icon, iconColor); mGroupHelper.onNotificationPosted(sbn, false); } // Check that the summary has private visibility verify(mCallback, times(1)).addAutoGroupSummary( anyInt(), eq(pkg), anyString(), eq(attr)); verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroup(anyString()); verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString()); // After auto-grouping, add new notification with public visibility StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT, String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, icon, iconColor); sbn.getNotification().visibility = VISIBILITY_PUBLIC; mGroupHelper.onNotificationPosted(sbn, true); // Check that the summary visibility was updated NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, icon, iconColor, VISIBILITY_PUBLIC); verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr)); } Loading @@ -781,7 +822,7 @@ public class GroupHelperTest extends UiServiceTestCase { when(initialIcon.sameAs(initialIcon)).thenReturn(true); final int initialIconColor = Color.BLUE; final NotificationAttributes initialAttr = new NotificationAttributes( GroupHelper.FLAG_INVALID, initialIcon, initialIconColor); GroupHelper.FLAG_INVALID, initialIcon, initialIconColor, DEFAULT_VISIBILITY); // Add AUTOGROUP_AT_COUNT-1 notifications with same icon and color ArrayList<StatusBarNotification> notifications = new ArrayList<>(); Loading Loading @@ -817,11 +858,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with the same icon List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT)); childrenAttr.add(new NotificationAttributes(0, icon, COLOR_DEFAULT, DEFAULT_VISIBILITY)); } //Check that the generated summary icon is the same as the child notifications' Icon summaryIcon = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon; Icon summaryIcon = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon; assertThat(summaryIcon).isEqualTo(icon); } Loading @@ -837,11 +879,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with different icons List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), COLOR_DEFAULT, DEFAULT_VISIBILITY)); } // Check that the generated summary icon is the monochrome icon Icon summaryIcon = groupHelper.getAutobundledSummaryIconAndColor(pkg, childrenAttr).icon; Icon summaryIcon = groupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).icon; assertThat(summaryIcon).isEqualTo(monochromeIcon); } Loading @@ -853,11 +896,12 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with the same icon color List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, DEFAULT_VISIBILITY)); } // Check that the generated summary icon color is the same as the child notifications' int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).iconColor; assertThat(summaryIconColor).isEqualTo(iconColor); } Loading @@ -869,15 +913,60 @@ public class GroupHelperTest extends UiServiceTestCase { // Create notifications with different icon colors List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i)); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), i, DEFAULT_VISIBILITY)); } // Check that the generated summary icon color is the default color int summaryIconColor = mGroupHelper.getAutobundledSummaryIconAndColor(pkg, int summaryIconColor = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).iconColor; assertThat(summaryIconColor).isEqualTo(Notification.COLOR_DEFAULT); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAutobundledSummaryVisibility_hasPublicChildren() { final String pkg = "package"; final int iconColor = Color.BLUE; // Create notifications with private and public visibility List<NotificationAttributes> childrenAttr = new ArrayList<>(); childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, VISIBILITY_PUBLIC)); for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) { childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, VISIBILITY_PRIVATE)); } // Check that the generated summary visibility is public int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).visibility; assertThat(summaryVisibility).isEqualTo(VISIBILITY_PUBLIC); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testAutobundledSummaryVisibility_noPublicChildren() { final String pkg = "package"; final int iconColor = Color.BLUE; int visibility = VISIBILITY_PRIVATE; // Create notifications with either private or secret visibility List<NotificationAttributes> childrenAttr = new ArrayList<>(); for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) { if (i % 2 == 0) { visibility = VISIBILITY_PRIVATE; } else { visibility = VISIBILITY_SECRET; } childrenAttr.add(new NotificationAttributes(0, mock(Icon.class), iconColor, visibility)); } // Check that the generated summary visibility is private int summaryVisibility = mGroupHelper.getAutobundledSummaryAttributes(pkg, childrenAttr).visibility; assertThat(summaryVisibility).isEqualTo(VISIBILITY_PRIVATE); } @Test @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE) public void testMonochromeAppIcon_adaptiveIconExists() throws Exception { Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +11 −8 Original line number Diff line number Diff line Loading @@ -37,6 +37,7 @@ import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; import static android.app.Notification.FLAG_USER_INITIATED_JOB; import static android.app.Notification.VISIBILITY_PRIVATE; import static android.app.NotificationChannel.USER_LOCKED_ALLOW_BUBBLE; import static android.app.NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED; import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; Loading Loading @@ -2338,7 +2339,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.updateAutobundledSummaryLocked(0, "pkg", new NotificationAttributes(GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0), false); mock(Icon.class), 0, VISIBILITY_PRIVATE), false); waitForIdle(); assertTrue(summary.getSbn().isOngoing()); Loading @@ -2357,7 +2358,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.updateAutobundledSummaryLocked(0, "pkg", new NotificationAttributes(GroupHelper.BASE_FLAGS, mock(Icon.class), 0), false); mock(Icon.class), 0, VISIBILITY_PRIVATE), false); waitForIdle(); assertFalse(summary.getSbn().isOngoing()); Loading Loading @@ -3479,7 +3480,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true); NotificationRecord r = mService.createAutoGroupSummary(temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0); temp.getSbn().getPackageName(), temp.getKey(), 0, mock(Icon.class), 0, VISIBILITY_PRIVATE); assertThat(r.isImportanceFixed()).isTrue(); } Loading Loading @@ -12215,9 +12217,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // grouphelper is a mock here, so make the calls it would make // add summary mService.addNotification(mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0)); mService.addNotification( mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS | FLAG_ONGOING_EVENT, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // cancel both children mBinderService.cancelNotificationWithTag(PKG, PKG, nr0.getSbn().getTag(), Loading Loading @@ -12246,7 +12249,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(nr1); mService.addNotification( mService.createAutoGroupSummary(nr1.getUserId(), nr1.getSbn().getPackageName(), nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0)); nr1.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // add notifications + summary for USER_ALL NotificationRecord nr0_all = Loading @@ -12259,7 +12262,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification( mService.createAutoGroupSummary(nr0_all.getUserId(), nr0_all.getSbn().getPackageName(), nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0)); nr0_all.getKey(), GroupHelper.BASE_FLAGS, mock(Icon.class), 0, VISIBILITY_PRIVATE)); // cancel both children for USER_ALL mBinderService.cancelNotificationWithTag(PKG, PKG, nr0_all.getSbn().getTag(),