/* * Copyright (C) 2008 The Android Open Source Project * * Modifications to original by Jeff Corcoran * * 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 ca.mimic.apphangar; import java.io.File; 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.Locale; import java.util.Map; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; 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.net.Uri; 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.LinearLayout; import android.widget.TextView; 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<String, String> mIconPackResources; private final Context mContext; private String mLoadedIconPackName; private Resources mLoadedIconPackResource; protected static Settings mActivity; protected static AppsRowItem mTask; protected static Settings.PrefsGet prefs; IconPackHelper(Context context) { mContext = context; mIconPackResources = new HashMap<String, String>(); } public static void setActivity(Settings activity) { mActivity = activity; } public static void setTask(AppsRowItem task) { mTask = task; } public static Map<String, IconPackInfo> getSupportedPackages(Context context) { Intent i = new Intent(); Map<String, IconPackInfo> packages = new HashMap<String, IconPackInfo>(); 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; } public static Map<String, IconPackInfo> getPickerPackages(Context context) { Intent i = new Intent(); Map<String, IconPackInfo> packages = new HashMap<String, IconPackInfo>(); PackageManager packageManager = context.getPackageManager(); i.setAction(Settings.ACTION_ADW_PICK_ICON); for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { IconPackInfo info = new IconPackInfo(r, packageManager); packages.put(r.activityInfo.packageName, info); } return packages; } private static void loadResourcesFromXmlParser(XmlPullParser parser, Map<String, String> 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(Locale.getDefault()); ComponentName name; 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<String, String> iconPackResources, String packageName) { Field[] drawableItems; 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(Locale.getDefault()); 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; try { res = mContext.getPackageManager().getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return false; } mLoadedIconPackResource = res; mLoadedIconPackName = packageName; return true; } public static Map<String, String> getIconPackResources(Context context, String packageName) { if (TextUtils.isEmpty(packageName)) { return null; } Resources res; try { res = context.getPackageManager().getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return null; } XmlPullParser parser = null; InputStream inputStream = null; Map<String, String> iconPackResources = new HashMap<String, String>(); 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(Locale.getDefault()); 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 IconPackInfo installNewPack(Context context) { try { Intent intent = context.getPackageManager().getLaunchIntentForPackage(Settings.PLAY_STORE_PACKAGENAME); ResolveInfo rInfo = context.getPackageManager().resolveActivity(intent, 0); Drawable icon = new IconCacheHelper(context).getFullResIcon(rInfo); String label = context.getResources().getString(R.string.title_icon_pack_install); return new IconPackInfo(label, icon, Settings.PLAY_STORE_PACKAGENAME); } catch (Exception e) { return null; } } @SuppressLint("InflateParams") public static void pickIconPack(final Context context, final boolean isPicker, final boolean moreAppIcon) { Map<String, IconPackInfo> supportedPackages = (isPicker) ? getPickerPackages(context) : getSupportedPackages(context); Boolean noPackages = false; if (supportedPackages.isEmpty()) { noPackages = true; } final IconAdapter adapter = new IconAdapter(context, supportedPackages, isPicker, moreAppIcon); AlertDialog.Builder builder = new AlertDialog.Builder(context); if (noPackages) { builder.setTitle(R.string.title_icon_pack_not_found); } else { builder.setTitle(isPicker ? R.string.title_icon_pack_choose_from : R.string.title_icon_pack_picker); } AlertDialog alertDialog = builder.create(); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int position) { String selectedPackage = adapter.getItem(position); if (isPicker && !selectedPackage.equals(Settings.PLAY_STORE_PACKAGENAME)) { if (mTask != null && selectedPackage.equals(mTask.getPackageName())) { ComponentName componentName = ComponentName.unflattenFromString(mTask.getPackageName() + "/" + mTask.getClassName()); Settings.resetIconComponent(componentName); return; } else if (moreAppIcon && position == 0) { Settings.resetIconCache(Settings.MORE_APPS_PACKAGE); return; } try { Intent intent = new Intent(); intent.setPackage(selectedPackage); intent.setAction(Settings.ACTION_ADW_PICK_ICON); mActivity.startActivityForResult(intent, moreAppIcon ? 2 : 1); } catch (Exception e) { Tools.HangarLog("Change icon intent failed! " + e + " : " + selectedPackage); } return; } prefs = new Settings.PrefsGet(context.getSharedPreferences(context.getPackageName(), Context.MODE_MULTI_PROCESS)); SharedPreferences.Editor mEditor = prefs.editorGet(); Tools.HangarLog("selectedPAckage: " + selectedPackage); if (selectedPackage.equals(Settings.PLAY_STORE_PACKAGENAME)) { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(Settings.PLAY_STORE_SEARCH_URI)); context.startActivity(intent); return; } mEditor.putString(Settings.ICON_PACK_PREFERENCE, selectedPackage); mEditor.apply(); // Deleting cached icons File[] files = context.getCacheDir().listFiles(); for (File file : files) { if (file.toString().contains(IconCacheHelper.getResourceName(Settings.MORE_APPS_PACKAGE))) continue; file.delete(); } Settings.iconPackUpdate.iconPackUpdated(); } }); if (isPicker) { boolean needsWarning = true; String alertTxt; String selectedPackage = prefs.prefsGet().getString(Settings.ICON_PACK_PREFERENCE, null); if (selectedPackage == null || selectedPackage.isEmpty()) { alertTxt = context.getString(R.string.title_icon_pack_no_single_picks); } else { for (int i = 0; i < supportedPackages.size(); i++) { if (supportedPackages.containsKey(selectedPackage)) { needsWarning = false; } } alertTxt = String.format(context.getString(R.string.title_icon_pack_no_single_ui), Tools.getApplicationName(context, selectedPackage)); } if (needsWarning) { LinearLayout iconPackWarning = (LinearLayout) alertDialog.getLayoutInflater().inflate(R.layout.iconpack_main, null); TextView tv = (TextView) iconPackWarning.findViewById(R.id.iconpack_warning); tv.setText(alertTxt); builder.setView(iconPackWarning); } } builder.show(); } public static void pickIconPack(final Context context) { pickIconPack(context, false, false); } public static void pickIconPicker(final Context context) { pickIconPack(context, true, false); } boolean isIconPackLoaded() { return mLoadedIconPackResource != null && mLoadedIconPackName != null && mIconPackResources != null; } private String replaceActivityName(String activityName) { // Hack for Gallery not showing up properly on some icon packs activityName = activityName.replace("com.android.gallery3d.app.galleryactivity", "com.android.gallery3d.app.gallery"); return activityName; } private int getResourceIdForDrawable(String resource) { return mLoadedIconPackResource.getIdentifier(resource, "drawable", mLoadedIconPackName); } public Resources getIconPackResources() { return mLoadedIconPackResource; } public int getResourceIdForActivityIcon(ActivityInfo info) { String activityName = replaceActivityName(info.name.toLowerCase(Locale.getDefault())); String drawable = mIconPackResources.get(activityName); if (drawable == null) { activityName = replaceActivityName(info.name.toLowerCase(Locale.getDefault())); drawable = mIconPackResources.get(activityName); } if (drawable == null) { // Icon pack doesn't have an icon for the activity, fallback to package icon drawable = mIconPackResources.get(info.packageName.toLowerCase(Locale.getDefault())); 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); } public IconPackInfo(String label, Drawable icon, String packageName) { this.label = label; this.icon = icon; this.packageName = packageName; } } private static class IconAdapter extends BaseAdapter { ArrayList<IconPackInfo> mSupportedPackages; LayoutInflater mLayoutInflater; String mCurrentIconPack; int mCurrentIconPackPosition; IconAdapter(Context context, Map<String, IconPackInfo> supportedPackages, boolean isPicker, boolean moreAppIcon) { mLayoutInflater = LayoutInflater.from(context); mSupportedPackages = new ArrayList<IconPackInfo>(supportedPackages.values()); Collections.sort(mSupportedPackages, new Comparator<IconPackInfo>() { @Override public int compare(IconPackInfo lhs, IconPackInfo rhs) { return lhs.label.toString().compareToIgnoreCase(rhs.label.toString()); } }); if (isPicker && (mTask != null || moreAppIcon)) { Resources res = context.getResources(); String defaultLabel = res.getString(R.string.reset_icon); if (moreAppIcon) { Drawable icon = context.getResources().getDrawable(Settings.MORE_APPS_DRAWABLE_RESOURCE); mSupportedPackages.add(0, new IconPackInfo(defaultLabel, icon, Settings.MORE_APPS_PACKAGE)); } else { Intent intent = context.getPackageManager().getLaunchIntentForPackage(mTask.getPackageName()); ResolveInfo rInfo = context.getPackageManager().resolveActivity(intent, 0); Drawable icon = new IconCacheHelper(context).getFullResIcon(rInfo.activityInfo, true); mSupportedPackages.add(0, new IconPackInfo(defaultLabel, icon, mTask.getPackageName())); } } else { Resources res = context.getResources(); String defaultLabel = res.getString(R.string.default_icon_pack); Drawable icon = res.getDrawable(R.drawable.ic_launcher_home); mSupportedPackages.add(0, new IconPackInfo(defaultLabel, icon, "")); } IconPackInfo installNew = installNewPack(context); if (installNew != null) mSupportedPackages.add(installNew); prefs = new Settings.PrefsGet(context.getSharedPreferences(context.getPackageName(), Context.MODE_MULTI_PROCESS)); SharedPreferences mPrefs = prefs.prefsGet(); mCurrentIconPack = mPrefs.getString(Settings.ICON_PACK_PREFERENCE, ""); } @Override public int getCount() { return mSupportedPackages.size(); } @Override public String getItem(int position) { return mSupportedPackages.get(position).packageName; } @Override public long getItemId(int position) { return 0; } @SuppressLint("InflateParams") @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; } } }