Adds a Util.setSafeIcon() function used to avoid crashes.

There are many places on Settings that loads an icon provided by an application,
and if the icon is too big it crashes Settings.

This CL creates a helper method used to set an icon in a safe way, and
uses it in a few places (but most likely not all of them).

Bug: 65739885
Test: manual verification with an app providing a 2MB png

Change-Id: Iae2becb6d0ec8893328d9ef1de618f9bd12fa4a0
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 3b2ea1a..8a832a9 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -50,6 +50,9 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.hardware.fingerprint.FingerprintManager;
 import android.icu.text.MeasureFormat;
 import android.icu.text.RelativeDateTimeFormatter;
@@ -1366,4 +1369,50 @@
     public static void setEditTextCursorPosition(EditText editText) {
         editText.setSelection(editText.getText().length());
     }
+
+    /**
+     * Sets the preference icon with a drawable that is scaled down to to avoid crashing Settings if
+     * it's too big.
+     */
+    public static void setSafeIcon(Preference pref, Drawable icon) {
+        Drawable safeIcon = icon;
+        if (icon != null) {
+            safeIcon = getSafeDrawable(icon, 500, 500);
+        }
+        pref.setIcon(safeIcon);
+    }
+
+    /**
+     * Gets a drawable with a limited size to avoid crashing Settings if it's too big.
+     *
+     * @param original original drawable, typically an app icon.
+     * @param maxWidth maximum width, in pixels.
+     * @param maxHeight maximum height, in pixels.
+     */
+    public static Drawable getSafeDrawable(Drawable original, int maxWidth, int maxHeight) {
+        final int actualWidth = original.getMinimumWidth();
+        final int actualHeight = original.getMinimumHeight();
+
+        if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
+            return original;
+        }
+
+        float scaleWidth = ((float) maxWidth) / actualWidth;
+        float scaleHeight = ((float) maxHeight) / actualHeight;
+        float scale = Math.min(scaleWidth, scaleHeight);
+        final int width = (int) (actualWidth * scale);
+        final int height = (int) (actualHeight * scale);
+
+        final Bitmap bitmap;
+        if (original instanceof BitmapDrawable) {
+            bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) original).getBitmap(), width,
+                    height, false);
+        } else {
+            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            final Canvas canvas = new Canvas(bitmap);
+            original.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
+            original.draw(canvas);
+        }
+        return new BitmapDrawable(null, bitmap);
+    }
 }