1 package com.android.systemui.util.drawable 2 3 import android.content.res.Resources 4 import android.graphics.Bitmap 5 import android.graphics.Canvas 6 import android.graphics.drawable.Animatable 7 import android.graphics.drawable.Animatable2 8 import android.graphics.drawable.AnimatedImageDrawable 9 import android.graphics.drawable.AnimatedRotateDrawable 10 import android.graphics.drawable.AnimatedStateListDrawable 11 import android.graphics.drawable.AnimatedVectorDrawable 12 import android.graphics.drawable.BitmapDrawable 13 import android.graphics.drawable.Drawable 14 import android.util.Log 15 import androidx.annotation.Px 16 import com.android.systemui.util.traceSection 17 18 class DrawableSize { 19 20 companion object { 21 22 const val TAG = "SysUiDrawableSize" 23 24 /** 25 * Downscales passed Drawable to set maximum width and height. This will only 26 * be done for Drawables that can be downscaled non-destructively - e.g. animated 27 * and stateful drawables will no be downscaled. 28 * 29 * Downscaling will keep the aspect ratio. 30 * This method will not touch drawables that already fit into size specification. 31 * 32 * @param resources Resources on which to base the density of resized drawable. 33 * @param drawable Drawable to downscale. 34 * @param maxWidth Maximum width of the downscaled drawable. 35 * @param maxHeight Maximum height of the downscaled drawable. 36 * 37 * @return returns downscaled drawable if it's possible to downscale it or original if it's 38 * not. 39 */ 40 @JvmStatic downscaleToSizenull41 fun downscaleToSize( 42 res: Resources, 43 drawable: Drawable, 44 @Px maxWidth: Int, 45 @Px maxHeight: Int 46 ): Drawable { 47 traceSection("DrawableSize#downscaleToSize") { 48 // Bitmap drawables can contain big bitmaps as their content while sneaking it past 49 // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap 50 // size for those. 51 val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width 52 ?: drawable.intrinsicWidth 53 val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height 54 ?: drawable.intrinsicHeight 55 56 // Don't touch drawable if we can't resolve sizes for whatever reason. 57 if (originalWidth <= 0 || originalHeight <= 0) { 58 return drawable 59 } 60 61 // Do not touch drawables that are already within bounds. 62 if (originalWidth < maxWidth && originalHeight < maxHeight) { 63 if (Log.isLoggable(TAG, Log.DEBUG)) { 64 Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " + 65 "to $maxWidth x $maxHeight") 66 } 67 68 return drawable 69 } 70 71 if (!isSimpleBitmap(drawable)) { 72 return drawable 73 } 74 75 val scaleWidth = maxWidth.toFloat() / originalWidth.toFloat() 76 val scaleHeight = maxHeight.toFloat() / originalHeight.toFloat() 77 val scale = minOf(scaleHeight, scaleWidth) 78 79 val width = (originalWidth * scale).toInt() 80 val height = (originalHeight * scale).toInt() 81 82 if (width <= 0 || height <= 0) { 83 Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " + 84 "from $originalWidth x $originalHeight to invalid $width x $height.") 85 return drawable 86 } 87 88 if (Log.isLoggable(TAG, Log.DEBUG)) { 89 Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " + 90 "from $originalWidth x $originalHeight to $width x $height") 91 } 92 93 // We want to keep existing config if it's more efficient than 32-bit RGB. 94 val config = (drawable as? BitmapDrawable)?.bitmap?.config 95 ?: Bitmap.Config.ARGB_8888 96 val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config) 97 val canvas = Canvas(scaledDrawableBitmap) 98 99 val originalBounds = drawable.bounds 100 drawable.setBounds(0, 0, width, height) 101 drawable.draw(canvas) 102 drawable.bounds = originalBounds 103 104 return BitmapDrawable(res, scaledDrawableBitmap) 105 } 106 } 107 isSimpleBitmapnull108 private fun isSimpleBitmap(drawable: Drawable): Boolean { 109 return !(drawable.isStateful || isAnimated(drawable)) 110 } 111 isAnimatednull112 private fun isAnimated(drawable: Drawable): Boolean { 113 if (drawable is Animatable || drawable is Animatable2) { 114 return true 115 } 116 117 return drawable is AnimatedImageDrawable || 118 drawable is AnimatedRotateDrawable || 119 drawable is AnimatedStateListDrawable || 120 drawable is AnimatedVectorDrawable 121 } 122 } 123 }