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 +++++++++++++++++++ .../java/android/app/NotificationManager.java | 1 + core/java/android/widget/RemoteViews.java | 16 ++++ .../notification_template_right_icon.xml | 4 +- core/res/res/values/dimens.xml | 15 ++++ core/res/res/values/symbols.xml | 8 ++ .../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 a3377d7440ccf..f4277ce3917e5 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1091,6 +1091,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. * @@ -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) { @@ -5158,6 +5167,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 */ @@ -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) { + } } /** @@ -5554,6 +5617,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 */ diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 26889a1af1cb9..0f0c4bac5f9f3 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 454f5b74aeb45..b77aa1c6888a9 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -198,6 +198,22 @@ public class RemoteViews implements Parcelable, Filter { mActions.add(new SetRemoteInputsAction(viewId, remoteInputs)); } + /** + * 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! */ diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml index fbf75387b786d..d379256f7753f 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 beab29aff1c5d..d1344628d7c03 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 aa38f31d66a16..c329918afc27b 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -804,6 +804,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}