Android系统无源码App替换图标

Android系统无源码App替换图标

需求

为了设计美观统一,就需要替换系统中部分内置应用的图标。

系统版本

Android 13

准备工作

准备替换后的图标,最好不同dpi都有,例如:ic_launcher_sogou.png,
知道替换应用的包名,例如:com.sohu.inputmethod.sogou

修改涉及到的文件

主要涉及到frameworks/base目录下的文件

/frameworks/base/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
/frameworks/base/core/res/res/values/symbols.xml

修改步骤

以下替换搜狗输入法图标作为例子说明。

  1. 添加图标

将不同分辨率的图标分别放在各个dpi的目录下。

/frameworks/base/core/res/res/mipmap-hdpi/ic_launcher_sogou.png
/frameworks/base/core/res/res/mipmap-ldpi/ic_launcher_sogou.png
/frameworks/base/core/res/res/mipmap-mdpi/ic_launcher_sogou.png
/frameworks/base/core/res/res/mipmap-xhdpi/ic_launcher_sogou.png
/frameworks/base/core/res/res/mipmap-xxhdpi/ic_launcher_sogou.png
/frameworks/base/core/res/res/mipmap-xxxhdpi/ic_launcher_sogou.png

  1. 声明资源

symbols.xml文件中声明图标资源,以便其他地方通过com.android.internal.R引入该资源使用。

<java-symbol type="mipmap" name="ic_launcher_sogou" />
  1. 解析Application时替换图标

ParsingPackageUtils文件中的parseBaseAppBasicFlags方法添加判断包名,然后替换图标的代码。

private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
	......
	......
	......
	int resIcon = getDefaultIcon(pkg.getPackageName());
	if (resIcon != 0) {
	    pkg.setIconRes(resIcon);
	}
}

public static int getDefaultIcon(String packageName) {
    int resId = 0;
    switch (packageName) {
        case "com.sohu.inputmethod.sogou":
            resId = com.android.internal.R.mipmap.ic_launcher_sogou;
            break;
    }
    return resId;
}
  1. 解析Activity时替换图标

有些应用的launcher activity也声明了icon的话,就需要在ParsedActivityUtils文件中的parseActivityOrAlias方法也添加上上述替换图标的代码。

@NonNull
private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivityImpl activity,
        ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
        TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
        ParseInput input, int parentActivityNameAttr, int permissionAttr,
        int exportedAttr) throws IOException, XmlPullParserException {
   	......
   	......
   	......
    final int depth = parser.getDepth();
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
            && (type != XmlPullParser.END_TAG
            || parser.getDepth() > depth)) {
        if (type != XmlPullParser.START_TAG) {
            continue;
        }

        final ParseResult result;
        if (parser.getName().equals("intent-filter")) {
            ParseResult<ParsedIntentInfoImpl> intentResult = parseIntentFilter(pkg, activity,
                    !isReceiver, visibleToEphemeral, resources, parser, input);
            if (intentResult.isSuccess()) {
                ParsedIntentInfoImpl intentInfo = intentResult.getResult();
                if (intentInfo != null) {
                    IntentFilter intentFilter = intentInfo.getIntentFilter();
                    activity.setOrder(Math.max(intentFilter.getOrder(), activity.getOrder()));
                    activity.addIntent(intentInfo);
                    if (LOG_UNSAFE_BROADCASTS && isReceiver
                            && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
                        int actionCount = intentFilter.countActions();
                        for (int i = 0; i < actionCount; i++) {
                            final String action = intentFilter.getAction(i);
                            if (action == null || !action.startsWith("android.")) {
                                continue;
                            }

                            if (!SAFE_BROADCASTS.contains(action)) {
                                Slog.w(TAG,
                                        "Broadcast " + action + " may never be delivered to "
                                                + pkg.getPackageName() + " as requested at: "
                                                + parser.getPositionDescription());
                            }
                        }
                    }
+                    if (intentFilter.hasAction(ACTION_MAIN)
+                            && intentFilter.hasCategory(CATEGORY_LAUNCHER)
+                            && activity.getIcon() != 0) {
+                        int resIcon = ParsingPackageUtils.getDefaultIcon(pkg.getPackageName());
+                        if (resIcon != 0) {
+                            activity.setIcon(resIcon);
+                        }
+                    }
                }
            }
            result = intentResult;
        } else if (parser.getName().equals("meta-data")) {
            result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
        } else if (parser.getName().equals("property")) {
            result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
        } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
            ParseResult<ParsedIntentInfoImpl> intentResult = parseIntentFilter(pkg, activity,
                    true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
                    resources, parser, input);
            if (intentResult.isSuccess()) {
                ParsedIntentInfoImpl intent = intentResult.getResult();
                if (intent != null) {
                    pkg.addPreferredActivityFilter(activity.getClassName(), intent);
                }
            }
            result = intentResult;
        } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
            ParseResult<ActivityInfo.WindowLayout> layoutResult =
                    parseActivityWindowLayout(resources, parser, input);
            if (layoutResult.isSuccess()) {
                activity.setWindowLayout(layoutResult.getResult());
            }
            result = layoutResult;
        } else {
            result = ParsingUtils.unknownTag(tag, pkg, parser, input);
        }

        if (result.isError()) {
            return input.error(result);
        }
    }
	......
	......
	......
}

通过在循环中的intentFilter判断包含ACTION_MAIN,CATEGORY_LAUNCHER字段,且设置了icon的话,去替换图标。

总结

以上就是Android系统无源码App替换图标的方法。

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值