Loading core/java/com/android/internal/widget/BigPictureNotificationImageView.java 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * 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; } } } core/java/com/android/internal/widget/LocalImageResolver.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 = Loading Loading @@ -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(); } } core/res/res/layout/notification_template_material_big_picture.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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" Loading graphics/java/android/graphics/drawable/Icon.java +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. * Loading @@ -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() { Loading Loading
core/java/com/android/internal/widget/BigPictureNotificationImageView.java 0 → 100644 +105 −0 Original line number Diff line number Diff line /* * 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; } } }
core/java/com/android/internal/widget/LocalImageResolver.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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 = Loading Loading @@ -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(); } }
core/res/res/layout/notification_template_material_big_picture.xml +1 −1 Original line number Diff line number Diff line Loading @@ -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" Loading
graphics/java/android/graphics/drawable/Icon.java +12 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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. * Loading @@ -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() { Loading