1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.widget; 18 19 import android.annotation.AttrRes; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.StyleRes; 23 import android.app.ActivityManager; 24 import android.content.Context; 25 import android.graphics.drawable.BitmapDrawable; 26 import android.graphics.drawable.Drawable; 27 import android.graphics.drawable.Icon; 28 import android.net.Uri; 29 import android.util.AttributeSet; 30 import android.util.Log; 31 import android.widget.ImageView; 32 import android.widget.RemoteViews; 33 import android.widget.flags.Flags; 34 35 import com.android.internal.R; 36 37 /** 38 * An ImageView used by BigPicture Notifications to correctly resolve the Uri in an Icon using the 39 * LocalImageResolver, allowing it to support animated drawables which are not supported by 40 * Icon.loadDrawable(). 41 */ 42 @RemoteViews.RemoteView 43 public class BigPictureNotificationImageView extends ImageView implements 44 NotificationDrawableConsumer { 45 46 private static final String TAG = BigPictureNotificationImageView.class.getSimpleName(); 47 48 private final int mMaximumDrawableWidth; 49 private final int mMaximumDrawableHeight; 50 51 private NotificationIconManager mIconManager; 52 BigPictureNotificationImageView(@onNull Context context)53 public BigPictureNotificationImageView(@NonNull Context context) { 54 this(context, null, 0, 0); 55 } 56 BigPictureNotificationImageView(@onNull Context context, @Nullable AttributeSet attrs)57 public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs) { 58 this(context, attrs, 0, 0); 59 } 60 BigPictureNotificationImageView(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr)61 public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs, 62 @AttrRes int defStyleAttr) { 63 this(context, attrs, defStyleAttr, 0); 64 } 65 BigPictureNotificationImageView(@onNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)66 public BigPictureNotificationImageView(@NonNull Context context, @Nullable AttributeSet attrs, 67 @AttrRes int defStyleAttr, @StyleRes int defStyleRes) { 68 super(context, attrs, defStyleAttr, defStyleRes); 69 boolean isLowRam = ActivityManager.isLowRamDeviceStatic(); 70 mMaximumDrawableWidth = context.getResources().getDimensionPixelSize( 71 isLowRam ? R.dimen.notification_big_picture_max_width_low_ram 72 : R.dimen.notification_big_picture_max_width); 73 mMaximumDrawableHeight = context.getResources().getDimensionPixelSize( 74 isLowRam ? R.dimen.notification_big_picture_max_height_low_ram 75 : R.dimen.notification_big_picture_max_height); 76 } 77 78 79 /** 80 * Sets an {@link NotificationIconManager} on this ImageView, which handles the loading of 81 * icons, instead of using the {@link LocalImageResolver} directly. 82 * If set, it overrides the behaviour of {@link #setImageIconAsync} and {@link #setImageIcon}, 83 * and it expects that the content of this imageView is only updated calling these two methods. 84 * 85 * @param iconManager to be called, when the icon is updated 86 */ setIconManager(NotificationIconManager iconManager)87 public void setIconManager(NotificationIconManager iconManager) { 88 mIconManager = iconManager; 89 } 90 91 @Override 92 @android.view.RemotableViewMethod(asyncImpl = "setImageURIAsync") setImageURI(@ullable Uri uri)93 public void setImageURI(@Nullable Uri uri) { 94 setImageDrawable(loadImage(uri)); 95 } 96 97 /** @hide **/ setImageURIAsync(@ullable Uri uri)98 public Runnable setImageURIAsync(@Nullable Uri uri) { 99 final Drawable drawable = loadImage(uri); 100 return () -> setImageDrawable(drawable); 101 } 102 103 @Override 104 @android.view.RemotableViewMethod(asyncImpl = "setImageIconAsync") setImageIcon(@ullable Icon icon)105 public void setImageIcon(@Nullable Icon icon) { 106 if (mIconManager != null) { 107 mIconManager.updateIcon(this, icon).run(); 108 return; 109 } 110 // old code path 111 setImageDrawable(loadImage(icon)); 112 } 113 114 /** @hide **/ setImageIconAsync(@ullable Icon icon)115 public Runnable setImageIconAsync(@Nullable Icon icon) { 116 if (mIconManager != null) { 117 return mIconManager.updateIcon(this, icon); 118 } 119 // old code path 120 final Drawable drawable = loadImage(icon); 121 return () -> setImageDrawable(drawable); 122 } 123 124 @Override setImageDrawable(@ullable Drawable drawable)125 public void setImageDrawable(@Nullable Drawable drawable) { 126 if (drawable instanceof BitmapDrawable bitmapDrawable) { 127 if (bitmapDrawable.getBitmap() == null) { 128 if (Flags.bigPictureStyleDiscardEmptyIconBitmapDrawables()) { 129 Log.e(TAG, "discarding BitmapDrawable with null Bitmap (invalid image file?)"); 130 drawable = null; 131 } else { 132 Log.e(TAG, "setting BitmapDrawable with null Bitmap (invalid image file?)"); 133 } 134 } 135 } 136 137 super.setImageDrawable(drawable); 138 } 139 loadImage(Uri uri)140 private Drawable loadImage(Uri uri) { 141 if (uri == null) return null; 142 return LocalImageResolver.resolveImage(uri, mContext, mMaximumDrawableWidth, 143 mMaximumDrawableHeight); 144 } 145 loadImage(Icon icon)146 private Drawable loadImage(Icon icon) { 147 if (icon == null) return null; 148 149 Drawable drawable = LocalImageResolver.resolveImage(icon, mContext, mMaximumDrawableWidth, 150 mMaximumDrawableHeight); 151 if (drawable != null) { 152 return drawable; 153 } 154 155 drawable = icon.loadDrawable(mContext); 156 if (drawable != null) { 157 return drawable; 158 } 159 160 Log.e(TAG, "Couldn't load drawable for icon: " + icon); 161 return null; 162 } 163 } 164