Android - 实现图标包支持

时间:2013-06-26 10:15:25

标签: java android icons assets

我正在尝试在我的应用程序中实现图标包支持,以便使用与Apex,Nova,ADW等相同的图标包。也可以使用我的应用程序。 使用此代码,找到appfilter xml文件,然后解析它以获得可绘制的名称,我已经能够使用免费主题:

Context context = createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY);
                Resources res = context.getResources();
                String str = "";
                res.getAssets().list(str);

但是使用付费图标包时,这不起作用。返回的资产是0.我相信这是因为JB +上的付费应用程序存在转发锁定,这使得资产变得私密。 (在此概述:Accessing assets of other Android app on Jelly Bean

我无法找到有关如何支持图标包和反编译图标包的任何信息apk我看不到内容提供商所以我只能假设这些启动器应用程序使用类似于我的方法来检索资产。

是否有人能够向我提供有关如何做到这一点的任何信息或指出我正确的方向?

2 个答案:

答案 0 :(得分:1)

不是官方来源,而是Apex Launcher Theme Tutorial注明

  

接下来,打开res / xml目录下的appfilter.xml文件。 (注意:此文件以前位于assets目录下,但JellyBean中的新应用程序加密功能使主题引擎无法访问付费主题的资产。)

因此看起来好像没有使用特殊的内容提供者 - 该机制只需要适应Jelly Bean上引入的安全机制。

答案 1 :(得分:1)

太晚了,但是几年前我在Solid Launcher中实现了shuch功能。它可能并不完美,但是它将为您提供有关主题工作原理的基本解释。

ThemeEngine.java:

package com.majeur.launcher.data;

import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.Log;

import com.majeur.launcher.R;
import com.majeur.launcher.preference.PreferenceHelper;
import com.majeur.launcher.util.BitmapUtils;
import com.majeur.launcher.util.Constants;
import com.majeur.launcher.util.Matrix;
import com.majeur.util.ArrayUtils;

import org.xmlpull.v1.XmlPullParser;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;


/**
 * Created by MajeurAndroid on 10/10/14.
 */
public class ThemeEngine {

    private static final String TAG = ThemeEngine.class.getSimpleName();
    private static final String CATEGORY_APEX_THEME = "com.anddoes.launcher.THEME";
    private static final String COMPONENT = "component";
    private static final String RES_DRAWABLE = "drawable";
    private static final String RES_XML = "xml";
    private static final String RES_BOOL = "bool";
    private static final String RES_ARRAY = "array";
    private static final String APPFILTER = "appfilter";
    private static final String ICONS = "icon_pack";
    private static final String ATTR_SUPPORT_ICON_PACK = "config_iconpack";
    private static final String ATTR_ITEM = "item";
    private static final String ATTR_SCALE = "scale";
    private static final String ATTR_FACTOR = "factor";
    private static final String ATTR_ICON_BACK = "iconback";
    private static final String ATTR_ICON_MASK = "iconmask";
    private static final String ATTR_ICON_UPON = "iconupon";
    private static final String[] ATTR_IMGS = {"img1", "img2", "img3", "img4", "img5"};

    private static final Matrix sMatrix = new Matrix();

    private Application mApplication;
    private boolean mIsOperational;

    private int mIconSize;

    private List<ComponentName> mAppFilterComponentNames = new ArrayList<ComponentName>();
    private List<String> mAppFilterDrawableStrings = new ArrayList<String>();

    private List<String> mAppFilterIconsBack = new ArrayList<String>(5);
    private List<String> mAppFilterIconsMask = new ArrayList<String>(5);
    private List<String> mAppFilterIconsUpon = new ArrayList<String>(5);
    private float mAppFilterScaleFactor = 1f;
    private boolean mSupportIconBack;
    private boolean mSupportIconMask;
    private boolean mSupportIconUpon;
    private boolean mMultipleIconBack;
    private boolean mMultipleIconMask;
    private boolean mMultipleIconUpon;

    private Resources mIconPackResources;
    private String mIconPackPackageName;

    private Random mRandom = new Random();

    private Paint mPaint;

    public ThemeEngine(Application application) {
        mApplication = application;
        mIconSize = application.getResources().getDimensionPixelSize(R.dimen.workspace_item_icon_size);

        initializeIconPack();
    }

    public boolean assertPackageIsCompatible(String iconPackPackageName) {
        List<IconPackRetriever.IconPackInfo> iconPackInfoList = new IconPackRetriever(mApplication).loadIconPacksInfo();
        for (IconPackRetriever.IconPackInfo iconPackInfo : iconPackInfoList)
            if (TextUtils.equals(iconPackInfo.packageName, iconPackPackageName))
                return true;
        return false;
    }

    public Bitmap getIconInPack(Resources iconPackResources, String pkgName, String resName) {
        int id = iconPackResources.getIdentifier(resName, RES_DRAWABLE, pkgName);

        return id != 0 ? BitmapFactory.decodeResource(iconPackResources, id,
                BitmapUtils.getOptimalBitmapOptions(iconPackResources, id, mIconSize)) : null;
    }

    public Bitmap getSpecialIcon(String iconPackPackage, String iconResName) {
        Resources localResources;
        try {
            localResources = mApplication
                    .createPackageContext(iconPackPackage, Context.CONTEXT_IGNORE_SECURITY)
                    .getResources();
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }

        int id = localResources.getIdentifier(iconResName, RES_DRAWABLE, iconPackPackage);

        return id != 0 ?
                BitmapFactory.decodeResource(localResources, id, BitmapUtils.getOptimalBitmapOptions(localResources, id, mIconSize))
                : null;
    }

    public String getIconPackPackageName() {
        return PreferenceHelper.preferences().getString(Constants.PREF_ICON_PACK_PKG_NAME, null);
    }

    public void setIconPack(String packageName) {
        if (packageName == null)
            PreferenceHelper.preferences()
                    .edit()
                    .remove(Constants.PREF_ICON_PACK_PKG_NAME)
                    .apply();
        else {
            PreferenceHelper.preferences()
                    .edit()
                    .putString(Constants.PREF_ICON_PACK_PKG_NAME, packageName)
                    .apply();
        }

        initializeIconPack();
    }

    public boolean isOperational() {
        return mIsOperational;
    }

    private void initializeIconPack() {
        try {
            prepareIconPackOrThrow();
            mIsOperational = true;
        } catch (Exception e) {
            //e.printStackTrace();

            PreferenceHelper.preferences()
                    .edit()
                    .remove(Constants.PREF_ICON_PACK_PKG_NAME)
                    .apply();
            mIsOperational = false;
        }
    }

    private void prepareIconPackOrThrow() throws Exception {
        mAppFilterComponentNames.clear();
        mAppFilterDrawableStrings.clear();
        mAppFilterIconsBack.clear();
        mAppFilterIconsMask.clear();
        mAppFilterIconsUpon.clear();

        Context localContext;
        mIconPackPackageName = getIconPackPackageName();
        if (mIconPackPackageName == null)
            throw new NullPointerException("Icon pack packageName is null");

        // throws NameNotFoundException
        localContext = mApplication.createPackageContext(mIconPackPackageName, Context.CONTEXT_IGNORE_SECURITY);

        mIconPackResources = localContext.getResources();

        int identifier = mIconPackResources.getIdentifier(APPFILTER, RES_XML, mIconPackPackageName);

        // can throw InvalidResIdException if id is 0 (eg. xml doesn't exist)
        XmlPullParser appFilterPullParser = mIconPackResources.getXml(identifier);

        // throws XmlPullParserException and IOException
        int eventType = appFilterPullParser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG) {

                String name = appFilterPullParser.getName();
                switch (name) {
                    case ATTR_ITEM:
                        String component = appFilterPullParser.getAttributeValue(null, COMPONENT);
                        String drawableName = appFilterPullParser.getAttributeValue(null, RES_DRAWABLE);

                        if (component != null && drawableName != null) {
                            try {
                                ComponentName cn = getComponentNameFromXmlAttribute(component);
                                mAppFilterComponentNames.add(cn);
                                mAppFilterDrawableStrings.add(drawableName);
                            } catch (StringIndexOutOfBoundsException e) {
                                Log.w(TAG, "Invalid flatten ComponentName: " + component);
                            }
                        }
                        break;
                    case ATTR_ICON_BACK:
                        for (String attribute : ATTR_IMGS) {
                            String s = appFilterPullParser.getAttributeValue(null, attribute);
                            if (s != null) {
                                mAppFilterIconsBack.add(s);
                            }
                        }
                        break;
                    case ATTR_ICON_MASK:
                        for (String attribute : ATTR_IMGS) {
                            String s = appFilterPullParser.getAttributeValue(null, attribute);
                            if (s != null)
                                mAppFilterIconsMask.add(s);
                        }
                        break;
                    case ATTR_ICON_UPON:
                        for (String attribute : ATTR_IMGS) {
                            String s = appFilterPullParser.getAttributeValue(null, attribute);
                            if (s != null)
                                mAppFilterIconsUpon.add(s);
                        }
                        break;
                    case ATTR_SCALE:
                        String s = appFilterPullParser.getAttributeValue(null, ATTR_FACTOR);
                        if (s != null)
                            mAppFilterScaleFactor = Float.parseFloat(s);
                        break;
                }
            }
            eventType = appFilterPullParser.next();
        }

        mSupportIconBack = mAppFilterIconsBack.size() > 0;
        mSupportIconMask = mAppFilterIconsMask.size() > 0;
        mSupportIconUpon = mAppFilterIconsUpon.size() > 0;
        mMultipleIconBack = mAppFilterIconsBack.size() > 1;
        mMultipleIconMask = mAppFilterIconsMask.size() > 1;
        mMultipleIconUpon = mAppFilterIconsUpon.size() > 1;

        setPaints();
    }

    private ComponentName getComponentNameFromXmlAttribute(String xmlAttribute) {
        String s = xmlAttribute.substring(14, xmlAttribute.length() - 1);
        return ComponentName.unflattenFromString(s);
    }

    private void setPaints() {
        mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
        mPaint.setAntiAlias(true);
    }

    private boolean isIconAvailable(ComponentName cn) {
        return mAppFilterComponentNames.contains(cn);
    }

    /**
     * Return themed icon if any, else return null
     *
     * @param componentName Name of the activity represented by the item
     * @return Themed icon if any available
     */
    public Bitmap peekIconBitmap(ComponentName componentName, int iconSize) {
        if (isIconAvailable(componentName)) {
            int index = mAppFilterComponentNames.indexOf(componentName);
            int resId = mIconPackResources.getIdentifier(mAppFilterDrawableStrings.get(index), RES_DRAWABLE, mIconPackPackageName);

            // Return prebuilt icon
            if (resId != 0)
                return BitmapFactory.decodeResource(mIconPackResources, resId, BitmapUtils.getOptimalBitmapOptions(mIconPackResources, resId, iconSize));
        }
        return null;
    }

    /**
     * Entry point for requesting app/shortcut icon
     *
     * @param defaultBitmap Default bitmap
     * @return Themed icon
     */
    public Bitmap loadIconBitmap(Bitmap defaultBitmap) {
        return getThemedBitmap(defaultBitmap);
    }

    /**
     * Entry point for requesting app/shortcut icon
     *
     * @param defaultBitmap Default bitmap
     * @return Themed icon
     */
    public Bitmap loadShortcutBitmap(Bitmap defaultBitmap) {
        return getThemedBitmap(defaultBitmap);
    }

    // Themes an icon, used if applicationIcon is not supported by icon pack or for shortcut icons
    private Bitmap getThemedBitmap(Bitmap appIcon) {

        Bitmap iconBack = null;
        if (mSupportIconBack) {
            String iconBackName;
            if (mMultipleIconBack) {
                iconBackName = randItem(mAppFilterIconsBack);
            } else iconBackName = mAppFilterIconsBack.get(0);

            int iconBackId = mIconPackResources.getIdentifier(iconBackName, RES_DRAWABLE, mIconPackPackageName);

            if (iconBackId != 0)
                iconBack = BitmapFactory.decodeResource(mIconPackResources, iconBackId, BitmapUtils.getOptimalBitmapOptions(mIconPackResources, iconBackId, mIconSize));

        }

        Bitmap iconUpon = null;
        if (mSupportIconUpon) {
            String iconUponName;
            if (mMultipleIconUpon) {
                iconUponName = randItem(mAppFilterIconsUpon);
            } else iconUponName = mAppFilterIconsUpon.get(0);

            int iconUponId = mIconPackResources.getIdentifier(iconUponName, RES_DRAWABLE, mIconPackPackageName);

            if (iconUponId != 0)
                iconUpon = BitmapFactory.decodeResource(mIconPackResources, iconUponId, BitmapUtils.getOptimalBitmapOptions(mIconPackResources, iconUponId, mIconSize));

        }

        Bitmap iconMask = null;
        if (mSupportIconMask) {
            String iconMaskName;
            if (mMultipleIconMask) {
                iconMaskName = randItem(mAppFilterIconsMask);
            } else iconMaskName = mAppFilterIconsMask.get(0);

            int iconMaskId = mIconPackResources.getIdentifier(iconMaskName, RES_DRAWABLE, mIconPackPackageName);

            if (iconMaskId != 0)
                iconMask = BitmapFactory.decodeResource(mIconPackResources, iconMaskId, BitmapUtils.getOptimalBitmapOptions(mIconPackResources, iconMaskId, mIconSize));

        }

        Bitmap resultBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
        Canvas resultCanvas = new Canvas(resultBitmap);

        if (iconBack != null) {
            resultCanvas.drawBitmap(iconBack, getResizeMatrix(iconBack, mIconSize, mIconSize), mPaint);
            iconBack.recycle();
        }

        int targetSize = ((int) (mIconSize * mAppFilterScaleFactor));
        int offset = (mIconSize / 2) - (targetSize / 2);

        if (iconMask != null) {
            Bitmap tempBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(tempBitmap);
            canvas.drawBitmap(appIcon, getResizeTranslateMatrix(appIcon, targetSize, targetSize, offset, offset), mPaint);

            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            canvas.drawBitmap(iconMask, getResizeMatrix(iconMask, mIconSize, mIconSize), mPaint);
            mPaint.setXfermode(null);

            iconMask.recycle();

            resultCanvas.drawBitmap(tempBitmap, 0, 0, mPaint);

            tempBitmap.recycle();
        } else {
            resultCanvas.drawBitmap(appIcon, getResizeTranslateMatrix(appIcon, targetSize, targetSize, offset, offset), mPaint);
        }

        if (iconUpon != null) {
            resultCanvas.drawBitmap(iconUpon, getResizeMatrix(iconUpon, mIconSize, mIconSize), mPaint);
            iconUpon.recycle();
        }

        return resultBitmap;
    }

    private <T> T randItem(List<T> list) {
        return list.get(mRandom.nextInt(list.size()));

    }

    private Matrix getResizeMatrix(Bitmap bm, int newWidth, int newHeight) {
        return getResizeTranslateMatrix(bm, newWidth, newHeight, 0, 0);
    }

    private Matrix getResizeTranslateMatrix(Bitmap bm, int newWidth, int newHeight, float dx, float dy) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        sMatrix.reset();
        sMatrix.postScale(scaleWidth, scaleHeight);
        sMatrix.postTranslate(dx, dy);
        return sMatrix;
    }

    public static class IconPackRetriever {

        private Context mContext;
        private PackageManager mPackageManager;

        public static IconPackRetriever newInstance(Context context) {
            return new IconPackRetriever(context);
        }

        private IconPackRetriever(Context context) {
            mContext = context;
            mPackageManager = context.getPackageManager();
        }

        public List<IconPackInfo> loadIconPacksInfo() {
            final Intent intent = new Intent(Intent.ACTION_MAIN, null);
            intent.addCategory(CATEGORY_APEX_THEME);
            List<ResolveInfo> resolveInfoList = mPackageManager.queryIntentActivities(intent, 0);

            List<IconPackInfo> iconPackInfoList = new LinkedList<>();

            for (ResolveInfo resolveInfo : resolveInfoList) {
                if (supportsIconPack(resolveInfo)) {
                    IconPackInfo iconPackInfo = new IconPackInfo();
                    iconPackInfo.packageName = resolveInfo.activityInfo.packageName;
                    iconPackInfo.icon = resolveInfo.activityInfo.loadIcon(mPackageManager);
                    iconPackInfo.label = resolveInfo.activityInfo.loadLabel(mPackageManager).toString();

                    iconPackInfoList.add(iconPackInfo);
                }
            }

            return iconPackInfoList;
        }

        private boolean supportsIconPack(ResolveInfo resolveInfo) {
            Resources localResources;
            try {
                localResources = mContext
                        .createPackageContext(resolveInfo.activityInfo.packageName, Context.CONTEXT_IGNORE_SECURITY)
                        .getResources();
            } catch (PackageManager.NameNotFoundException e) {
                return false;
            }

            int id = localResources.getIdentifier(ATTR_SUPPORT_ICON_PACK, RES_BOOL, resolveInfo.activityInfo.packageName);

            return id != 0 && localResources.getBoolean(id);
        }

        public String[] getIconNamesForPack(String packageName) {
            Resources localResources;
            try {
                localResources = mContext
                        .createPackageContext(packageName, Context.CONTEXT_IGNORE_SECURITY)
                        .getResources();
            } catch (PackageManager.NameNotFoundException e) {
                return null;
            }

            int id = localResources.getIdentifier(ICONS, RES_ARRAY, packageName);

            return id != 0 ? assertedArray(packageName, localResources.getStringArray(id)) : null;
        }

        private String[] assertedArray(String iconPackPackageName, String[] drawableNames) {
            Resources localResources;
            try {
                localResources = mContext.createPackageContext(iconPackPackageName, Context.CONTEXT_IGNORE_SECURITY)
                        .getResources();
            } catch (PackageManager.NameNotFoundException e) {
                return null;
            }

            List<String> list = ArrayUtils.asList(drawableNames);
            Iterator<String> iterator = list.iterator();

            while (iterator.hasNext())
                if (localResources.getIdentifier(iterator.next(), RES_DRAWABLE, iconPackPackageName) == 0)
                    iterator.remove();

            return list.toArray(new String[list.size()]);
        }

        public static class IconPackInfo {
            public String packageName;
            public Drawable icon;
            public String label;
        }
    }
}

或在这里: https://gist.github.com/MajeurAndroid/a51869e826b9a283a173b65e923857f8