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
This commit is contained in:
Felipe Leme
2017-09-15 18:16:21 -07:00
parent b51efdb511
commit bd88450183
4 changed files with 56 additions and 3 deletions

View File

@@ -50,6 +50,9 @@ import android.content.res.TypedArray;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; 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.hardware.fingerprint.FingerprintManager;
import android.icu.text.MeasureFormat; import android.icu.text.MeasureFormat;
import android.icu.text.RelativeDateTimeFormatter; import android.icu.text.RelativeDateTimeFormatter;
@@ -1366,4 +1369,50 @@ public final class Utils extends com.android.settingslib.Utils {
public static void setEditTextCursorPosition(EditText editText) { public static void setEditTextCursorPosition(EditText editText) {
editText.setSelection(editText.getText().length()); 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);
}
} }

View File

@@ -485,7 +485,7 @@ public class AccessibilitySettings extends SettingsPreferenceFragment implements
preference.setKey(componentName.flattenToString()); preference.setKey(componentName.flattenToString());
preference.setTitle(title); preference.setTitle(title);
preference.setIcon(icon); Utils.setSafeIcon(preference, icon);
final boolean serviceEnabled = accessibilityEnabled final boolean serviceEnabled = accessibilityEnabled
&& enabledServices.contains(componentName); && enabledServices.contains(componentName);
final String serviceState = serviceEnabled ? final String serviceState = serviceEnabled ?

View File

@@ -26,6 +26,7 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin; import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.GearPreference; import com.android.settings.widget.GearPreference;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
@@ -54,7 +55,7 @@ public abstract class DefaultAppPreferenceController extends AbstractPreferenceC
CharSequence defaultAppLabel = getDefaultAppLabel(); CharSequence defaultAppLabel = getDefaultAppLabel();
if (!TextUtils.isEmpty(defaultAppLabel)) { if (!TextUtils.isEmpty(defaultAppLabel)) {
preference.setSummary(defaultAppLabel); preference.setSummary(defaultAppLabel);
preference.setIcon(getDefaultAppIcon()); Utils.setSafeIcon(preference, getDefaultAppIcon());
} else { } else {
Log.d(TAG, "No default app"); Log.d(TAG, "No default app");
preference.setSummary(R.string.app_list_preference_none); preference.setSummary(R.string.app_list_preference_none);

View File

@@ -17,6 +17,9 @@
package com.android.settings.widget; package com.android.settings.widget;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle; import android.os.UserHandle;
@@ -154,7 +157,7 @@ public abstract class RadioButtonPickerFragment extends InstrumentedPreferenceFr
public RadioButtonPreference bindPreference(RadioButtonPreference pref, public RadioButtonPreference bindPreference(RadioButtonPreference pref,
String key, CandidateInfo info, String defaultKey) { String key, CandidateInfo info, String defaultKey) {
pref.setTitle(info.loadLabel()); pref.setTitle(info.loadLabel());
pref.setIcon(info.loadIcon()); Utils.setSafeIcon(pref, info.loadIcon());
pref.setKey(key); pref.setKey(key);
if (TextUtils.equals(defaultKey, key)) { if (TextUtils.equals(defaultKey, key)) {
pref.setChecked(true); pref.setChecked(true);