Loading Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ aidl_files := \ frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \ frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl \ frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ frameworks/base/core/java/android/app/ComposedIconInfo.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ frameworks/base/core/java/android/app/NotificationGroup.aidl \ frameworks/base/core/java/android/app/Profile.aidl \ Loading core/java/android/app/ComposedIconInfo.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; /** @hide */ parcelable ComposedIconInfo; core/java/android/app/ComposedIconInfo.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Parcel; import android.os.Parcelable; /** @hide */ public class ComposedIconInfo implements Parcelable { public BitmapDrawable iconUpon, iconMask; public BitmapDrawable[] iconBacks; public float iconScale; public int iconDensity; public int iconSize; public ComposedIconInfo() { super(); } private ComposedIconInfo(Parcel source) { ClassLoader bmpClassLoader = Bitmap.class.getClassLoader(); iconScale = source.readFloat(); iconDensity = source.readInt(); iconSize = source.readInt(); int backCount = source.readInt(); if (backCount > 0) { iconBacks = new BitmapDrawable[backCount]; for (int i = 0; i < backCount; i++) { iconBacks[i] = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); } } iconMask = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); iconUpon = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloat(iconScale); dest.writeInt(iconDensity); dest.writeInt(iconSize); dest.writeInt(iconBacks != null ? iconBacks.length : 0); if (iconBacks != null) { for (BitmapDrawable d : iconBacks) { dest.writeParcelable(d != null ? d.getBitmap() : null, flags); } } dest.writeParcelable(iconMask != null ? iconMask.getBitmap() : null, flags); dest.writeParcelable(iconUpon != null ? iconUpon.getBitmap() : null, flags); } public static final Creator<ComposedIconInfo> CREATOR = new Creator<ComposedIconInfo>() { @Override public ComposedIconInfo createFromParcel(Parcel source) { return new ComposedIconInfo(source); } @Override public ComposedIconInfo[] newArray(int size) { return new ComposedIconInfo[0]; } }; } core/java/android/app/IconPackHelper.java +284 −20 Original line number Diff line number Diff line Loading @@ -15,12 +15,28 @@ */ package android.app; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import android.content.pm.PackageInfo; import android.content.res.IThemeService; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.PaintDrawable; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.TypedValue; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; Loading @@ -40,24 +56,47 @@ import android.util.DisplayMetrics; /** @hide */ public class IconPackHelper { private static final String TAG = IconPackHelper.class.getSimpleName(); private static final String ICON_MASK_TAG = "iconmask"; private static final String ICON_BACK_TAG = "iconback"; private static final String ICON_UPON_TAG = "iconupon"; private static final String ICON_SCALE_TAG = "scale"; private static final String ICON_BACK_FORMAT = "iconback%d"; private static final ComponentName ICON_BACK_COMPONENT; private static final ComponentName ICON_MASK_COMPONENT; private static final ComponentName ICON_UPON_COMPONENT; private static final ComponentName ICON_SCALE_COMPONENT; private static final float DEFAULT_SCALE = 1.0f; private static final int COMPOSED_ICON_COOKIE = 128; private final Context mContext; private Map<ComponentName, String> mIconPackResourceMap; private String mLoadedIconPackName; private Resources mLoadedIconPackResource; private float mIconScale; private ComposedIconInfo mComposedIconInfo; private int mIconBackCount = 0; static { ICON_BACK_COMPONENT = new ComponentName(ICON_BACK_TAG, ""); ICON_MASK_COMPONENT = new ComponentName(ICON_MASK_TAG, ""); ICON_UPON_COMPONENT = new ComponentName(ICON_UPON_TAG, ""); ICON_SCALE_COMPONENT = new ComponentName(ICON_SCALE_TAG, ""); } public IconPackHelper(Context context) { mContext = context; mIconPackResourceMap = new HashMap<ComponentName, String>(); ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); mComposedIconInfo = new ComposedIconInfo(); mComposedIconInfo.iconSize = am.getLauncherLargeIconSize(); mComposedIconInfo.iconDensity = am.getLauncherLargeIconDensity(); } private static void loadResourcesFromXmlParser(XmlPullParser parser, private void loadResourcesFromXmlParser(XmlPullParser parser, Map<ComponentName, String> iconPackResources) throws XmlPullParserException, IOException { mIconBackCount = 0; int eventType = parser.getEventType(); do { Loading @@ -65,6 +104,21 @@ public class IconPackHelper { continue; } if (parseComposedIconComponent(parser, iconPackResources)) { continue; } if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { String factor = parser.getAttributeValue(null, "factor"); if (factor == null) { if (parser.getAttributeCount() == 1) { factor = parser.getAttributeValue(0); } } iconPackResources.put(ICON_SCALE_COMPONENT, factor); continue; } if (!parser.getName().equalsIgnoreCase("item")) { continue; } Loading Loading @@ -100,28 +154,98 @@ public class IconPackHelper { } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); } private boolean isComposedIconComponent(String tag) { return tag.equalsIgnoreCase(ICON_MASK_TAG) || tag.equalsIgnoreCase(ICON_BACK_TAG) || tag.equalsIgnoreCase(ICON_UPON_TAG); } private boolean parseComposedIconComponent(XmlPullParser parser, Map<ComponentName, String> iconPackResources) { String icon; String tag = parser.getName(); if (!isComposedIconComponent(tag)) { return false; } if (parser.getAttributeCount() >= 1) { if (tag.equalsIgnoreCase(ICON_BACK_TAG)) { mIconBackCount = parser.getAttributeCount(); for (int i = 0; i < mIconBackCount; i++) { tag = String.format(ICON_BACK_FORMAT, i); icon = parser.getAttributeValue(i); iconPackResources.put(new ComponentName(tag, ""), icon); } } else { icon = parser.getAttributeValue(0); iconPackResources.put(new ComponentName(tag, ""), icon); } return true; } return false; } public void loadIconPack(String packageName) throws NameNotFoundException { if (packageName == null) { mLoadedIconPackResource = null; mLoadedIconPackName = null; mComposedIconInfo.iconBacks = null; mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = null; mComposedIconInfo.iconScale = 0; } else { mIconBackCount = 0; Resources res = createIconResource(mContext, packageName); mIconPackResourceMap = getIconResMapFromXml(res, packageName); mLoadedIconPackResource = res; mLoadedIconPackName = packageName; String scale = mIconPackResourceMap.get(ICON_SCALE_TAG); loadComposedIconComponents(); } } public ComposedIconInfo getComposedIconInfo() { return mComposedIconInfo; } private void loadComposedIconComponents() { mComposedIconInfo.iconMask = (BitmapDrawable) getDrawableForName(ICON_MASK_COMPONENT); mComposedIconInfo.iconUpon = (BitmapDrawable) getDrawableForName(ICON_UPON_COMPONENT); // Take care of loading iconback which can have multiple images if (mIconBackCount > 0) { mComposedIconInfo.iconBacks = new BitmapDrawable[mIconBackCount]; for (int i = 0; i < mIconBackCount; i++) { mComposedIconInfo.iconBacks[i] = (BitmapDrawable) getDrawableForName( new ComponentName(String.format(ICON_BACK_FORMAT, i), "")); } } // Get the icon scale from this pack String scale = mIconPackResourceMap.get(ICON_SCALE_COMPONENT); if (scale != null) { try { mIconScale = Float.valueOf(scale); mComposedIconInfo.iconScale = Float.valueOf(scale); } catch (NumberFormatException e) { mComposedIconInfo.iconScale = DEFAULT_SCALE; } } else { mComposedIconInfo.iconScale = DEFAULT_SCALE; } } private Drawable getDrawableForName(ComponentName component) { if (isIconPackLoaded()) { String item = mIconPackResourceMap.get(component); if (!TextUtils.isEmpty(item)) { int id = getResourceIdForDrawable(item); if (id != 0) { return mLoadedIconPackResource.getDrawable(id); } } public float getIconScale() { return mIconScale; } return null; } public static Resources createIconResource(Context context, String packageName) throws NameNotFoundException { Loading Loading @@ -151,7 +275,7 @@ public class IconPackHelper { return res; } public static Map<ComponentName, String> getIconResMapFromXml(Resources res, String packageName) { public Map<ComponentName, String> getIconResMapFromXml(Resources res, String packageName) { XmlPullParser parser = null; InputStream inputStream = null; Map<ComponentName, String> iconPackResources = new HashMap<ComponentName, String>(); Loading Loading @@ -254,16 +378,20 @@ public class IconPackHelper { if (!isIconPackLoaded()) { return 0; } ComponentName compName = new ComponentName(info.packageName.toLowerCase(), info.name.toLowerCase()); ComponentName compName = new ComponentName(info.packageName.toLowerCase(), info.name.toLowerCase()); String drawable = mIconPackResourceMap.get(compName); if (drawable == null) { if (drawable != null) { int resId = getResourceIdForDrawable(drawable); if (resId != 0) return resId; } // Icon pack doesn't have an icon for the activity, fallback to package icon compName = new ComponentName(info.packageName.toLowerCase(), ""); drawable = mIconPackResourceMap.get(compName); if (drawable == null) { return 0; } } return getResourceIdForDrawable(drawable); } Loading @@ -277,12 +405,148 @@ public class IconPackHelper { public Drawable getDrawableForActivity(ActivityInfo info) { int id = getResourceIdForActivityIcon(info); if (id == 0) return null; return mLoadedIconPackResource.getDrawable(id); return mLoadedIconPackResource.getDrawable(id, false); } public Drawable getDrawableForActivityWithDensity(ActivityInfo info, int density) { int id = getResourceIdForActivityIcon(info); if (id == 0) return null; return mLoadedIconPackResource.getDrawableForDensity(id, density); return mLoadedIconPackResource.getDrawableForDensity(id, density, false); } public static class IconCustomizer { private static final Random sRandom = new Random(); private static final IThemeService sThemeService; static { sThemeService = IThemeService.Stub.asInterface( ServiceManager.getService(Context.THEME_SERVICE)); } public static Drawable getComposedIconDrawable(Drawable icon, Context context, ComposedIconInfo iconInfo) { final Resources res = context.getResources(); return getComposedIconDrawable(icon, res, iconInfo); } public static Drawable getComposedIconDrawable(Drawable icon, Resources res, ComposedIconInfo iconInfo) { if (iconInfo == null) return icon; Drawable back = null; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[sRandom.nextInt(iconInfo.iconBacks.length)]; } Bitmap bmp = createIconBitmap(icon, res, back, iconInfo.iconMask, iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize); return bmp != null ? new BitmapDrawable(res, bmp): null; } public static void getValue(Resources res, int resId, TypedValue outValue, Drawable baseIcon) { if (!(baseIcon instanceof BitmapDrawable)) return; final String pkgName = res.getAssets().getAppName(); TypedValue tempValue = new TypedValue(); tempValue.setTo(outValue); outValue.assetCookie = COMPOSED_ICON_COOKIE; outValue.data = resId & (COMPOSED_ICON_COOKIE << 24 | 0x00ffffff); outValue.string = getCachedIconPath(pkgName, resId, outValue.density); if (!(new File(outValue.string.toString()).exists())) { // compose the icon and cache it final ComposedIconInfo iconInfo = res.getComposedIconInfo(); Drawable back = null; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[(outValue.string.hashCode() & 0x7fffffff) % iconInfo.iconBacks.length]; } Bitmap bmp = createIconBitmap(baseIcon, res, back, iconInfo.iconMask, iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize); if (!cacheComposedIcon(bmp, getCachedIconName(pkgName, resId, outValue.density))) { Log.w(TAG, "Unable to cache icon " + outValue.string); // restore the original TypedValue outValue.setTo(tempValue); } } } private static Bitmap createIconBitmap(Drawable icon, Resources res, Drawable iconBack, Drawable iconMask, Drawable iconUpon, float scale, int iconSize) { if (iconSize <= 0) return null; final Canvas canvas = new Canvas(); canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG)); if (icon instanceof PaintDrawable) { PaintDrawable painter = (PaintDrawable) icon; painter.setIntrinsicWidth(iconSize); painter.setIntrinsicHeight(iconSize); } else if (icon instanceof BitmapDrawable) { // Ensure the bitmap has a density. BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; Bitmap bitmap = bitmapDrawable.getBitmap(); if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { bitmapDrawable.setTargetDensity(res.getDisplayMetrics()); } canvas.setDensity(bitmap.getDensity()); } Bitmap bitmap = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); // Scale the original Rect oldBounds = new Rect(); oldBounds.set(icon.getBounds()); icon.setBounds(0, 0, iconSize, iconSize); canvas.save(); canvas.scale(scale, scale, iconSize / 2, iconSize / 2); icon.draw(canvas); canvas.restore(); // Mask off the original if iconMask is not null if (iconMask != null) { iconMask.setBounds(icon.getBounds()); ((BitmapDrawable) iconMask).getPaint().setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); iconMask.draw(canvas); } // Draw the iconBacks if not null and then the original (scaled and masked) icon on top if (iconBack != null) { iconBack.setBounds(icon.getBounds()); ((BitmapDrawable) iconBack).getPaint().setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); iconBack.draw(canvas); } // Finally draw the foreground if one was supplied if (iconUpon != null) { iconUpon.draw(canvas); } icon.setBounds(oldBounds); bitmap.setDensity(canvas.getDensity()); return bitmap; } private static boolean cacheComposedIcon(Bitmap bmp, String path) { try { return sThemeService.cacheComposedIcon(bmp, path); } catch (RemoteException e) { Log.e(TAG, "Unable to cache icon.", e); } return false; } private static String getCachedIconPath(String pkgName, int resId, int density) { return String.format("%s/%s", ThemeUtils.SYSTEM_THEME_ICON_CACHE_DIR, getCachedIconName(pkgName, resId, density)); } private static String getCachedIconName(String pkgName, int resId, int density) { return String.format("%s_%08x_%d.png", pkgName, resId, density); } } } core/java/android/app/ResourcesManager.java +15 −3 Original line number Diff line number Diff line Loading @@ -284,20 +284,31 @@ public class ResourcesManager { //Map application icon if (pkgInfo != null && pkgInfo.applicationInfo != null) { appInfo = pkgInfo.applicationInfo; if (appInfo.themedIcon != 0) iconResources.put(appInfo.icon, appInfo); if (appInfo.themedIcon != 0 || iconResources.get(appInfo.icon) == null) { iconResources.put(appInfo.icon, appInfo); } } //Map activity icons. if (pkgInfo != null && pkgInfo.activities != null) { for (ActivityInfo ai : pkgInfo.activities) { if (ai.themedIcon != 0 && ai.icon != 0) { if (ai.icon != 0 && (ai.themedIcon != 0 || iconResources.get(ai.icon) == null)) { iconResources.put(ai.icon, ai); } else if (ai.themedIcon != 0 && appInfo != null && appInfo.icon != 0) { } else if (appInfo != null && appInfo.icon != 0 && (ai.themedIcon != 0 || iconResources.get(appInfo.icon) == null)) { iconResources.put(appInfo.icon, ai); } } } r.setIconResources(iconResources); final IPackageManager pm = getPackageManager(); try { ComposedIconInfo iconInfo = pm.getComposedIconInfo(); r.setComposedIconInfo(iconInfo); } catch (RemoteException e) { e.printStackTrace(); } } public final int applyConfigurationToResourcesLocked(Configuration config, Loading Loading @@ -349,6 +360,7 @@ public class ResourcesManager { AssetManager am = r.getAssets(); if (am.hasThemeSupport()) { r.setIconResources(null); r.setComposedIconInfo(null); detachThemeAssets(am); if (config.customTheme != null) { attachThemeAssets(am, config.customTheme); Loading Loading
Android.mk +1 −0 Original line number Diff line number Diff line Loading @@ -372,6 +372,7 @@ aidl_files := \ frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \ frameworks/base/core/java/android/accounts/IAccountAuthenticator.aidl \ frameworks/base/core/java/android/accounts/IAccountAuthenticatorResponse.aidl \ frameworks/base/core/java/android/app/ComposedIconInfo.aidl \ frameworks/base/core/java/android/app/Notification.aidl \ frameworks/base/core/java/android/app/NotificationGroup.aidl \ frameworks/base/core/java/android/app/Profile.aidl \ Loading
core/java/android/app/ComposedIconInfo.aidl 0 → 100644 +19 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; /** @hide */ parcelable ComposedIconInfo;
core/java/android/app/ComposedIconInfo.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Parcel; import android.os.Parcelable; /** @hide */ public class ComposedIconInfo implements Parcelable { public BitmapDrawable iconUpon, iconMask; public BitmapDrawable[] iconBacks; public float iconScale; public int iconDensity; public int iconSize; public ComposedIconInfo() { super(); } private ComposedIconInfo(Parcel source) { ClassLoader bmpClassLoader = Bitmap.class.getClassLoader(); iconScale = source.readFloat(); iconDensity = source.readInt(); iconSize = source.readInt(); int backCount = source.readInt(); if (backCount > 0) { iconBacks = new BitmapDrawable[backCount]; for (int i = 0; i < backCount; i++) { iconBacks[i] = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); } } iconMask = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); iconUpon = new BitmapDrawable((Bitmap) source.readParcelable(bmpClassLoader)); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeFloat(iconScale); dest.writeInt(iconDensity); dest.writeInt(iconSize); dest.writeInt(iconBacks != null ? iconBacks.length : 0); if (iconBacks != null) { for (BitmapDrawable d : iconBacks) { dest.writeParcelable(d != null ? d.getBitmap() : null, flags); } } dest.writeParcelable(iconMask != null ? iconMask.getBitmap() : null, flags); dest.writeParcelable(iconUpon != null ? iconUpon.getBitmap() : null, flags); } public static final Creator<ComposedIconInfo> CREATOR = new Creator<ComposedIconInfo>() { @Override public ComposedIconInfo createFromParcel(Parcel source) { return new ComposedIconInfo(source); } @Override public ComposedIconInfo[] newArray(int size) { return new ComposedIconInfo[0]; } }; }
core/java/android/app/IconPackHelper.java +284 −20 Original line number Diff line number Diff line Loading @@ -15,12 +15,28 @@ */ package android.app; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Random; import android.content.pm.PackageInfo; import android.content.res.IThemeService; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.PaintDrawable; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.util.TypedValue; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; Loading @@ -40,24 +56,47 @@ import android.util.DisplayMetrics; /** @hide */ public class IconPackHelper { private static final String TAG = IconPackHelper.class.getSimpleName(); private static final String ICON_MASK_TAG = "iconmask"; private static final String ICON_BACK_TAG = "iconback"; private static final String ICON_UPON_TAG = "iconupon"; private static final String ICON_SCALE_TAG = "scale"; private static final String ICON_BACK_FORMAT = "iconback%d"; private static final ComponentName ICON_BACK_COMPONENT; private static final ComponentName ICON_MASK_COMPONENT; private static final ComponentName ICON_UPON_COMPONENT; private static final ComponentName ICON_SCALE_COMPONENT; private static final float DEFAULT_SCALE = 1.0f; private static final int COMPOSED_ICON_COOKIE = 128; private final Context mContext; private Map<ComponentName, String> mIconPackResourceMap; private String mLoadedIconPackName; private Resources mLoadedIconPackResource; private float mIconScale; private ComposedIconInfo mComposedIconInfo; private int mIconBackCount = 0; static { ICON_BACK_COMPONENT = new ComponentName(ICON_BACK_TAG, ""); ICON_MASK_COMPONENT = new ComponentName(ICON_MASK_TAG, ""); ICON_UPON_COMPONENT = new ComponentName(ICON_UPON_TAG, ""); ICON_SCALE_COMPONENT = new ComponentName(ICON_SCALE_TAG, ""); } public IconPackHelper(Context context) { mContext = context; mIconPackResourceMap = new HashMap<ComponentName, String>(); ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); mComposedIconInfo = new ComposedIconInfo(); mComposedIconInfo.iconSize = am.getLauncherLargeIconSize(); mComposedIconInfo.iconDensity = am.getLauncherLargeIconDensity(); } private static void loadResourcesFromXmlParser(XmlPullParser parser, private void loadResourcesFromXmlParser(XmlPullParser parser, Map<ComponentName, String> iconPackResources) throws XmlPullParserException, IOException { mIconBackCount = 0; int eventType = parser.getEventType(); do { Loading @@ -65,6 +104,21 @@ public class IconPackHelper { continue; } if (parseComposedIconComponent(parser, iconPackResources)) { continue; } if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { String factor = parser.getAttributeValue(null, "factor"); if (factor == null) { if (parser.getAttributeCount() == 1) { factor = parser.getAttributeValue(0); } } iconPackResources.put(ICON_SCALE_COMPONENT, factor); continue; } if (!parser.getName().equalsIgnoreCase("item")) { continue; } Loading Loading @@ -100,28 +154,98 @@ public class IconPackHelper { } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); } private boolean isComposedIconComponent(String tag) { return tag.equalsIgnoreCase(ICON_MASK_TAG) || tag.equalsIgnoreCase(ICON_BACK_TAG) || tag.equalsIgnoreCase(ICON_UPON_TAG); } private boolean parseComposedIconComponent(XmlPullParser parser, Map<ComponentName, String> iconPackResources) { String icon; String tag = parser.getName(); if (!isComposedIconComponent(tag)) { return false; } if (parser.getAttributeCount() >= 1) { if (tag.equalsIgnoreCase(ICON_BACK_TAG)) { mIconBackCount = parser.getAttributeCount(); for (int i = 0; i < mIconBackCount; i++) { tag = String.format(ICON_BACK_FORMAT, i); icon = parser.getAttributeValue(i); iconPackResources.put(new ComponentName(tag, ""), icon); } } else { icon = parser.getAttributeValue(0); iconPackResources.put(new ComponentName(tag, ""), icon); } return true; } return false; } public void loadIconPack(String packageName) throws NameNotFoundException { if (packageName == null) { mLoadedIconPackResource = null; mLoadedIconPackName = null; mComposedIconInfo.iconBacks = null; mComposedIconInfo.iconMask = mComposedIconInfo.iconUpon = null; mComposedIconInfo.iconScale = 0; } else { mIconBackCount = 0; Resources res = createIconResource(mContext, packageName); mIconPackResourceMap = getIconResMapFromXml(res, packageName); mLoadedIconPackResource = res; mLoadedIconPackName = packageName; String scale = mIconPackResourceMap.get(ICON_SCALE_TAG); loadComposedIconComponents(); } } public ComposedIconInfo getComposedIconInfo() { return mComposedIconInfo; } private void loadComposedIconComponents() { mComposedIconInfo.iconMask = (BitmapDrawable) getDrawableForName(ICON_MASK_COMPONENT); mComposedIconInfo.iconUpon = (BitmapDrawable) getDrawableForName(ICON_UPON_COMPONENT); // Take care of loading iconback which can have multiple images if (mIconBackCount > 0) { mComposedIconInfo.iconBacks = new BitmapDrawable[mIconBackCount]; for (int i = 0; i < mIconBackCount; i++) { mComposedIconInfo.iconBacks[i] = (BitmapDrawable) getDrawableForName( new ComponentName(String.format(ICON_BACK_FORMAT, i), "")); } } // Get the icon scale from this pack String scale = mIconPackResourceMap.get(ICON_SCALE_COMPONENT); if (scale != null) { try { mIconScale = Float.valueOf(scale); mComposedIconInfo.iconScale = Float.valueOf(scale); } catch (NumberFormatException e) { mComposedIconInfo.iconScale = DEFAULT_SCALE; } } else { mComposedIconInfo.iconScale = DEFAULT_SCALE; } } private Drawable getDrawableForName(ComponentName component) { if (isIconPackLoaded()) { String item = mIconPackResourceMap.get(component); if (!TextUtils.isEmpty(item)) { int id = getResourceIdForDrawable(item); if (id != 0) { return mLoadedIconPackResource.getDrawable(id); } } public float getIconScale() { return mIconScale; } return null; } public static Resources createIconResource(Context context, String packageName) throws NameNotFoundException { Loading Loading @@ -151,7 +275,7 @@ public class IconPackHelper { return res; } public static Map<ComponentName, String> getIconResMapFromXml(Resources res, String packageName) { public Map<ComponentName, String> getIconResMapFromXml(Resources res, String packageName) { XmlPullParser parser = null; InputStream inputStream = null; Map<ComponentName, String> iconPackResources = new HashMap<ComponentName, String>(); Loading Loading @@ -254,16 +378,20 @@ public class IconPackHelper { if (!isIconPackLoaded()) { return 0; } ComponentName compName = new ComponentName(info.packageName.toLowerCase(), info.name.toLowerCase()); ComponentName compName = new ComponentName(info.packageName.toLowerCase(), info.name.toLowerCase()); String drawable = mIconPackResourceMap.get(compName); if (drawable == null) { if (drawable != null) { int resId = getResourceIdForDrawable(drawable); if (resId != 0) return resId; } // Icon pack doesn't have an icon for the activity, fallback to package icon compName = new ComponentName(info.packageName.toLowerCase(), ""); drawable = mIconPackResourceMap.get(compName); if (drawable == null) { return 0; } } return getResourceIdForDrawable(drawable); } Loading @@ -277,12 +405,148 @@ public class IconPackHelper { public Drawable getDrawableForActivity(ActivityInfo info) { int id = getResourceIdForActivityIcon(info); if (id == 0) return null; return mLoadedIconPackResource.getDrawable(id); return mLoadedIconPackResource.getDrawable(id, false); } public Drawable getDrawableForActivityWithDensity(ActivityInfo info, int density) { int id = getResourceIdForActivityIcon(info); if (id == 0) return null; return mLoadedIconPackResource.getDrawableForDensity(id, density); return mLoadedIconPackResource.getDrawableForDensity(id, density, false); } public static class IconCustomizer { private static final Random sRandom = new Random(); private static final IThemeService sThemeService; static { sThemeService = IThemeService.Stub.asInterface( ServiceManager.getService(Context.THEME_SERVICE)); } public static Drawable getComposedIconDrawable(Drawable icon, Context context, ComposedIconInfo iconInfo) { final Resources res = context.getResources(); return getComposedIconDrawable(icon, res, iconInfo); } public static Drawable getComposedIconDrawable(Drawable icon, Resources res, ComposedIconInfo iconInfo) { if (iconInfo == null) return icon; Drawable back = null; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[sRandom.nextInt(iconInfo.iconBacks.length)]; } Bitmap bmp = createIconBitmap(icon, res, back, iconInfo.iconMask, iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize); return bmp != null ? new BitmapDrawable(res, bmp): null; } public static void getValue(Resources res, int resId, TypedValue outValue, Drawable baseIcon) { if (!(baseIcon instanceof BitmapDrawable)) return; final String pkgName = res.getAssets().getAppName(); TypedValue tempValue = new TypedValue(); tempValue.setTo(outValue); outValue.assetCookie = COMPOSED_ICON_COOKIE; outValue.data = resId & (COMPOSED_ICON_COOKIE << 24 | 0x00ffffff); outValue.string = getCachedIconPath(pkgName, resId, outValue.density); if (!(new File(outValue.string.toString()).exists())) { // compose the icon and cache it final ComposedIconInfo iconInfo = res.getComposedIconInfo(); Drawable back = null; if (iconInfo.iconBacks != null && iconInfo.iconBacks.length > 0) { back = iconInfo.iconBacks[(outValue.string.hashCode() & 0x7fffffff) % iconInfo.iconBacks.length]; } Bitmap bmp = createIconBitmap(baseIcon, res, back, iconInfo.iconMask, iconInfo.iconUpon, iconInfo.iconScale, iconInfo.iconSize); if (!cacheComposedIcon(bmp, getCachedIconName(pkgName, resId, outValue.density))) { Log.w(TAG, "Unable to cache icon " + outValue.string); // restore the original TypedValue outValue.setTo(tempValue); } } } private static Bitmap createIconBitmap(Drawable icon, Resources res, Drawable iconBack, Drawable iconMask, Drawable iconUpon, float scale, int iconSize) { if (iconSize <= 0) return null; final Canvas canvas = new Canvas(); canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.ANTI_ALIAS_FLAG, Paint.FILTER_BITMAP_FLAG)); if (icon instanceof PaintDrawable) { PaintDrawable painter = (PaintDrawable) icon; painter.setIntrinsicWidth(iconSize); painter.setIntrinsicHeight(iconSize); } else if (icon instanceof BitmapDrawable) { // Ensure the bitmap has a density. BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; Bitmap bitmap = bitmapDrawable.getBitmap(); if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { bitmapDrawable.setTargetDensity(res.getDisplayMetrics()); } canvas.setDensity(bitmap.getDensity()); } Bitmap bitmap = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888); canvas.setBitmap(bitmap); // Scale the original Rect oldBounds = new Rect(); oldBounds.set(icon.getBounds()); icon.setBounds(0, 0, iconSize, iconSize); canvas.save(); canvas.scale(scale, scale, iconSize / 2, iconSize / 2); icon.draw(canvas); canvas.restore(); // Mask off the original if iconMask is not null if (iconMask != null) { iconMask.setBounds(icon.getBounds()); ((BitmapDrawable) iconMask).getPaint().setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); iconMask.draw(canvas); } // Draw the iconBacks if not null and then the original (scaled and masked) icon on top if (iconBack != null) { iconBack.setBounds(icon.getBounds()); ((BitmapDrawable) iconBack).getPaint().setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); iconBack.draw(canvas); } // Finally draw the foreground if one was supplied if (iconUpon != null) { iconUpon.draw(canvas); } icon.setBounds(oldBounds); bitmap.setDensity(canvas.getDensity()); return bitmap; } private static boolean cacheComposedIcon(Bitmap bmp, String path) { try { return sThemeService.cacheComposedIcon(bmp, path); } catch (RemoteException e) { Log.e(TAG, "Unable to cache icon.", e); } return false; } private static String getCachedIconPath(String pkgName, int resId, int density) { return String.format("%s/%s", ThemeUtils.SYSTEM_THEME_ICON_CACHE_DIR, getCachedIconName(pkgName, resId, density)); } private static String getCachedIconName(String pkgName, int resId, int density) { return String.format("%s_%08x_%d.png", pkgName, resId, density); } } }
core/java/android/app/ResourcesManager.java +15 −3 Original line number Diff line number Diff line Loading @@ -284,20 +284,31 @@ public class ResourcesManager { //Map application icon if (pkgInfo != null && pkgInfo.applicationInfo != null) { appInfo = pkgInfo.applicationInfo; if (appInfo.themedIcon != 0) iconResources.put(appInfo.icon, appInfo); if (appInfo.themedIcon != 0 || iconResources.get(appInfo.icon) == null) { iconResources.put(appInfo.icon, appInfo); } } //Map activity icons. if (pkgInfo != null && pkgInfo.activities != null) { for (ActivityInfo ai : pkgInfo.activities) { if (ai.themedIcon != 0 && ai.icon != 0) { if (ai.icon != 0 && (ai.themedIcon != 0 || iconResources.get(ai.icon) == null)) { iconResources.put(ai.icon, ai); } else if (ai.themedIcon != 0 && appInfo != null && appInfo.icon != 0) { } else if (appInfo != null && appInfo.icon != 0 && (ai.themedIcon != 0 || iconResources.get(appInfo.icon) == null)) { iconResources.put(appInfo.icon, ai); } } } r.setIconResources(iconResources); final IPackageManager pm = getPackageManager(); try { ComposedIconInfo iconInfo = pm.getComposedIconInfo(); r.setComposedIconInfo(iconInfo); } catch (RemoteException e) { e.printStackTrace(); } } public final int applyConfigurationToResourcesLocked(Configuration config, Loading Loading @@ -349,6 +360,7 @@ public class ResourcesManager { AssetManager am = r.getAssets(); if (am.hasThemeSupport()) { r.setIconResources(null); r.setComposedIconInfo(null); detachThemeAssets(am); if (config.customTheme != null) { attachThemeAssets(am, config.customTheme); Loading