diff options
| -rw-r--r-- | core/java/com/android/internal/widget/LocalImageResolver.java | 137 | ||||
| -rw-r--r-- | core/tests/coretests/res/drawable/big_a.png | bin | 11905 -> 0 bytes | |||
| -rw-r--r-- | core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java | 169 |
3 files changed, 18 insertions, 288 deletions
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java index 66a3ff950577..616b69961b79 100644 --- a/core/java/com/android/internal/widget/LocalImageResolver.java +++ b/core/java/com/android/internal/widget/LocalImageResolver.java @@ -16,25 +16,21 @@ package com.android.internal.widget; -import android.annotation.DrawableRes; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Bitmap; import android.graphics.ImageDecoder; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.util.Size; -import com.android.internal.annotations.VisibleForTesting; - import java.io.IOException; /** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */ public class LocalImageResolver { + private static final String TAG = LocalImageResolver.class.getSimpleName(); - @VisibleForTesting - static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480; + private static final int MAX_SAFE_ICON_SIZE_PX = 480; /** * Resolve an image from the given Uri using {@link ImageDecoder} @@ -42,20 +38,9 @@ public class LocalImageResolver { public static Drawable resolveImage(Uri uri, Context context) throws IOException { final ImageDecoder.Source source = ImageDecoder.createSource(context.getContentResolver(), uri); - return ImageDecoder.decodeDrawable(source, - (decoder, info, s) -> LocalImageResolver.onHeaderDecoded(decoder, info, - DEFAULT_MAX_SAFE_ICON_SIZE_PX, DEFAULT_MAX_SAFE_ICON_SIZE_PX)); - } - - /** - * Get the drawable from Icon using {@link ImageDecoder} if it contains a Uri, or - * using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's, - * tint, if present, to the drawable. - */ - public static Drawable resolveImage(@Nullable Icon icon, Context context) - throws IOException { - return resolveImage(icon, context, DEFAULT_MAX_SAFE_ICON_SIZE_PX, - DEFAULT_MAX_SAFE_ICON_SIZE_PX); + final Drawable drawable = + ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded); + return drawable; } /** @@ -63,38 +48,17 @@ public class LocalImageResolver { * using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's, * tint, if present, to the drawable. */ - @Nullable - public static Drawable resolveImage(@Nullable Icon icon, Context context, int maxWidth, - int maxHeight) - throws IOException { - if (icon == null) { - return null; - } - - switch (icon.getType()) { - case Icon.TYPE_URI: - case Icon.TYPE_URI_ADAPTIVE_BITMAP: - Uri uri = getResolvableUri(icon); - if (uri != null) { - Drawable result = resolveImage(uri, context, maxWidth, maxHeight); - return tintDrawable(icon, result); - } - break; - case Icon.TYPE_RESOURCE: - Drawable result = resolveImage(icon.getResId(), context, maxWidth, maxHeight); - if (result != null) { - return tintDrawable(icon, result); - } - break; - case Icon.TYPE_BITMAP: - case Icon.TYPE_ADAPTIVE_BITMAP: - return resolveBitmapImage(icon, context, maxWidth, maxHeight); - case Icon.TYPE_DATA: // We can't really improve on raw data images. - default: - break; + public static Drawable resolveImage(Icon icon, Context context) throws IOException { + Uri uri = getResolvableUri(icon); + if (uri != null) { + Drawable result = resolveImage(uri, context); + if (icon.hasTint()) { + result.mutate(); + result.setTintList(icon.getTintList()); + result.setTintBlendMode(icon.getTintBlendMode()); + } + return result; } - - // Fallback to straight drawable load if we fail with more efficient approach. return icon.loadDrawable(context); } @@ -102,71 +66,7 @@ public class LocalImageResolver { throws IOException { final ImageDecoder.Source source = ImageDecoder.createSource(context.getContentResolver(), uri); - return resolveImage(source, maxWidth, maxHeight); - } - - /** - * Attempts to resolve the resource as a bitmap drawable constrained within max sizes. - * - * @return decoded drawable or null if the passed resource is not a straight bitmap - */ - @Nullable - public static Drawable resolveImage(@DrawableRes int resId, Context context, int maxWidth, - int maxHeight) - throws IOException { - final ImageDecoder.Source source = ImageDecoder.createSource(context.getResources(), resId); - // It's possible that the resource isn't an actual bitmap drawable so this decode can fail. - // Return null in that case. - try { - return resolveImage(source, maxWidth, maxHeight); - } catch (ImageDecoder.DecodeException e) { - return null; - } - } - - @Nullable - private static Drawable resolveBitmapImage(Icon icon, Context context, int maxWidth, - int maxHeight) { - Bitmap bitmap = icon.getBitmap(); - if (bitmap == null) { - return null; - } - - if (bitmap.getWidth() > maxWidth || bitmap.getHeight() > maxHeight) { - Icon smallerIcon = icon.getType() == Icon.TYPE_ADAPTIVE_BITMAP - ? Icon.createWithAdaptiveBitmap(bitmap) : Icon.createWithBitmap(bitmap); - // We don't want to modify the source icon, create a copy. - smallerIcon.setTintList(icon.getTintList()) - .setTintBlendMode(icon.getTintBlendMode()) - .scaleDownIfNecessary(maxWidth, maxHeight); - return smallerIcon.loadDrawable(context); - } - - return icon.loadDrawable(context); - } - - @Nullable - private static Drawable tintDrawable(Icon icon, @Nullable Drawable drawable) { - if (drawable == null) { - return null; - } - - if (icon.hasTint()) { - drawable.mutate(); - drawable.setTintList(icon.getTintList()); - drawable.setTintBlendMode(icon.getTintBlendMode()); - } - - return drawable; - } - - private static Drawable resolveImage(ImageDecoder.Source source, int maxWidth, int maxHeight) - throws IOException { return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> { - if (maxWidth <= 0 || maxHeight <= 0) { - return; - } - final Size size = info.getSize(); if (size.getWidth() > size.getHeight()) { if (size.getWidth() > maxWidth) { @@ -188,12 +88,11 @@ public class LocalImageResolver { } private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info, - int maxWidth, int maxHeight) { + ImageDecoder.Source source) { final Size size = info.getSize(); final int originalSize = Math.max(size.getHeight(), size.getWidth()); - final int maxSize = Math.max(maxWidth, maxHeight); - final double ratio = (originalSize > maxSize) - ? originalSize * 1f / maxSize + final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX) + ? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX : 1.0; decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio)); } diff --git a/core/tests/coretests/res/drawable/big_a.png b/core/tests/coretests/res/drawable/big_a.png Binary files differdeleted file mode 100644 index dc059a3557a8..000000000000 --- a/core/tests/coretests/res/drawable/big_a.png +++ /dev/null diff --git a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java b/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java deleted file mode 100644 index 8dcb4a27b24d..000000000000 --- a/core/tests/coretests/src/com/android/internal/widget/LocalImageResolverTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2022 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.internal.widget; - -import android.content.Context; -import android.graphics.BitmapFactory; -import android.graphics.drawable.AdaptiveIconDrawable; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.Icon; - -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.frameworks.coretests.R; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.IOException; - -@RunWith(AndroidJUnit4ClassRunner.class) -public class LocalImageResolverTest { - - private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); - - @Test - public void resolveImage_largeBitmapIcon_defaultSize_resizeToDefaultSize() throws - IOException { - Icon icon = Icon.createWithBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a)); - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - // No isLessOrEqualThan sadly. - assertThat(bd.getBitmap().getWidth()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - assertThat(bd.getBitmap().getHeight()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - } - - @Test - public void resolveImage_largeAdaptiveBitmapIcon_defaultSize_resizeToDefaultSize() throws - IOException { - Icon icon = Icon.createWithAdaptiveBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a)); - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - - assertThat(d).isInstanceOf(AdaptiveIconDrawable.class); - BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground(); - // No isLessOrEqualThan sadly. - assertThat(bd.getBitmap().getWidth()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - assertThat(bd.getBitmap().getHeight()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - } - - @Test - public void resolveImage_largeResourceIcon_defaultSize_resizeToDefaultSize() throws - IOException { - Icon icon = Icon.createWithResource(mContext, R.drawable.big_a); - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - // No isLessOrEqualThan sadly. - assertThat(bd.getBitmap().getWidth()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - assertThat(bd.getBitmap().getHeight()).isLessThan( - LocalImageResolver.DEFAULT_MAX_SAFE_ICON_SIZE_PX + 1); - } - - @Test - public void resolveImage_largeResourceIcon_passedSize_resizeToDefinedSize() throws - IOException { - Icon icon = Icon.createWithResource(mContext, R.drawable.big_a); - Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - assertThat(bd.getBitmap().getWidth()).isLessThan(101); - assertThat(bd.getBitmap().getHeight()).isLessThan(51); - } - - @Test - public void resolveImage_largeBitmapIcon_passedSize_resizeToDefinedSize() throws - IOException { - Icon icon = Icon.createWithBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a)); - Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - assertThat(bd.getBitmap().getWidth()).isLessThan(101); - assertThat(bd.getBitmap().getHeight()).isLessThan(51); - } - - @Test - public void resolveImage_largeAdaptiveBitmapIcon_passedSize_resizeToDefinedSize() throws - IOException { - Icon icon = Icon.createWithAdaptiveBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.big_a)); - Drawable d = LocalImageResolver.resolveImage(icon, mContext, 100, 50); - - assertThat(d).isInstanceOf(AdaptiveIconDrawable.class); - BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground(); - assertThat(bd.getBitmap().getWidth()).isLessThan(101); - assertThat(bd.getBitmap().getHeight()).isLessThan(51); - } - - - @Test - public void resolveImage_smallResourceIcon_defaultSize_untouched() throws IOException { - Icon icon = Icon.createWithResource(mContext, R.drawable.test32x24); - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - assertThat(bd.getBitmap().getWidth()).isEqualTo(32); - assertThat(bd.getBitmap().getHeight()).isEqualTo(24); - } - - @Test - public void resolveImage_smallBitmapIcon_defaultSize_untouched() throws IOException { - Icon icon = Icon.createWithBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24)); - final int originalWidth = icon.getBitmap().getWidth(); - final int originalHeight = icon.getBitmap().getHeight(); - - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - - assertThat(d).isInstanceOf(BitmapDrawable.class); - BitmapDrawable bd = (BitmapDrawable) d; - assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth); - assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight); - } - - @Test - public void resolveImage_smallAdaptiveBitmapIcon_defaultSize_untouched() throws IOException { - Icon icon = Icon.createWithAdaptiveBitmap( - BitmapFactory.decodeResource(mContext.getResources(), R.drawable.test32x24)); - final int originalWidth = icon.getBitmap().getWidth(); - final int originalHeight = icon.getBitmap().getHeight(); - - Drawable d = LocalImageResolver.resolveImage(icon, mContext); - assertThat(d).isInstanceOf(AdaptiveIconDrawable.class); - BitmapDrawable bd = (BitmapDrawable) ((AdaptiveIconDrawable) d).getForeground(); - assertThat(bd.getBitmap().getWidth()).isEqualTo(originalWidth); - assertThat(bd.getBitmap().getHeight()).isEqualTo(originalHeight); - - } -} |