From d0426628f60d063836c0dce691d4698b12434724 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 11 Jul 2017 13:19:59 +0200 Subject: [PATCH] Reducing bitmap sizes in notifications Bitmap sizes could be arbitrary large when they were sent over to the system. We're now reducing them to reasonable sizes.s Also fixed that notification bitmaps were not put into ashmem anymore since it got lost in a refactor. Test: code inspection Bug: 62319200 Change-Id: I87db7656e749666b9eab1f67fd497f155c407e18 --- core/java/android/app/Notification.java | 84 ++++++++++++++++++++++ core/java/android/app/NotificationManager.java | 1 + core/java/android/widget/RemoteViews.java | 16 +++++ .../layout/notification_template_right_icon.xml | 4 +- core/res/res/values/dimens.xml | 15 ++++ core/res/res/values/symbols.xml | 8 +++ graphics/java/android/graphics/drawable/Icon.java | 37 ++++++++++ 7 files changed, 163 insertions(+), 2 deletions(-) diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index a3377d7440cc..f4277ce3917e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1092,6 +1092,11 @@ public class Notification implements Parcelable public static final String EXTRA_CONTAINS_CUSTOM_VIEW = "android.contains.customView"; /** + * @hide + */ + public static final String EXTRA_REDUCED_IMAGES = "android.reduced.images"; + + /** * {@link #extras} key: the audio contents of this notification. * * This is for use when rendering the notification on an audio-focused interface; @@ -4956,8 +4961,12 @@ public class Notification implements Parcelable buildUnstyled(); if (mStyle != null) { + mStyle.reduceImageSizes(mContext); + mStyle.purgeResources(); mStyle.buildStyled(mN); } + mN.reduceImageSizes(mContext); + if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N && (useExistingRemoteView())) { if (mN.contentView == null) { @@ -5159,6 +5168,52 @@ public class Notification implements Parcelable } /** + * Reduces the image sizes to conform to a maximum allowed size. This also processes all custom + * remote views. + * + * @hide + */ + void reduceImageSizes(Context context) { + if (extras.getBoolean(EXTRA_REDUCED_IMAGES)) { + return; + } + if (mLargeIcon != null || largeIcon != null) { + Resources resources = context.getResources(); + Class style = getNotificationStyle(); + int maxWidth = resources.getDimensionPixelSize(R.dimen.notification_right_icon_size); + int maxHeight = maxWidth; + if (MediaStyle.class.equals(style) + || DecoratedMediaCustomViewStyle.class.equals(style)) { + maxHeight = resources.getDimensionPixelSize( + R.dimen.notification_media_image_max_height); + maxWidth = resources.getDimensionPixelSize( + R.dimen.notification_media_image_max_width); + } + if (mLargeIcon != null) { + mLargeIcon.scaleDownIfNecessary(maxWidth, maxHeight); + } + if (largeIcon != null) { + largeIcon = Icon.scaleDownIfNecessary(largeIcon, maxWidth, maxHeight); + } + } + reduceImageSizesForRemoteView(contentView, context); + reduceImageSizesForRemoteView(headsUpContentView, context); + reduceImageSizesForRemoteView(bigContentView, context); + extras.putBoolean(EXTRA_REDUCED_IMAGES, true); + } + + private void reduceImageSizesForRemoteView(RemoteViews remoteView, Context context) { + if (remoteView != null) { + Resources resources = context.getResources(); + int maxWidth = resources.getDimensionPixelSize( + R.dimen.notification_custom_view_max_image_width); + int maxHeight = resources.getDimensionPixelSize( + R.dimen.notification_custom_view_max_image_height); + remoteView.reduceImageSizes(maxWidth, maxHeight); + } + } + + /** * @return whether this notification is a foreground service notification */ private boolean isForegroundService() { @@ -5459,6 +5514,14 @@ public class Notification implements Parcelable public boolean displayCustomViewInline() { return false; } + + /** + * Reduces the image sizes contained in this style. + * + * @hide + */ + public void reduceImageSizes(Context context) { + } } /** @@ -5557,6 +5620,27 @@ public class Notification implements Parcelable /** * @hide */ + @Override + public void reduceImageSizes(Context context) { + super.reduceImageSizes(context); + Resources resources = context.getResources(); + if (mPicture != null) { + int maxPictureWidth = resources.getDimensionPixelSize( + R.dimen.notification_big_picture_max_height); + int maxPictureHeight = resources.getDimensionPixelSize( + R.dimen.notification_big_picture_max_width); + mPicture = Icon.scaleDownIfNecessary(mPicture, maxPictureWidth, maxPictureHeight); + } + if (mBigLargeIcon != null) { + int rightIconSize = resources.getDimensionPixelSize( + R.dimen.notification_right_icon_size); + mBigLargeIcon.scaleDownIfNecessary(rightIconSize, rightIconSize); + } + } + + /** + * @hide + */ public RemoteViews makeBigContentView() { // Replace mN.mLargeIcon with mBigLargeIcon if mBigLargeIconSet // This covers the following cases: diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 26889a1af1cb..0f0c4bac5f9f 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -307,6 +307,7 @@ public class NotificationManager { } } if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")"); + notification.reduceImageSizes(mContext); ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); boolean isLowRam = am.isLowRamDevice(); final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam); diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 454f5b74aeb4..b77aa1c6888a 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -199,6 +199,22 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Reduces all images and ensures that they are all below the given sizes. + * + * @param maxWidth the maximum width allowed + * @param maxHeight the maximum height allowed + * + * @hide + */ + public void reduceImageSizes(int maxWidth, int maxHeight) { + ArrayList cache = mBitmapCache.mBitmaps; + for (int i = 0; i < cache.size(); i++) { + Bitmap bitmap = cache.get(i); + cache.set(i, Icon.scaleDownIfNecessary(bitmap, maxWidth, maxHeight)); + } + } + + /** * Handle with care! */ static class MutablePair { diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml index fbf75387b786..d379256f7753 100644 --- a/core/res/res/layout/notification_template_right_icon.xml +++ b/core/res/res/layout/notification_template_right_icon.xml @@ -21,8 +21,8 @@ android:layout_height="wrap_content" android:layout_gravity="top|end"> 120dp 800dp + + 284dp + + 450dp + + 284dp + + 416dp + + 140dp + + 280dp + + 40dp + 90% diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index beab29aff1c5..d1344628d7c0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2932,6 +2932,14 @@ + + + + + + + + diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index aa38f31d66a1..c329918afc27 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -805,6 +805,43 @@ public final class Icon implements Parcelable { }; /** + * Scale down a bitmap to a given max width and max height. The scaling will be done in a uniform way + * @param bitmap the bitmap to scale down + * @param maxWidth the maximum width allowed + * @param maxHeight the maximum height allowed + * + * @return the scaled bitmap if necessary or the original bitmap if no scaling was needed + * @hide + */ + public static Bitmap scaleDownIfNecessary(Bitmap bitmap, int maxWidth, int maxHeight) { + int bitmapWidth = bitmap.getWidth(); + int bitmapHeight = bitmap.getHeight(); + if (bitmapWidth > maxWidth || bitmapHeight > maxHeight) { + float scale = Math.min((float) maxWidth / bitmapWidth, + (float) maxHeight / bitmapHeight); + bitmap = Bitmap.createScaledBitmap(bitmap, (int) (scale * bitmapWidth), + (int) (scale * bitmapHeight), true /* filter */); + } + return bitmap; + } + + /** + * Scale down this icon to a given max width and max height. + * The scaling will be done in a uniform way and currently only bitmaps are supported. + * @param maxWidth the maximum width allowed + * @param maxHeight the maximum height allowed + * + * @hide + */ + public void scaleDownIfNecessary(int maxWidth, int maxHeight) { + if (mType != TYPE_BITMAP && mType != TYPE_ADAPTIVE_BITMAP) { + return; + } + Bitmap bitmap = getBitmap(); + setBitmap(scaleDownIfNecessary(bitmap, maxWidth, maxHeight)); + } + + /** * Implement this interface to receive a callback when * {@link #loadDrawableAsync(Context, OnDrawableLoadedListener, Handler) loadDrawableAsync} * is finished and your Drawable is ready. -- 2.11.0