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 的任何信息我看不到任何内容提供程序,因此我只能假设这些启动器应用程序正在使用类似于我的方法来检索资产。

有没有人能给我任何关于如何做到这一点的信息或指出我正确的方向?

4

2 回答 2

1

不是官方来源,但Apex 启动器主题教程指出

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

所以看起来好像没有使用特殊的内容提供程序——该机制只需要适应 Jelly Bean 上引入的安全机制即可。

于 2014-06-13T14:49:03.213 回答
1

有点太晚了,但这是我几年前在 Solid Launcher 中实现 shuch 功能的方式。它可能并不完美,但它会给你一个关于主题如何工作的基本解释。

主题引擎.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

于 2020-05-21T22:22:34.487 回答