diff options
3 files changed, 158 insertions, 2 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 551731824570..234ab936345b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1197,6 +1197,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mMenuRow != null && mMenuRow.getMenuView() != null) { mMenuRow.onConfigurationChanged(); } + if (mImageResolver != null) { + mImageResolver.updateMaxImageSizes(); + } } public void onUiModeChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java index fa4bc2aba21a..52f7c2cfee96 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolver.java @@ -19,12 +19,17 @@ package com.android.systemui.statusbar.notification.row; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ImageResolver; import com.android.internal.widget.LocalImageResolver; import com.android.internal.widget.MessagingMessage; @@ -36,6 +41,10 @@ import java.util.Set; /** * Custom resolver with built-in image cache for image messages. + * + * If the URL points to a bitmap that's larger than the maximum width or height, the bitmap + * will be resized down to that maximum size before being cached. See {@link #getMaxImageWidth()}, + * {@link #getMaxImageHeight()}, and {@link #resolveImage(Uri)} for the downscaling implementation. */ public class NotificationInlineImageResolver implements ImageResolver { private static final String TAG = NotificationInlineImageResolver.class.getSimpleName(); @@ -44,6 +53,13 @@ public class NotificationInlineImageResolver implements ImageResolver { private final ImageCache mImageCache; private Set<Uri> mWantedUriSet; + // max allowed bitmap width, in pixels + @VisibleForTesting + protected int mMaxImageWidth; + // max allowed bitmap height, in pixels + @VisibleForTesting + protected int mMaxImageHeight; + /** * Constructor. * @param context Context. @@ -56,6 +72,8 @@ public class NotificationInlineImageResolver implements ImageResolver { if (mImageCache != null) { mImageCache.setImageResolver(this); } + + updateMaxImageSizes(); } /** @@ -66,14 +84,49 @@ public class NotificationInlineImageResolver implements ImageResolver { return mImageCache != null && !ActivityManager.isLowRamDeviceStatic(); } + private boolean isLowRam() { + return ActivityManager.isLowRamDeviceStatic(); + } + + /** + * Update the maximum width and height allowed for bitmaps, ex. after a configuration change. + */ + public void updateMaxImageSizes() { + mMaxImageWidth = getMaxImageWidth(); + mMaxImageHeight = getMaxImageHeight(); + } + + @VisibleForTesting + protected int getMaxImageWidth() { + return mContext.getResources().getDimensionPixelSize(isLowRam() + ? R.dimen.notification_custom_view_max_image_width_low_ram + : R.dimen.notification_custom_view_max_image_width); + } + + @VisibleForTesting + protected int getMaxImageHeight() { + return mContext.getResources().getDimensionPixelSize(isLowRam() + ? R.dimen.notification_custom_view_max_image_height_low_ram + : R.dimen.notification_custom_view_max_image_height); + } + + @VisibleForTesting + protected BitmapDrawable resolveImageInternal(Uri uri) throws IOException { + return (BitmapDrawable) LocalImageResolver.resolveImage(uri, mContext); + } + /** - * To resolve image from specified uri directly. + * To resolve image from specified uri directly. If the resulting image is larger than the + * maximum allowed size, scale it down. * @param uri Uri of the image. * @return Drawable of the image. * @throws IOException Throws if failed at resolving the image. */ Drawable resolveImage(Uri uri) throws IOException { - return LocalImageResolver.resolveImage(uri, mContext); + BitmapDrawable image = resolveImageInternal(uri); + Bitmap bitmap = image.getBitmap(); + image.setBitmap(Icon.scaleDownIfNecessary(bitmap, mMaxImageWidth, mMaxImageHeight)); + return image; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java new file mode 100644 index 000000000000..7f48cd1313fe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInlineImageResolverTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.net.Uri; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class NotificationInlineImageResolverTest extends SysuiTestCase { + + NotificationInlineImageResolver mResolver; + Bitmap mBitmap; + BitmapDrawable mBitmapDrawable; + Uri mUri; + + @Before + public void setup() { + mResolver = spy(new NotificationInlineImageResolver(mContext, null)); + mBitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); + mBitmapDrawable = new BitmapDrawable(mContext.getResources(), mBitmap); + mUri = mock(Uri.class); + } + + @Test + public void refreshMaxImageSizes() { + assertNotEquals("Starts different height", mResolver.mMaxImageHeight, 20); + assertNotEquals("Starts different width", mResolver.mMaxImageWidth, 15); + + doReturn(20).when(mResolver).getMaxImageHeight(); + doReturn(15).when(mResolver).getMaxImageWidth(); + + mResolver.updateMaxImageSizes(); + + assertEquals("Height matches new config", mResolver.mMaxImageHeight, 20); + assertEquals("Width matches new config", mResolver.mMaxImageWidth, 15); + } + + @Test + public void resolveImage_sizeTooBig() throws IOException { + doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); + mResolver.mMaxImageHeight = 5; + mResolver.mMaxImageWidth = 5; + + // original bitmap size is 10x10 + BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); + Bitmap resolvedBitmap = resolved.getBitmap(); + assertEquals("Bitmap width reduced", 5, resolvedBitmap.getWidth()); + assertEquals("Bitmap height reduced", 5, resolvedBitmap.getHeight()); + assertNotSame("Bitmap replaced", resolvedBitmap, mBitmap); + } + + @Test + public void resolveImage_sizeOK() throws IOException { + doReturn(mBitmapDrawable).when(mResolver).resolveImageInternal(mUri); + mResolver.mMaxImageWidth = 15; + mResolver.mMaxImageHeight = 15; + + // original bitmap size is 10x10 + BitmapDrawable resolved = (BitmapDrawable) mResolver.resolveImage(mUri); + Bitmap resolvedBitmap = resolved.getBitmap(); + assertEquals("Bitmap width unchanged", 10, resolvedBitmap.getWidth()); + assertEquals("Bitmap height unchanged", 10, resolvedBitmap.getHeight()); + assertSame("Bitmap not replaced", resolvedBitmap, mBitmap); + } +} |