Android应用 - 实现图标包支持

13

我正在尝试在我的应用程序中实现图标包支持,以便与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+上付费应用程序上存在的前向锁定使资产私有。(在此处详细说明:访问Jelly Bean上其他Android应用程序的资产

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

是否有人能够给我任何相关信息或指引我正确的方向?

2个回答

1

这不是官方来源,但Apex Launcher主题教程指出:

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

因此,看起来没有使用特殊内容提供程序 - 该机制只需适应Jelly Bean引入的安全机制即可。


1
“有点晚了,但是这里是我几年前在Solid Launcher中实现此功能的方式。它可能不完美,但会基本解释主题如何工作。”

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接