Merge changes from topic "large-icons-tm" into tm-dev
* changes: Size restrict right notification icon size Downscale large bitmaps in CachingIconView Support downscaling of Drawable icons in LocalImageResolver
This commit is contained in:
@@ -23,6 +23,7 @@ import android.annotation.Nullable;
|
|||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@@ -35,6 +36,9 @@ import android.view.RemotableViewMethod;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
|
import com.android.internal.R;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@@ -55,9 +59,42 @@ public class CachingIconView extends ImageView {
|
|||||||
private int mBackgroundColor;
|
private int mBackgroundColor;
|
||||||
private boolean mWillBeForceHidden;
|
private boolean mWillBeForceHidden;
|
||||||
|
|
||||||
|
private int mMaxDrawableWidth = -1;
|
||||||
|
private int mMaxDrawableHeight = -1;
|
||||||
|
|
||||||
|
public CachingIconView(Context context) {
|
||||||
|
this(context, null, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
|
||||||
public CachingIconView(Context context, @Nullable AttributeSet attrs) {
|
public CachingIconView(Context context, @Nullable AttributeSet attrs) {
|
||||||
super(context, attrs);
|
this(context, attrs, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachingIconView(Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr) {
|
||||||
|
this(context, attrs, defStyleAttr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CachingIconView(Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr, int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
init(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
|
||||||
|
int defStyleRes) {
|
||||||
|
if (attrs == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypedArray ta = context.obtainStyledAttributes(attrs,
|
||||||
|
R.styleable.CachingIconView, defStyleAttr, defStyleRes);
|
||||||
|
mMaxDrawableWidth = ta.getDimensionPixelSize(R.styleable
|
||||||
|
.CachingIconView_maxDrawableWidth, -1);
|
||||||
|
mMaxDrawableHeight = ta.getDimensionPixelSize(R.styleable
|
||||||
|
.CachingIconView_maxDrawableHeight, -1);
|
||||||
|
ta.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,15 +103,31 @@ public class CachingIconView extends ImageView {
|
|||||||
if (!testAndSetCache(icon)) {
|
if (!testAndSetCache(icon)) {
|
||||||
mInternalSetDrawable = true;
|
mInternalSetDrawable = true;
|
||||||
// This calls back to setImageDrawable, make sure we don't clear the cache there.
|
// This calls back to setImageDrawable, make sure we don't clear the cache there.
|
||||||
super.setImageIcon(icon);
|
Drawable drawable = loadSizeRestrictedIcon(icon);
|
||||||
|
if (drawable == null) {
|
||||||
|
super.setImageIcon(icon);
|
||||||
|
} else {
|
||||||
|
super.setImageDrawable(drawable);
|
||||||
|
}
|
||||||
mInternalSetDrawable = false;
|
mInternalSetDrawable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Drawable loadSizeRestrictedIcon(@Nullable Icon icon) {
|
||||||
|
try {
|
||||||
|
return LocalImageResolver.resolveImage(icon, getContext(), mMaxDrawableWidth,
|
||||||
|
mMaxDrawableHeight);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Runnable setImageIconAsync(@Nullable Icon icon) {
|
public Runnable setImageIconAsync(@Nullable final Icon icon) {
|
||||||
resetCache();
|
resetCache();
|
||||||
return super.setImageIconAsync(icon);
|
Drawable drawable = loadSizeRestrictedIcon(icon);
|
||||||
|
return () -> setImageDrawable(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,14 +136,34 @@ public class CachingIconView extends ImageView {
|
|||||||
if (!testAndSetCache(resId)) {
|
if (!testAndSetCache(resId)) {
|
||||||
mInternalSetDrawable = true;
|
mInternalSetDrawable = true;
|
||||||
// This calls back to setImageDrawable, make sure we don't clear the cache there.
|
// This calls back to setImageDrawable, make sure we don't clear the cache there.
|
||||||
super.setImageResource(resId);
|
Drawable drawable = loadSizeRestrictedDrawable(resId);
|
||||||
|
if (drawable == null) {
|
||||||
|
super.setImageResource(resId);
|
||||||
|
} else {
|
||||||
|
super.setImageDrawable(drawable);
|
||||||
|
}
|
||||||
mInternalSetDrawable = false;
|
mInternalSetDrawable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Drawable loadSizeRestrictedDrawable(@DrawableRes int resId) {
|
||||||
|
try {
|
||||||
|
return LocalImageResolver.resolveImage(resId, getContext(), mMaxDrawableWidth,
|
||||||
|
mMaxDrawableHeight);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Runnable setImageResourceAsync(@DrawableRes int resId) {
|
public Runnable setImageResourceAsync(@DrawableRes int resId) {
|
||||||
resetCache();
|
resetCache();
|
||||||
|
Drawable drawable = loadSizeRestrictedDrawable(resId);
|
||||||
|
if (drawable != null) {
|
||||||
|
return () -> setImageDrawable(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
return super.setImageResourceAsync(resId);
|
return super.setImageResourceAsync(resId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,13 +171,35 @@ public class CachingIconView extends ImageView {
|
|||||||
@RemotableViewMethod(asyncImpl="setImageURIAsync")
|
@RemotableViewMethod(asyncImpl="setImageURIAsync")
|
||||||
public void setImageURI(@Nullable Uri uri) {
|
public void setImageURI(@Nullable Uri uri) {
|
||||||
resetCache();
|
resetCache();
|
||||||
super.setImageURI(uri);
|
Drawable drawable = loadSizeRestrictedUri(uri);
|
||||||
|
if (drawable == null) {
|
||||||
|
super.setImageURI(uri);
|
||||||
|
} else {
|
||||||
|
mInternalSetDrawable = true;
|
||||||
|
super.setImageDrawable(drawable);
|
||||||
|
mInternalSetDrawable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Drawable loadSizeRestrictedUri(@Nullable Uri uri) {
|
||||||
|
try {
|
||||||
|
return LocalImageResolver.resolveImage(uri, getContext(), mMaxDrawableWidth,
|
||||||
|
mMaxDrawableHeight);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Runnable setImageURIAsync(@Nullable Uri uri) {
|
public Runnable setImageURIAsync(@Nullable Uri uri) {
|
||||||
resetCache();
|
resetCache();
|
||||||
return super.setImageURIAsync(uri);
|
Drawable drawable = loadSizeRestrictedUri(uri);
|
||||||
|
if (drawable == null) {
|
||||||
|
return super.setImageURIAsync(uri);
|
||||||
|
} else {
|
||||||
|
return () -> setImageDrawable(drawable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -307,4 +402,18 @@ public class CachingIconView extends ImageView {
|
|||||||
public void setWillBeForceHidden(boolean forceHidden) {
|
public void setWillBeForceHidden(boolean forceHidden) {
|
||||||
mWillBeForceHidden = forceHidden;
|
mWillBeForceHidden = forceHidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set maximum width of drawable in pixels. -1 if not set.
|
||||||
|
*/
|
||||||
|
public int getMaxDrawableWidth() {
|
||||||
|
return mMaxDrawableWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the set maximum height of drawable in pixels. -1 if not set.
|
||||||
|
*/
|
||||||
|
public int getMaxDrawableHeight() {
|
||||||
|
return mMaxDrawableHeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,21 +16,25 @@
|
|||||||
|
|
||||||
package com.android.internal.widget;
|
package com.android.internal.widget;
|
||||||
|
|
||||||
|
import android.annotation.DrawableRes;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.ImageDecoder;
|
import android.graphics.ImageDecoder;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
|
/** A class to extract Drawables from a MessagingStyle/ConversationStyle message. */
|
||||||
public class LocalImageResolver {
|
public class LocalImageResolver {
|
||||||
private static final String TAG = LocalImageResolver.class.getSimpleName();
|
|
||||||
|
|
||||||
private static final int MAX_SAFE_ICON_SIZE_PX = 480;
|
@VisibleForTesting
|
||||||
|
static final int DEFAULT_MAX_SAFE_ICON_SIZE_PX = 480;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve an image from the given Uri using {@link ImageDecoder}
|
* Resolve an image from the given Uri using {@link ImageDecoder}
|
||||||
@@ -38,9 +42,9 @@ public class LocalImageResolver {
|
|||||||
public static Drawable resolveImage(Uri uri, Context context) throws IOException {
|
public static Drawable resolveImage(Uri uri, Context context) throws IOException {
|
||||||
final ImageDecoder.Source source =
|
final ImageDecoder.Source source =
|
||||||
ImageDecoder.createSource(context.getContentResolver(), uri);
|
ImageDecoder.createSource(context.getContentResolver(), uri);
|
||||||
final Drawable drawable =
|
return ImageDecoder.decodeDrawable(source,
|
||||||
ImageDecoder.decodeDrawable(source, LocalImageResolver::onHeaderDecoded);
|
(decoder, info, s) -> LocalImageResolver.onHeaderDecoded(decoder, info,
|
||||||
return drawable;
|
DEFAULT_MAX_SAFE_ICON_SIZE_PX, DEFAULT_MAX_SAFE_ICON_SIZE_PX));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,17 +52,49 @@ public class LocalImageResolver {
|
|||||||
* using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's,
|
* using {@link Icon#loadDrawable(Context)} otherwise. This will correctly apply the Icon's,
|
||||||
* tint, if present, to the drawable.
|
* tint, if present, to the drawable.
|
||||||
*/
|
*/
|
||||||
public static Drawable resolveImage(Icon icon, Context context) throws IOException {
|
public static Drawable resolveImage(@Nullable Icon icon, Context context)
|
||||||
Uri uri = getResolvableUri(icon);
|
throws IOException {
|
||||||
if (uri != null) {
|
return resolveImage(icon, context, DEFAULT_MAX_SAFE_ICON_SIZE_PX,
|
||||||
Drawable result = resolveImage(uri, context);
|
DEFAULT_MAX_SAFE_ICON_SIZE_PX);
|
||||||
if (icon.hasTint()) {
|
}
|
||||||
result.mutate();
|
|
||||||
result.setTintList(icon.getTintList());
|
/**
|
||||||
result.setTintBlendMode(icon.getTintBlendMode());
|
* 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,
|
||||||
return result;
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to straight drawable load if we fail with more efficient approach.
|
||||||
return icon.loadDrawable(context);
|
return icon.loadDrawable(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +102,71 @@ public class LocalImageResolver {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
final ImageDecoder.Source source =
|
final ImageDecoder.Source source =
|
||||||
ImageDecoder.createSource(context.getContentResolver(), uri);
|
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) -> {
|
return ImageDecoder.decodeDrawable(source, (decoder, info, unused) -> {
|
||||||
|
if (maxWidth <= 0 || maxHeight <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final Size size = info.getSize();
|
final Size size = info.getSize();
|
||||||
if (size.getWidth() > size.getHeight()) {
|
if (size.getWidth() > size.getHeight()) {
|
||||||
if (size.getWidth() > maxWidth) {
|
if (size.getWidth() > maxWidth) {
|
||||||
@@ -88,11 +188,12 @@ public class LocalImageResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
|
private static void onHeaderDecoded(ImageDecoder decoder, ImageDecoder.ImageInfo info,
|
||||||
ImageDecoder.Source source) {
|
int maxWidth, int maxHeight) {
|
||||||
final Size size = info.getSize();
|
final Size size = info.getSize();
|
||||||
final int originalSize = Math.max(size.getHeight(), size.getWidth());
|
final int originalSize = Math.max(size.getHeight(), size.getWidth());
|
||||||
final double ratio = (originalSize > MAX_SAFE_ICON_SIZE_PX)
|
final int maxSize = Math.max(maxWidth, maxHeight);
|
||||||
? originalSize * 1f / MAX_SAFE_ICON_SIZE_PX
|
final double ratio = (originalSize > maxSize)
|
||||||
|
? originalSize * 1f / maxSize
|
||||||
: 1.0;
|
: 1.0;
|
||||||
decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
|
decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,8 @@
|
|||||||
android:layout_marginStart="@dimen/notification_icon_circle_start"
|
android:layout_marginStart="@dimen/notification_icon_circle_start"
|
||||||
android:background="@drawable/notification_icon_circle"
|
android:background="@drawable/notification_icon_circle"
|
||||||
android:padding="@dimen/notification_icon_circle_padding"
|
android:padding="@dimen/notification_icon_circle_padding"
|
||||||
|
android:maxDrawableWidth="@dimen/notification_icon_circle_size"
|
||||||
|
android:maxDrawableHeight="@dimen/notification_icon_circle_size"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- extends ViewGroup -->
|
<!-- extends ViewGroup -->
|
||||||
|
|||||||
@@ -45,6 +45,8 @@
|
|||||||
android:layout_marginStart="@dimen/notification_icon_circle_start"
|
android:layout_marginStart="@dimen/notification_icon_circle_start"
|
||||||
android:background="@drawable/notification_icon_circle"
|
android:background="@drawable/notification_icon_circle"
|
||||||
android:padding="@dimen/notification_icon_circle_padding"
|
android:padding="@dimen/notification_icon_circle_padding"
|
||||||
|
android:maxDrawableWidth="@dimen/notification_icon_circle_size"
|
||||||
|
android:maxDrawableHeight="@dimen/notification_icon_circle_size"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
@@ -136,7 +138,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ImageView
|
<com.android.internal.widget.CachingIconView
|
||||||
android:id="@+id/right_icon"
|
android:id="@+id/right_icon"
|
||||||
android:layout_width="@dimen/notification_right_icon_size"
|
android:layout_width="@dimen/notification_right_icon_size"
|
||||||
android:layout_height="@dimen/notification_right_icon_size"
|
android:layout_height="@dimen/notification_right_icon_size"
|
||||||
@@ -148,6 +150,8 @@
|
|||||||
android:clipToOutline="true"
|
android:clipToOutline="true"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
|
android:maxDrawableWidth="@dimen/notification_right_icon_size"
|
||||||
|
android:maxDrawableHeight="@dimen/notification_right_icon_size"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
~ See the License for the specific language governing permissions and
|
~ See the License for the specific language governing permissions and
|
||||||
~ limitations under the License
|
~ limitations under the License
|
||||||
-->
|
-->
|
||||||
<ImageView
|
<com.android.internal.widget.CachingIconView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/right_icon"
|
android:id="@+id/right_icon"
|
||||||
android:layout_width="@dimen/notification_right_icon_size"
|
android:layout_width="@dimen/notification_right_icon_size"
|
||||||
@@ -25,4 +25,6 @@
|
|||||||
android:clipToOutline="true"
|
android:clipToOutline="true"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
|
android:maxDrawableWidth="@dimen/notification_right_icon_size"
|
||||||
|
android:maxDrawableHeight="@dimen/notification_right_icon_size"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -9807,4 +9807,12 @@
|
|||||||
of the supported locale. {@link android.app.LocaleConfig} -->
|
of the supported locale. {@link android.app.LocaleConfig} -->
|
||||||
<attr name="name" />
|
<attr name="name" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<!-- @hide -->
|
||||||
|
<declare-styleable name="CachingIconView">
|
||||||
|
<!-- Maximum width of displayed drawable. Drawables exceeding this size will be downsampled. -->
|
||||||
|
<attr name="maxDrawableWidth" format="dimension"/>
|
||||||
|
<!-- Maximum width of height drawable. Drawables exceeding this size will be downsampled. -->
|
||||||
|
<attr name="maxDrawableHeight" format="dimension"/>
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -148,6 +148,10 @@
|
|||||||
<public name="supportsInlineSuggestionsWithTouchExploration" />
|
<public name="supportsInlineSuggestionsWithTouchExploration" />
|
||||||
<public name="lineBreakStyle" />
|
<public name="lineBreakStyle" />
|
||||||
<public name="lineBreakWordStyle" />
|
<public name="lineBreakWordStyle" />
|
||||||
|
<!-- @hide -->
|
||||||
|
<public name="maxDrawableWidth" />
|
||||||
|
<!-- @hide -->
|
||||||
|
<public name="maxDrawableHeight" />
|
||||||
</staging-public-group>
|
</staging-public-group>
|
||||||
|
|
||||||
<staging-public-group type="id" first-id="0x01de0000">
|
<staging-public-group type="id" first-id="0x01de0000">
|
||||||
|
|||||||
BIN
core/tests/coretests/res/drawable/big_a.png
Normal file
BIN
core/tests/coretests/res/drawable/big_a.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<com.android.internal.widget.CachingIconView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/caching_icon_view"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:maxDrawableWidth="80dp"
|
||||||
|
android:maxDrawableHeight="80dp" />
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<com.android.internal.widget.CachingIconView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/caching_icon_view"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp" />
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
* 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.graphics.drawable.Icon;
|
||||||
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
|
import com.android.frameworks.coretests.R;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class CachingIconViewTest {
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageIcon_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageIcon(Icon.createWithResource(mContext, R.drawable.custom_drawable));
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageIconAsync_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.custom_drawable)).run();
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageResource_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageResource(R.drawable.custom_drawable);
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageResourceAsync_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageResourceAsync(R.drawable.custom_drawable).run();
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageUri_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageURI(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/"
|
||||||
|
+ R.drawable.custom_drawable));
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void customDrawable_setImageUriAsync_skipsResizeSuccessfully() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageURIAsync(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/"
|
||||||
|
+ R.drawable.custom_drawable)).run();
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(InsetDrawable.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageIcon_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageIcon_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageIcon(Icon.createWithResource(mContext, R.drawable.big_a));
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageIconAsync_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageIconAsync_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageIconAsync(Icon.createWithResource(mContext, R.drawable.big_a)).run();
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageResource_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageResource(R.drawable.big_a);
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageResource_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageResource(R.drawable.big_a);
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageResourceAsync_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageResourceAsync(R.drawable.big_a).run();
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageResourceAsync_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageResourceAsync(R.drawable.big_a).run();
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageUri_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageURI(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageUri_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageURI(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/" + R.drawable.big_a));
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableDimensionsSet_setImageUriAsync_resizesImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_max_size, null);
|
||||||
|
view.setImageURIAsync(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
|
||||||
|
|
||||||
|
assertDrawableResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void maxDrawableWithNoDimensionsSet_setImageUriAsync_doesNotResizeImageIcon() {
|
||||||
|
CachingIconView view = (CachingIconView) LayoutInflater.from(mContext).inflate(
|
||||||
|
R.layout.caching_icon_view_test_no_max_size, null);
|
||||||
|
view.setImageURIAsync(Uri.parse(
|
||||||
|
"android.resource://com.android.frameworks.coretests/" + R.drawable.big_a)).run();
|
||||||
|
|
||||||
|
assertDrawableNotResized(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void assertDrawableResized(@Nullable CachingIconView view) {
|
||||||
|
assertThat(view).isNotNull();
|
||||||
|
int maxSize =
|
||||||
|
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
|
||||||
|
mContext.getResources().getDisplayMetrics());
|
||||||
|
assertThat(view.getMaxDrawableHeight()).isEqualTo(maxSize);
|
||||||
|
assertThat(view.getMaxDrawableWidth()).isEqualTo(maxSize);
|
||||||
|
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(BitmapDrawable.class);
|
||||||
|
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||||
|
assertThat(bitmapDrawable.getBitmap().getWidth()).isLessThan(maxSize + 1);
|
||||||
|
assertThat(bitmapDrawable.getBitmap().getHeight()).isLessThan(maxSize + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertDrawableNotResized(@Nullable CachingIconView view) {
|
||||||
|
assertThat(view).isNotNull();
|
||||||
|
int maxSize =
|
||||||
|
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80f,
|
||||||
|
mContext.getResources().getDisplayMetrics());
|
||||||
|
assertThat(view.getMaxDrawableHeight()).isEqualTo(-1);
|
||||||
|
assertThat(view.getMaxDrawableWidth()).isEqualTo(-1);
|
||||||
|
|
||||||
|
Drawable drawable = view.getDrawable();
|
||||||
|
assertThat(drawable).isInstanceOf(BitmapDrawable.class);
|
||||||
|
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
||||||
|
assertThat(bitmapDrawable.getBitmap().getWidth()).isGreaterThan(maxSize);
|
||||||
|
assertThat(bitmapDrawable.getBitmap().getHeight()).isGreaterThan(maxSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user