安卓 - 实现图标包支持

2022-09-04 23:26:26

我正在尝试在我的应用程序中实现图标包支持,以便与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的任何信息,我看不到任何内容提供商,所以我只能假设这些启动器应用程序正在使用类似于我的方法来检索资产。

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


答案 1

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

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

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


答案 2

有点太晚了,但这是我几年前在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


推荐