Loading core/java/android/app/Notification.java +38 −28 Original line number Diff line number Diff line Loading @@ -3266,6 +3266,43 @@ public class Notification implements Parcelable return false; } /** * @hide */ public static boolean areIconsDifferent(Notification first, Notification second) { return areIconsMaybeDifferent(first.getSmallIcon(), second.getSmallIcon()) || areIconsMaybeDifferent(first.getLargeIcon(), second.getLargeIcon()); } /** * Note that we aren't actually comparing the contents of the bitmaps here; this is only a * cursory inspection. We will not return false negatives, but false positives are likely. */ private static boolean areIconsMaybeDifferent(Icon a, Icon b) { if (a == b) { return false; } if (a == null || b == null) { return true; } if (a.sameAs(b)) { return false; } final int aType = a.getType(); if (aType != b.getType()) { return true; } if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) { final Bitmap aBitmap = a.getBitmap(); final Bitmap bBitmap = b.getBitmap(); return aBitmap.getWidth() != bBitmap.getWidth() || aBitmap.getHeight() != bBitmap.getHeight() || aBitmap.getConfig() != bBitmap.getConfig() || aBitmap.getGenerationId() != bBitmap.getGenerationId(); } return true; } /** * @hide */ Loading Loading @@ -7643,8 +7680,6 @@ public class Notification implements Parcelable /** * @hide * Note that we aren't actually comparing the contents of the bitmaps here, so this * is only doing a cursory inspection. Bitmaps of equal size will appear the same. */ @Override public boolean areNotificationsVisiblyDifferent(Style other) { Loading @@ -7652,32 +7687,7 @@ public class Notification implements Parcelable return true; } BigPictureStyle otherS = (BigPictureStyle) other; return areIconsObviouslyDifferent(getBigPicture(), otherS.getBigPicture()); } private static boolean areIconsObviouslyDifferent(Icon a, Icon b) { if (a == b) { return false; } if (a == null || b == null) { return true; } if (a.sameAs(b)) { return false; } final int aType = a.getType(); if (aType != b.getType()) { return true; } if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) { final Bitmap aBitmap = a.getBitmap(); final Bitmap bBitmap = b.getBitmap(); return aBitmap.getWidth() != bBitmap.getWidth() || aBitmap.getHeight() != bBitmap.getHeight() || aBitmap.getConfig() != bBitmap.getConfig() || aBitmap.getGenerationId() != bBitmap.getGenerationId(); } return true; return areIconsMaybeDifferent(getBigPicture(), otherS.getBigPicture()); } } Loading core/tests/coretests/src/android/app/NotificationTest.java +63 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.app.Notification.CallStyle; import android.content.Context; import android.content.Intent; import android.content.LocusId; Loading Loading @@ -1038,6 +1037,69 @@ public class NotificationTest { Assert.assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId()); } @Test public void areIconsDifferent_sameSmallIcon_false() { Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isFalse(); } @Test public void areIconsDifferent_differentSmallIcon_true() { Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_sameLargeIcon_false() { Icon icon1 = Icon.createWithContentUri("uri"); Icon icon2 = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon2).build(); // Note that this will almost certainly not happen for Icons created from Bitmaps, since // their serialization/deserialization of Bitmaps (app -> system_process) results in a // different getGenerationId() value. :( assertThat(Notification.areIconsDifferent(n1, n2)).isFalse(); } @Test public void areIconsDifferent_differentLargeIcon_true() { Icon icon1 = Icon.createWithContentUri("uri1"); Icon icon2 = Icon.createWithContentUri("uri2"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(2).setLargeIcon(icon2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_addedLargeIcon_true() { Icon icon = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(2).setLargeIcon(icon).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_removedLargeIcon_true() { Icon icon = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void testStyleChangeVisiblyDifferent_noStyles() { Notification.Builder n1 = new Notification.Builder(mContext, "test"); Loading services/core/java/com/android/server/notification/NotificationManagerService.java +10 −9 Original line number Diff line number Diff line Loading @@ -7803,7 +7803,8 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") @VisibleForTesting protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) { protected boolean isVisuallyInterruptive(@Nullable NotificationRecord old, @NonNull NotificationRecord r) { // Ignore summary updates because we don't display most of the information. if (r.getSbn().isGroup() && r.getSbn().getNotification().isGroupSummary()) { if (DEBUG_INTERRUPTIVENESS) { Loading @@ -7821,14 +7822,6 @@ public class NotificationManagerService extends SystemService { return true; } if (r == null) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " + r.getKey() + " is not interruptive: null"); } return false; } Notification oldN = old.getSbn().getNotification(); Notification newN = r.getSbn().getNotification(); if (oldN.extras == null || newN.extras == null) { Loading Loading @@ -7886,6 +7879,14 @@ public class NotificationManagerService extends SystemService { return true; } if (Notification.areIconsDifferent(oldN, newN)) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " + r.getKey() + " is interruptive: icons differ"); } return true; } // Fields below are invisible to bubbles. if (r.canBubble()) { if (DEBUG_INTERRUPTIVENESS) { Loading services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +51 −0 Original line number Diff line number Diff line Loading @@ -5840,6 +5840,57 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(mService.isVisuallyInterruptive(null, r2)); } @Test public void testVisualDifference_sameImages() { Icon large = Icon.createWithResource(mContext, 1); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isFalse(); } @Test public void testVisualDifference_differentSmallImage() { Icon large = Icon.createWithResource(mContext, 1); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(2).setLargeIcon(large).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue(); } @Test public void testVisualDifference_differentLargeImage() { Icon large1 = Icon.createWithResource(mContext, 1); Icon large2 = Icon.createWithResource(mContext, 2); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large1).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large2).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue(); } private NotificationRecord notificationToRecord(Notification n) { return new NotificationRecord( mContext, new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, n, UserHandle.getUserHandleForUid(mUid), null, 0), mock(NotificationChannel.class)); } @Test public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() { // post 2 notification from this package Loading Loading
core/java/android/app/Notification.java +38 −28 Original line number Diff line number Diff line Loading @@ -3266,6 +3266,43 @@ public class Notification implements Parcelable return false; } /** * @hide */ public static boolean areIconsDifferent(Notification first, Notification second) { return areIconsMaybeDifferent(first.getSmallIcon(), second.getSmallIcon()) || areIconsMaybeDifferent(first.getLargeIcon(), second.getLargeIcon()); } /** * Note that we aren't actually comparing the contents of the bitmaps here; this is only a * cursory inspection. We will not return false negatives, but false positives are likely. */ private static boolean areIconsMaybeDifferent(Icon a, Icon b) { if (a == b) { return false; } if (a == null || b == null) { return true; } if (a.sameAs(b)) { return false; } final int aType = a.getType(); if (aType != b.getType()) { return true; } if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) { final Bitmap aBitmap = a.getBitmap(); final Bitmap bBitmap = b.getBitmap(); return aBitmap.getWidth() != bBitmap.getWidth() || aBitmap.getHeight() != bBitmap.getHeight() || aBitmap.getConfig() != bBitmap.getConfig() || aBitmap.getGenerationId() != bBitmap.getGenerationId(); } return true; } /** * @hide */ Loading Loading @@ -7643,8 +7680,6 @@ public class Notification implements Parcelable /** * @hide * Note that we aren't actually comparing the contents of the bitmaps here, so this * is only doing a cursory inspection. Bitmaps of equal size will appear the same. */ @Override public boolean areNotificationsVisiblyDifferent(Style other) { Loading @@ -7652,32 +7687,7 @@ public class Notification implements Parcelable return true; } BigPictureStyle otherS = (BigPictureStyle) other; return areIconsObviouslyDifferent(getBigPicture(), otherS.getBigPicture()); } private static boolean areIconsObviouslyDifferent(Icon a, Icon b) { if (a == b) { return false; } if (a == null || b == null) { return true; } if (a.sameAs(b)) { return false; } final int aType = a.getType(); if (aType != b.getType()) { return true; } if (aType == Icon.TYPE_BITMAP || aType == Icon.TYPE_ADAPTIVE_BITMAP) { final Bitmap aBitmap = a.getBitmap(); final Bitmap bBitmap = b.getBitmap(); return aBitmap.getWidth() != bBitmap.getWidth() || aBitmap.getHeight() != bBitmap.getHeight() || aBitmap.getConfig() != bBitmap.getConfig() || aBitmap.getGenerationId() != bBitmap.getGenerationId(); } return true; return areIconsMaybeDifferent(getBigPicture(), otherS.getBigPicture()); } } Loading
core/tests/coretests/src/android/app/NotificationTest.java +63 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,6 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.annotation.Nullable; import android.app.Notification.CallStyle; import android.content.Context; import android.content.Intent; import android.content.LocusId; Loading Loading @@ -1038,6 +1037,69 @@ public class NotificationTest { Assert.assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId()); } @Test public void areIconsDifferent_sameSmallIcon_false() { Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isFalse(); } @Test public void areIconsDifferent_differentSmallIcon_true() { Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_sameLargeIcon_false() { Icon icon1 = Icon.createWithContentUri("uri"); Icon icon2 = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon2).build(); // Note that this will almost certainly not happen for Icons created from Bitmaps, since // their serialization/deserialization of Bitmaps (app -> system_process) results in a // different getGenerationId() value. :( assertThat(Notification.areIconsDifferent(n1, n2)).isFalse(); } @Test public void areIconsDifferent_differentLargeIcon_true() { Icon icon1 = Icon.createWithContentUri("uri1"); Icon icon2 = Icon.createWithContentUri("uri2"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(2).setLargeIcon(icon2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_addedLargeIcon_true() { Icon icon = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test").setSmallIcon(1).build(); Notification n2 = new Notification.Builder(mContext, "test") .setSmallIcon(2).setLargeIcon(icon).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void areIconsDifferent_removedLargeIcon_true() { Icon icon = Icon.createWithContentUri("uri"); Notification n1 = new Notification.Builder(mContext, "test") .setSmallIcon(1).setLargeIcon(icon).build(); Notification n2 = new Notification.Builder(mContext, "test").setSmallIcon(2).build(); assertThat(Notification.areIconsDifferent(n1, n2)).isTrue(); } @Test public void testStyleChangeVisiblyDifferent_noStyles() { Notification.Builder n1 = new Notification.Builder(mContext, "test"); Loading
services/core/java/com/android/server/notification/NotificationManagerService.java +10 −9 Original line number Diff line number Diff line Loading @@ -7803,7 +7803,8 @@ public class NotificationManagerService extends SystemService { */ @GuardedBy("mNotificationLock") @VisibleForTesting protected boolean isVisuallyInterruptive(NotificationRecord old, NotificationRecord r) { protected boolean isVisuallyInterruptive(@Nullable NotificationRecord old, @NonNull NotificationRecord r) { // Ignore summary updates because we don't display most of the information. if (r.getSbn().isGroup() && r.getSbn().getNotification().isGroupSummary()) { if (DEBUG_INTERRUPTIVENESS) { Loading @@ -7821,14 +7822,6 @@ public class NotificationManagerService extends SystemService { return true; } if (r == null) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " + r.getKey() + " is not interruptive: null"); } return false; } Notification oldN = old.getSbn().getNotification(); Notification newN = r.getSbn().getNotification(); if (oldN.extras == null || newN.extras == null) { Loading Loading @@ -7886,6 +7879,14 @@ public class NotificationManagerService extends SystemService { return true; } if (Notification.areIconsDifferent(oldN, newN)) { if (DEBUG_INTERRUPTIVENESS) { Slog.v(TAG, "INTERRUPTIVENESS: " + r.getKey() + " is interruptive: icons differ"); } return true; } // Fields below are invisible to bubbles. if (r.canBubble()) { if (DEBUG_INTERRUPTIVENESS) { Loading
services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +51 −0 Original line number Diff line number Diff line Loading @@ -5840,6 +5840,57 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(mService.isVisuallyInterruptive(null, r2)); } @Test public void testVisualDifference_sameImages() { Icon large = Icon.createWithResource(mContext, 1); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isFalse(); } @Test public void testVisualDifference_differentSmallImage() { Icon large = Icon.createWithResource(mContext, 1); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(2).setLargeIcon(large).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue(); } @Test public void testVisualDifference_differentLargeImage() { Icon large1 = Icon.createWithResource(mContext, 1); Icon large2 = Icon.createWithResource(mContext, 2); Notification n1 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large1).build(); Notification n2 = new Notification.Builder(mContext, "channel") .setSmallIcon(1).setLargeIcon(large2).build(); NotificationRecord r1 = notificationToRecord(n1); NotificationRecord r2 = notificationToRecord(n2); assertThat(mService.isVisuallyInterruptive(r1, r2)).isTrue(); } private NotificationRecord notificationToRecord(Notification n) { return new NotificationRecord( mContext, new StatusBarNotification(PKG, PKG, 0, "tag", mUid, 0, n, UserHandle.getUserHandleForUid(mUid), null, 0), mock(NotificationChannel.class)); } @Test public void testHideAndUnhideNotificationsOnSuspendedPackageBroadcast() { // post 2 notification from this package Loading