package com.android.launcher3; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Map; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import android.app.Activity; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import com.android.launcher3.settings.SettingsProvider; public class IconPackHelper { public final static String[] sSupportedActions = new String[] { "org.adw.launcher.THEMES", "com.gau.go.launcherex.theme" }; public static final String[] sSupportedCategories = new String[] { "com.fede.launcher.THEME_ICONPACK", "com.anddoes.launcher.THEME", "com.teslacoilsw.launcher.THEME" }; // Holds package/class -> drawable private Map mIconPackResources; private final Context mContext; private String mLoadedIconPackName; private Resources mLoadedIconPackResource; IconPackHelper(Context context) { mContext = context; mIconPackResources = new HashMap(); } public static Map getSupportedPackages(Context context) { Intent i = new Intent(); Map packages = new HashMap(); PackageManager packageManager = context.getPackageManager(); for (String action : sSupportedActions) { i.setAction(action); for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { IconPackInfo info = new IconPackInfo(r, packageManager); packages.put(r.activityInfo.packageName, info); } } i = new Intent(Intent.ACTION_MAIN); for (String category : sSupportedCategories) { i.addCategory(category); for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { IconPackInfo info = new IconPackInfo(r, packageManager); packages.put(r.activityInfo.packageName, info); } i.removeCategory(category); } return packages; } private static void loadResourcesFromXmlParser(XmlPullParser parser, Map iconPackResources) throws XmlPullParserException, IOException { int eventType = parser.getEventType(); do { if (eventType != XmlPullParser.START_TAG) { continue; } if (!parser.getName().equalsIgnoreCase("item")) { continue; } String component = parser.getAttributeValue(null, "component"); String drawable = parser.getAttributeValue(null, "drawable"); // Validate component/drawable exist if (TextUtils.isEmpty(component) || TextUtils.isEmpty(drawable)) { continue; } // Validate format/length of component if (!component.startsWith("ComponentInfo{") || !component.endsWith("}") || component.length() < 16) { continue; } // Sanitize stored value component = component.substring(14, component.length() - 1).toLowerCase(); ComponentName name = null; if (!component.contains("/")) { // Package icon reference iconPackResources.put(component, drawable); } else { name = ComponentName.unflattenFromString(component); if (name != null) { iconPackResources.put(name.getPackageName(), drawable); iconPackResources.put(name.getClassName(), drawable); } } } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); } private static void loadApplicationResources(Context context, Map iconPackResources, String packageName) { Field[] drawableItems = null; try { Context appContext = context.createPackageContext(packageName, Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); drawableItems = Class.forName(packageName+".R$drawable", true, appContext.getClassLoader()).getFields(); } catch (Exception e){ return; } for (Field f : drawableItems) { String name = f.getName(); String icon = name.toLowerCase(); name = name.replaceAll("_", "."); iconPackResources.put(name, icon); int activityIndex = name.lastIndexOf("."); if (activityIndex <= 0 || activityIndex == name.length() - 1) { continue; } String iconPackage = name.substring(0, activityIndex); if (TextUtils.isEmpty(iconPackage)) { continue; } iconPackResources.put(iconPackage, icon); String iconActivity = name.substring(activityIndex + 1); if (TextUtils.isEmpty(iconActivity)) { continue; } iconPackResources.put(iconPackage + "." + iconActivity, icon); } } public boolean loadIconPack(String packageName) { mIconPackResources = getIconPackResources(mContext, packageName); Resources res = null; try { res = mContext.getPackageManager().getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return false; } mLoadedIconPackResource = res; mLoadedIconPackName = packageName; return true; } public static Map getIconPackResources(Context context, String packageName) { if (TextUtils.isEmpty(packageName)) { return null; } Resources res = null; try { res = context.getPackageManager().getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return null; } XmlPullParser parser = null; InputStream inputStream = null; Map iconPackResources = new HashMap(); try { inputStream = res.getAssets().open("appfilter.xml"); XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); parser = factory.newPullParser(); parser.setInput(inputStream, "UTF-8"); } catch (Exception e) { // Catch any exception since we want to fall back to parsing the xml/ // resource in all cases int resId = res.getIdentifier("appfilter", "xml", packageName); if (resId != 0) { parser = res.getXml(resId); } } if (parser != null) { try { loadResourcesFromXmlParser(parser, iconPackResources); return iconPackResources; } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // Cleanup resources if (parser instanceof XmlResourceParser) { ((XmlResourceParser) parser).close(); } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { } } } } // Application uses a different theme format (most likely launcher pro) int arrayId = res.getIdentifier("theme_iconpack", "array", packageName); if (arrayId == 0) { arrayId = res.getIdentifier("icon_pack", "array", packageName); } if (arrayId != 0) { String[] iconPack = res.getStringArray(arrayId); for (String entry : iconPack) { if (TextUtils.isEmpty(entry)) { continue; } String icon = entry.toLowerCase(); entry = entry.replaceAll("_", "."); iconPackResources.put(entry, icon); int activityIndex = entry.lastIndexOf("."); if (activityIndex <= 0 || activityIndex == entry.length() - 1) { continue; } String iconPackage = entry.substring(0, activityIndex); if (TextUtils.isEmpty(iconPackage)) { continue; } iconPackResources.put(iconPackage, icon); String iconActivity = entry.substring(activityIndex + 1); if (TextUtils.isEmpty(iconActivity)) { continue; } iconPackResources.put(iconPackage + "." + iconActivity, icon); } } else { loadApplicationResources(context, iconPackResources, packageName); } return iconPackResources; } public void unloadIconPack() { mLoadedIconPackResource = null; mLoadedIconPackName = null; if (mIconPackResources != null) { mIconPackResources.clear(); } } public static void pickIconPack(final Context context, final boolean pickIcon) { Map supportedPackages = getSupportedPackages(context); if (supportedPackages.isEmpty()) { Toast.makeText(context, R.string.no_iconpacks_summary, Toast.LENGTH_SHORT).show(); return; } final IconAdapter adapter = new IconAdapter(context, supportedPackages); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.dialog_pick_iconpack_title); if (!pickIcon) { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int position) { if (adapter.isOriginalIconPack(position)) { return; } String selectedPackage = adapter.getItem(position); SettingsProvider.putString(context, SettingsProvider.SETTINGS_UI_GENERAL_ICONS_ICON_PACK, selectedPackage); LauncherAppState.getInstance().getIconCache().flush(); LauncherAppState.getInstance().getModel().forceReload(); } }); } else { builder.setAdapter(adapter, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { String selectedPackage = adapter.getItem(which); Launcher launcherActivity = (Launcher) context; if (TextUtils.isEmpty(selectedPackage)) { launcherActivity.onActivityResult(Launcher.REQUEST_PICK_ICON, Activity.RESULT_OK, null); } else { Intent i = new Intent(); i.setClass(context, IconPickerActivity.class); i.putExtra(IconPickerActivity.PACKAGE_NAME_EXTRA, selectedPackage); launcherActivity.startActivityForResult(i, Launcher.REQUEST_PICK_ICON); } } }); } builder.show().getWindow().getDecorView().setAlpha(0.8f); } boolean isIconPackLoaded() { return mLoadedIconPackResource != null && mLoadedIconPackName != null && mIconPackResources != null; } private int getResourceIdForDrawable(String resource) { int resId = mLoadedIconPackResource.getIdentifier(resource, "drawable", mLoadedIconPackName); return resId; } public Resources getIconPackResources() { return mLoadedIconPackResource; } public int getResourceIdForActivityIcon(ActivityInfo info) { String drawable = mIconPackResources.get(info.name.toLowerCase()); if (drawable == null) { // Icon pack doesn't have an icon for the activity, fallback to package icon drawable = mIconPackResources.get(info.packageName.toLowerCase()); if (drawable == null) { return 0; } } return getResourceIdForDrawable(drawable); } static class IconPackInfo { String packageName; CharSequence label; Drawable icon; IconPackInfo(ResolveInfo r, PackageManager packageManager) { packageName = r.activityInfo.packageName; icon = r.loadIcon(packageManager); label = r.loadLabel(packageManager); } IconPackInfo(){ } public IconPackInfo(String label, Drawable icon, String packageName) { this.label = label; this.icon = icon; this.packageName = packageName; } } private static class IconAdapter extends BaseAdapter { ArrayList mSupportedPackages; LayoutInflater mLayoutInflater; String mCurrentIconPack; int mCurrentIconPackPosition; IconAdapter(Context ctx, Map supportedPackages) { mLayoutInflater = LayoutInflater.from(ctx); mSupportedPackages = new ArrayList(supportedPackages.values()); Collections.sort(mSupportedPackages, new Comparator() { @Override public int compare(IconPackInfo lhs, IconPackInfo rhs) { return lhs.label.toString().compareToIgnoreCase(rhs.toString()); } }); Resources res = ctx.getResources(); String defaultLabel = res.getString(R.string.default_iconpack_title); Drawable icon = res.getDrawable(R.mipmap.ic_launcher_home); mSupportedPackages.add(0, new IconPackInfo(defaultLabel, icon, "")); mCurrentIconPack = SettingsProvider.getStringCustomDefault(ctx, SettingsProvider.SETTINGS_UI_GENERAL_ICONS_ICON_PACK, ""); } @Override public int getCount() { return mSupportedPackages.size(); } @Override public String getItem(int position) { return (String) mSupportedPackages.get(position).packageName; } @Override public long getItemId(int position) { return 0; } public boolean isOriginalIconPack(int position) { return mCurrentIconPackPosition == position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mLayoutInflater.inflate(R.layout.iconpack_chooser, null); } IconPackInfo info = mSupportedPackages.get(position); TextView txtView = (TextView) convertView.findViewById(R.id.title); txtView.setText(info.label); ImageView imgView = (ImageView) convertView.findViewById(R.id.icon); imgView.setImageDrawable(info.icon); ImageView chk = (ImageView) convertView.findViewById(R.id.check); boolean isCurrentIconPack = info.packageName.equals(mCurrentIconPack); chk.setVisibility(isCurrentIconPack ? View.VISIBLE : View.GONE); if (isCurrentIconPack) { mCurrentIconPackPosition = position; } return convertView; } } }