1 /* 2 * Copyright (C) 2024 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.settings.applications.credentials; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.ColorFilter; 22 import android.graphics.Paint; 23 import android.graphics.PaintFlagsDrawFilter; 24 import android.graphics.PixelFormat; 25 import android.graphics.Rect; 26 import android.graphics.drawable.BitmapDrawable; 27 import android.graphics.drawable.Drawable; 28 import android.graphics.drawable.PaintDrawable; 29 import android.util.DisplayMetrics; 30 31 import androidx.annotation.NonNull; 32 33 /** Handles resizing of images for CredMan settings. */ 34 public class ImageUtils { 35 36 /** 37 * Utility class to resize icons to match default icon size. Code is mostly borrowed from 38 * Launcher and ActivityPicker. 39 */ 40 public static class IconResizer { 41 private final int mIconWidth; 42 private final int mIconHeight; 43 44 private final DisplayMetrics mMetrics; 45 private final Rect mOldBounds = new Rect(); 46 private final Canvas mCanvas = new Canvas(); 47 IconResizer(int width, int height, DisplayMetrics metrics)48 public IconResizer(int width, int height, DisplayMetrics metrics) { 49 mCanvas.setDrawFilter( 50 new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); 51 52 mMetrics = metrics; 53 mIconWidth = width; 54 mIconHeight = height; 55 } 56 57 /** 58 * Returns a Drawable representing the thumbnail of the specified Drawable. The size of the 59 * thumbnail is defined by the dimension android.R.dimen.app_icon_size. 60 * 61 * <p>This method is not thread-safe and should be invoked on the UI thread only. 62 * 63 * @param icon The icon to get a thumbnail of. 64 * @return A thumbnail for the specified icon or the icon itself if the thumbnail could not 65 * be created. 66 */ createIconThumbnail(Drawable icon)67 public Drawable createIconThumbnail(Drawable icon) { 68 int width = mIconWidth; 69 int height = mIconHeight; 70 71 if (icon == null) { 72 return new EmptyDrawable(width, height); 73 } 74 75 try { 76 if (icon instanceof PaintDrawable) { 77 PaintDrawable painter = (PaintDrawable) icon; 78 painter.setIntrinsicWidth(width); 79 painter.setIntrinsicHeight(height); 80 } else if (icon instanceof BitmapDrawable) { 81 // Ensure the bitmap has a density. 82 BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; 83 Bitmap bitmap = bitmapDrawable.getBitmap(); 84 if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { 85 bitmapDrawable.setTargetDensity(mMetrics); 86 } 87 } 88 int iconWidth = icon.getIntrinsicWidth(); 89 int iconHeight = icon.getIntrinsicHeight(); 90 91 if (iconWidth > 0 && iconHeight > 0) { 92 if (width < iconWidth || height < iconHeight) { 93 final float ratio = (float) iconWidth / iconHeight; 94 95 if (iconWidth > iconHeight) { 96 height = (int) (width / ratio); 97 } else if (iconHeight > iconWidth) { 98 width = (int) (height * ratio); 99 } 100 101 final Bitmap.Config c = 102 icon.getOpacity() != PixelFormat.OPAQUE 103 ? Bitmap.Config.ARGB_8888 104 : Bitmap.Config.RGB_565; 105 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); 106 final Canvas canvas = mCanvas; 107 canvas.setBitmap(thumb); 108 109 // Copy the old bounds to restore them later 110 // If we were to do oldBounds = icon.getBounds(), 111 // the call to setBounds() that follows would 112 // change the same instance and we would lose the 113 // old bounds. 114 mOldBounds.set(icon.getBounds()); 115 final int x = (mIconWidth - width) / 2; 116 final int y = (mIconHeight - height) / 2; 117 icon.setBounds(x, y, x + width, y + height); 118 icon.draw(canvas); 119 icon.setBounds(mOldBounds); 120 121 // Create the new resized drawable. 122 icon = createBitmapDrawable(thumb); 123 } else if (iconWidth < width && iconHeight < height) { 124 final Bitmap.Config c = Bitmap.Config.ARGB_8888; 125 final Bitmap thumb = Bitmap.createBitmap(mIconWidth, mIconHeight, c); 126 final Canvas canvas = mCanvas; 127 canvas.setBitmap(thumb); 128 mOldBounds.set(icon.getBounds()); 129 130 // Set the bounds for the new icon. 131 final int x = (width - iconWidth) / 2; 132 final int y = (height - iconHeight) / 2; 133 icon.setBounds(x, y, x + iconWidth, y + iconHeight); 134 icon.draw(canvas); 135 icon.setBounds(mOldBounds); 136 137 // Create the new resized drawable. 138 icon = createBitmapDrawable(thumb); 139 } 140 } 141 142 } catch (Throwable t) { 143 icon = new EmptyDrawable(width, height); 144 } 145 146 return icon; 147 } 148 createBitmapDrawable(Bitmap thumb)149 private BitmapDrawable createBitmapDrawable(Bitmap thumb) { 150 BitmapDrawable icon = new BitmapDrawable(thumb); 151 icon.setTargetDensity(mMetrics); 152 mCanvas.setBitmap(null); 153 return icon; 154 } 155 } 156 157 public static class EmptyDrawable extends Drawable { 158 private final int mWidth; 159 private final int mHeight; 160 EmptyDrawable(int width, int height)161 EmptyDrawable(int width, int height) { 162 mWidth = width; 163 mHeight = height; 164 } 165 166 @Override getIntrinsicWidth()167 public int getIntrinsicWidth() { 168 return mWidth; 169 } 170 171 @Override getIntrinsicHeight()172 public int getIntrinsicHeight() { 173 return mHeight; 174 } 175 176 @Override getMinimumWidth()177 public int getMinimumWidth() { 178 return mWidth; 179 } 180 181 @Override getMinimumHeight()182 public int getMinimumHeight() { 183 return mHeight; 184 } 185 186 @Override draw(@onNull Canvas canvas)187 public void draw(@NonNull Canvas canvas) {} 188 189 @Override setAlpha(int alpha)190 public void setAlpha(int alpha) {} 191 192 @Override setColorFilter(@onNull ColorFilter cf)193 public void setColorFilter(@NonNull ColorFilter cf) {} 194 195 @Override getOpacity()196 public int getOpacity() { 197 return PixelFormat.TRANSLUCENT; 198 } 199 } 200 } 201