Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3405f17f authored by Jernej Virag's avatar Jernej Virag
Browse files

Downscale oversized MessagingStyle avatars

Notification API downscales oversized icons in all formats except in MessagingStyle. This seems to be an oversight that can cause massive memory usage.
This downscales avatars to a reasonable size if they're too big.

Bug:193720474
Bug:221890932

Test: atest NotificationTest
      Built a test app with 4000x4000 avatar icon.
      Memory usage went from 250MB to ~170KB per notification.
Change-Id: I35377cdb5d61ee6e23fccc2bffb5d3aec6ca2b7d
parent 67936291
Loading
Loading
Loading
Loading
+47 −0
Original line number Original line Diff line number Diff line
@@ -7917,6 +7917,8 @@ public class Notification implements Parcelable
         * @hide
         * @hide
         */
         */
        public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) {
        public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) {
            // TODO(b/228941516): This icon should be downscaled to avoid using too much memory,
            // see reduceImageSizes.
            mShortcutIcon = conversationIcon;
            mShortcutIcon = conversationIcon;
            return this;
            return this;
        }
        }
@@ -8423,6 +8425,51 @@ public class Notification implements Parcelable
            return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
            return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP);
        }
        }
        /**
         * @hide
         */
        @Override
        public void reduceImageSizes(Context context) {
            super.reduceImageSizes(context);
            Resources resources = context.getResources();
            boolean isLowRam = ActivityManager.isLowRamDeviceStatic();
            if (mShortcutIcon != null) {
                int maxSize = resources.getDimensionPixelSize(
                        isLowRam ? R.dimen.notification_small_icon_size_low_ram
                                : R.dimen.notification_small_icon_size);
                mShortcutIcon.scaleDownIfNecessary(maxSize, maxSize);
            }
            int maxAvatarSize = resources.getDimensionPixelSize(
                    isLowRam ? R.dimen.notification_person_icon_max_size
                            : R.dimen.notification_person_icon_max_size_low_ram);
            if (mUser != null && mUser.getIcon() != null) {
                mUser.getIcon().scaleDownIfNecessary(maxAvatarSize, maxAvatarSize);
            }
            reduceMessagesIconSizes(mMessages, maxAvatarSize);
            reduceMessagesIconSizes(mHistoricMessages, maxAvatarSize);
        }
        /**
         * @hide
         */
        private static void reduceMessagesIconSizes(@Nullable List<Message> messages, int maxSize) {
            if (messages == null) {
                return;
            }
            for (Message message : messages) {
                Person sender = message.mSender;
                if (sender != null) {
                    Icon icon = sender.getIcon();
                    if (icon != null) {
                        icon.scaleDownIfNecessary(maxSize, maxSize);
                    }
                }
            }
        }
        public static final class Message {
        public static final class Message {
            /** @hide */
            /** @hide */
            public static final String KEY_TEXT = "text";
            public static final String KEY_TEXT = "text";
+8 −0
Original line number Original line Diff line number Diff line
@@ -779,6 +779,10 @@
    <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen>
    <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen>
    <!-- The alpha of a disabled notification button -->
    <!-- The alpha of a disabled notification button -->
    <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item>
    <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item>
    <!-- The maximum size of Person avatar image in MessagingStyle notifications.
         This is bigger than displayed because listeners can use it for other displays
         e.g. wearables. -->
    <dimen name="notification_person_icon_max_size">144dp</dimen>


    <!-- The maximum size of the small notification icon on low memory devices. -->
    <!-- The maximum size of the small notification icon on low memory devices. -->
    <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
    <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen>
@@ -792,6 +796,10 @@
    <dimen name="notification_big_picture_max_width_low_ram">294dp</dimen>
    <dimen name="notification_big_picture_max_width_low_ram">294dp</dimen>
    <!-- The size of the right icon image when on low ram -->
    <!-- The size of the right icon image when on low ram -->
    <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
    <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen>
    <!-- The maximum size of Person avatar image in MessagingStyle notifications.
     This is bigger than displayed because listeners can use it for other displays
     e.g. wearables. -->
    <dimen name="notification_person_icon_max_size_low_ram">96dp</dimen>
    <!-- The maximum size of the grayscale icon -->
    <!-- The maximum size of the grayscale icon -->
    <dimen name="notification_grayscale_icon_max_size">256dp</dimen>
    <dimen name="notification_grayscale_icon_max_size">256dp</dimen>


+2 −0
Original line number Original line Diff line number Diff line
@@ -3603,6 +3603,7 @@
  <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
  <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_height"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_width"/>
  <java-symbol type="dimen" name="notification_person_icon_max_size" />


  <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
  <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/>
  <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
  <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/>
@@ -3611,6 +3612,7 @@
  <java-symbol type="dimen" name="notification_grayscale_icon_max_size"/>
  <java-symbol type="dimen" name="notification_grayscale_icon_max_size"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_width_low_ram"/>
  <java-symbol type="dimen" name="notification_custom_view_max_image_width_low_ram"/>
  <java-symbol type="dimen" name="notification_person_icon_max_size_low_ram" />


  <!-- Accessibility fingerprint gestures -->
  <!-- Accessibility fingerprint gestures -->
  <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
  <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" />
+64 −0
Original line number Original line Diff line number Diff line
@@ -520,6 +520,70 @@ public class NotificationTest {
                        R.dimen.notification_small_icon_size));
                        R.dimen.notification_small_icon_size));
    }
    }


    @Test
    public void testBuild_ensureMessagingUserIsNotTooBig_resizesIcon() {
        Icon hugeIcon = Icon.createWithBitmap(
                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
        Icon hugeMessageAvatar = Icon.createWithBitmap(
                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
        Icon hugeHistoricMessageAvatar = Icon.createWithBitmap(
                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));

        Notification.MessagingStyle style = new Notification.MessagingStyle(
                new Person.Builder().setIcon(hugeIcon).setName("A User").build());
        style.addMessage(new Notification.MessagingStyle.Message("A message", 123456,
                new Person.Builder().setIcon(hugeMessageAvatar).setName("A Sender").build()));
        style.addHistoricMessage(new Notification.MessagingStyle.Message("A message", 123456,
                new Person.Builder().setIcon(hugeHistoricMessageAvatar).setName(
                        "A Historic Sender").build()));
        Notification notification = new Notification.Builder(mContext, "Channel").setStyle(
                style).build();

        Bitmap personIcon = style.getUser().getIcon().getBitmap();
        assertThat(personIcon.getWidth()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));
        assertThat(personIcon.getHeight()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));

        Bitmap avatarIcon = style.getMessages().get(0).getSenderPerson().getIcon().getBitmap();
        assertThat(avatarIcon.getWidth()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));
        assertThat(avatarIcon.getHeight()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));

        Bitmap historicAvatarIcon = style.getHistoricMessages().get(
                0).getSenderPerson().getIcon().getBitmap();
        assertThat(historicAvatarIcon.getWidth()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));
        assertThat(historicAvatarIcon.getHeight()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_person_icon_max_size));
    }

    @Test
    public void testBuild_ensureMessagingShortcutIconIsNotTooBig_resizesIcon() {
        Icon hugeIcon = Icon.createWithBitmap(
                Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888));
        Notification.MessagingStyle style = new Notification.MessagingStyle(
                new Person.Builder().setName("A User").build()).setShortcutIcon(hugeIcon);

        Notification notification = new Notification.Builder(mContext, "Channel").setStyle(
                style).build();
        Bitmap shortcutIcon = style.getShortcutIcon().getBitmap();

        assertThat(shortcutIcon.getWidth()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_small_icon_size));
        assertThat(shortcutIcon.getHeight()).isEqualTo(
                mContext.getResources().getDimensionPixelSize(
                        R.dimen.notification_small_icon_size));
    }

    @Test
    @Test
    public void testColors_ensureColors_dayMode_producesValidPalette() {
    public void testColors_ensureColors_dayMode_producesValidPalette() {
        Notification.Colors c = new Notification.Colors();
        Notification.Colors c = new Notification.Colors();