From bd88450183e68f5f543e4ecb8e189b5c0ec5693c Mon Sep 17 00:00:00 2001 From: Felipe Leme Date: Fri, 15 Sep 2017 18:16:21 -0700 Subject: [PATCH] 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 --- src/com/android/settings/Utils.java | 49 +++++++++++++++++++ .../accessibility/AccessibilitySettings.java | 2 +- .../DefaultAppPreferenceController.java | 3 +- .../widget/RadioButtonPickerFragment.java | 5 +- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java index 3b2ea1a4bae..8a832a922b7 100644 --- a/src/com/android/settings/Utils.java +++ b/src/com/android/settings/Utils.java @@ -50,6 +50,9 @@ import android.content.res.TypedArray; 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 final class Utils extends com.android.settingslib.Utils { 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); + } } diff --git a/src/com/android/settings/accessibility/AccessibilitySettings.java b/src/com/android/settings/accessibility/AccessibilitySettings.java index a335c83b04c..fd48c39f5bd 100644 --- a/src/com/android/settings/accessibility/AccessibilitySettings.java +++ b/src/com/android/settings/accessibility/AccessibilitySettings.java @@ -485,7 +485,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements preference.setKey(componentName.flattenToString()); preference.setTitle(title); - preference.setIcon(icon); + Utils.setSafeIcon(preference, icon); final boolean serviceEnabled = accessibilityEnabled && enabledServices.contains(componentName); final String serviceState = serviceEnabled ? diff --git a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java index d533d0f4993..91143b1d59d 100644 --- a/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java +++ b/src/com/android/settings/applications/defaultapps/DefaultAppPreferenceController.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.settings.R; +import com.android.settings.Utils; import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.widget.GearPreference; import com.android.settingslib.core.AbstractPreferenceController; @@ -54,7 +55,7 @@ public abstract class DefaultAppPreferenceController extends AbstractPreferenceC CharSequence defaultAppLabel = getDefaultAppLabel(); if (!TextUtils.isEmpty(defaultAppLabel)) { preference.setSummary(defaultAppLabel); - preference.setIcon(getDefaultAppIcon()); + Utils.setSafeIcon(preference, getDefaultAppIcon()); } else { Log.d(TAG, "No default app"); preference.setSummary(R.string.app_list_preference_none); diff --git a/src/com/android/settings/widget/RadioButtonPickerFragment.java b/src/com/android/settings/widget/RadioButtonPickerFragment.java index 68547934c5c..d73631965bc 100644 --- a/src/com/android/settings/widget/RadioButtonPickerFragment.java +++ b/src/com/android/settings/widget/RadioButtonPickerFragment.java @@ -17,6 +17,9 @@ package com.android.settings.widget; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; @@ -154,7 +157,7 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr public RadioButtonPreference bindPreference(RadioButtonPreference pref, String key, CandidateInfo info, String defaultKey) { pref.setTitle(info.loadLabel()); - pref.setIcon(info.loadIcon()); + Utils.setSafeIcon(pref, info.loadIcon()); pref.setKey(key); if (TextUtils.equals(defaultKey, key)) { pref.setChecked(true);