Merge "Use ImageResolver for BigPictureStyle when using BigPicture" into sc-dev

This commit is contained in:
Jeff DeCew
2021-04-09 14:24:34 +00:00
committed by Android (Google) Code Review
4 changed files with 154 additions and 3 deletions

View File

@@ -0,0 +1,105 @@
/*
* Copyright (C) 2021 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.annotation.AttrRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.ImageView;
import android.widget.RemoteViews;
import java.io.IOException;
/**
* An ImageView used by BigPicture Notifications to correctly resolve the Uri in an Icon using the
* LocalImageResolver, allowing it to support animated drawables which are not supported by
* Icon.loadDrawable().
*/
@RemoteViews.RemoteView
public class BigPictureNotificationImageView extends ImageView {
private static final String TAG = BigPictureNotificationImageView.class.getSimpleName();
public BigPictureNotificationImageView(@NonNull Context context) {
super(context);
}
public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
@android.view.RemotableViewMethod(asyncImpl = "setImageURIAsync")
public void setImageURI(@Nullable Uri uri) {
setImageDrawable(loadImage(uri));
}
/** @hide **/
public Runnable setImageURIAsync(@Nullable Uri uri) {
final Drawable drawable = loadImage(uri);
return () -> setImageDrawable(drawable);
}
@Override
@android.view.RemotableViewMethod(asyncImpl = "setImageIconAsync")
public void setImageIcon(@Nullable Icon icon) {
setImageDrawable(loadImage(icon));
}
/** @hide **/
public Runnable setImageIconAsync(@Nullable Icon icon) {
final Drawable drawable = loadImage(icon);
return () -> setImageDrawable(drawable);
}
private Drawable loadImage(Uri uri) {
if (uri == null) return null;
try {
return LocalImageResolver.resolveImage(uri, mContext);
} catch (IOException ex) {
Log.d(TAG, "Resolve failed from " + uri, ex);
return null;
}
}
private Drawable loadImage(Icon icon) {
if (icon == null) return null;
try {
return LocalImageResolver.resolveImage(icon, mContext);
} catch (IOException ex) {
Log.d(TAG, "Resolve failed from " + icon, ex);
return null;
}
}
}

View File

@@ -16,10 +16,11 @@
package com.android.internal.widget;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.ImageDecoder;
import android.graphics.drawable.AnimatedImageDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.net.Uri;
import android.util.Size;
@@ -31,6 +32,9 @@ public class LocalImageResolver {
private static final int MAX_SAFE_ICON_SIZE_PX = 480;
/**
* Resolve an image from the given Uri using {@link ImageDecoder}
*/
public static Drawable resolveImage(Uri uri, Context context) throws IOException {
final ImageDecoder.Source source =
ImageDecoder.createSource(context.getContentResolver(), uri);
@@ -39,6 +43,25 @@ public class LocalImageResolver {
return drawable;
}
/**
* 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(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;
}
return icon.loadDrawable(context);
}
public static Drawable resolveImage(Uri uri, Context context, int maxWidth, int maxHeight)
throws IOException {
final ImageDecoder.Source source =
@@ -73,4 +96,16 @@ public class LocalImageResolver {
: 1.0;
decoder.setTargetSampleSize(getPowerOfTwoForSampleRatio(ratio));
}
/**
* Gets the Uri for this icon, assuming the icon can be treated as a pure Uri. Null otherwise.
*/
@Nullable
public static Uri getResolvableUri(@Nullable Icon icon) {
if (icon == null || (icon.getType() != Icon.TYPE_URI
&& icon.getType() != Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
return null;
}
return icon.getUri();
}
}

View File

@@ -58,7 +58,7 @@
<include layout="@layout/notification_template_text_multiline" />
</LinearLayout>
<ImageView
<com.android.internal.widget.BigPictureNotificationImageView
android:id="@+id/big_picture"
android:layout_width="match_parent"
android:layout_height="0dp"

View File

@@ -23,6 +23,7 @@ import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ContentResolver;
import android.content.Context;
@@ -336,7 +337,7 @@ public final class Icon implements Parcelable {
*/
public Drawable loadDrawable(Context context) {
final Drawable result = loadDrawableInner(context);
if (result != null && (mTintList != null || mBlendMode != DEFAULT_BLEND_MODE)) {
if (result != null && hasTint()) {
result.mutate();
result.setTintList(mTintList);
result.setTintBlendMode(mBlendMode);
@@ -762,6 +763,11 @@ public final class Icon implements Parcelable {
return this;
}
/** @hide */
public @Nullable ColorStateList getTintList() {
return mTintList;
}
/**
* Store a blending mode to use whenever this Icon is drawn.
*
@@ -784,6 +790,11 @@ public final class Icon implements Parcelable {
return this;
}
/** @hide */
public @NonNull BlendMode getTintBlendMode() {
return mBlendMode;
}
/** @hide */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public boolean hasTint() {