Android第三方无源码应用图标icon定制、修改、替换与添加背景

本文详细介绍了如何在Android系统中替换、定制和修改应用图标,包括在Launcher层面和系统层面的修改方法,以及如何添加应用图标背景和移除图标白边。涉及 BubbleTextView.java、IconCache.java、PackageParser.java 和 ParsingPackageUtils.java 等关键代码文件的修改。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为适配的各种UI,客户经常会对要求应用的图标进行替换、定制或修改。修改的方式有很多,效果也不尽相同,本文章将会把所有的修改方式与君解析。

目录

替换应用图标

一、Launcher层面修改

通过BubbleTextView.java修改

通过IconCache.java修改

二、系统层面修改

Android11之前的修改方式

Android11之后的修改方式

添加应用图标背景

Android11之前

Android11之后

移除图标白边,图标铺满

Android11之前

Android11之后


替换应用图标

一、Launcher层面修改

缺点:仅在Launcher界面上面显示的icon被修改了,setting和系统安装应用界面等还是原来的图标。

通过BubbleTextView.java修改

BubbleTextView.java这个类在Launcher运行阶段属于比较晚的,所以如果说有大量第三方应用图标需要替换的话,在Launcher启动时可能会出现卡顿。

代码路径:Launcher3/src/com/android/launcher3/BubbleTextView.java

基于Android9:

private void applyIconAndLabel(ItemInfoWithIcon info) {
        //FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
		String pkg = info.getIntent().getComponent().getPackageName();
		if("com.android.vending".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.player);
            BitmapDrawable clock_drawable = new BitmapDrawable(bitmap);
            if(clock_drawable!=null)setIcon(clock_drawable);
		}else if("com.android.chrome".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.chrome);
            BitmapDrawable clock_drawable = new BitmapDrawable(bitmap);
            if(clock_drawable!=null)setIcon(clock_drawable);
		}else{
			FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
			  setIcon(iconDrawable);
		}
        mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
 
        //setIcon(iconDrawable);
        setText(info.title);
        if (info.contentDescription != null) {
            setContentDescription(info.isDisabled()
                    ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
                    : info.contentDescription);
        }
    }

基于Android13:

代码路径:packages/apps/Launcher3/src/com/android/launcher3/BubbleTextView.java

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;


...
...

@UiThread
    protected void applyIconAndLabel(ItemInfoWithIcon info) {
        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER
                || mDisplay == DISPLAY_TASKBAR;
        int flags = useTheme ? FLAG_THEMED : 0;
        if (mHideBadge) {
            flags |= FLAG_NO_BADGE;
        }
        FastBitmapDrawable iconDrawable = info.newIcon(getContext(), flags);
        mDotParams.appColor = iconDrawable.getIconColor();
        mDotParams.dotColor = getContext().getResources()
                .getColor(android.R.color.system_accent3_200, getContext().getTheme());
//add
        //setIcon(iconDrawable);
		String pkg = info.getIntent().getComponent().getPackageName();
		String cls = info.getIntent().getComponent().getClassName();
		if("com.android.vending".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_player);
				setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.chrome".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_chrome);
				setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.google.android.apps.nbu.files".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_files);
				setIcon(new FastBitmapDrawable(bitmap));
		}/* else if("com.android.fmradio".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_fmradio);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.gallery3d.app.GalleryActivity".equals(cls)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_gallery);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.cooliris.media.Gallery".equals(cls)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_video);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.soundrecorder".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_soundrecorder);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.deskclock".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_deskclock);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.calendar".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_calendar);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.music".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_music);
			setIcon(new FastBitmapDrawable(bitmap));
		}else if("com.android.calculator2".equals(pkg)){
			Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.BS_calculator2);
			setIcon(new FastBitmapDrawable(bitmap));
		} */else{
			  setIcon(iconDrawable);
		}
//add
        applyLabel(info);
    }
通过IconCache.java修改

通过IconCache.java修改第三方应用图标,相比于BubbleTextView运行的更早并且可以单独修改应用图标的大小。

代码路径:Launcher3/src/com/android/launcher3/IconCache.java

基于Android9:

import android.support.v4.content.ContextCompat;

    
protected CacheEntry cacheLocked(
            @NonNull ComponentName componentName,
            @NonNull Provider<LauncherActivityInfo> infoProvider,
            UserHandle user, boolean usePackageIcon, boolean useLowResIcon) {
        Preconditions.assertWorkerThread();
        ComponentKey cacheKey = new ComponentKey(componentName, user);
        CacheEntry entry = mCache.get(cacheKey);
        if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
            entry = new CacheEntry();
            mCache.put(cacheKey, entry);

            // Check the DB first.
            LauncherActivityInfo info = null;
            boolean providerFetchedOnce = false;

            if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
                info = infoProvider.get();
                providerFetchedOnce = true;

                if (info != null) {
                    LauncherIcons li = LauncherIcons.obtain(mContext);
                    li.createBadgedIconBitmap(getFullResIcon(info), info.getUser(),
                            info.getApplicationInfo().targetSdkVersion).applyTo(entry);
                    li.recycle();
                } else {
                    if (usePackageIcon) {
                        CacheEntry packageEntry = getEntryForPackageLocked(
                                componentName.getPackageName(), user, false);
                        if (packageEntry != null) {
                            if (DEBUG) Log.d(TAG, "using package default icon for " +
                                    componentName.toShortString());
                            packageEntry.applyTo(entry);
                            entry.title = packageEntry.title;
                            entry.contentDescription = packageEntry.contentDescription;
                        }
                    }
                    if (entry.icon == null) {
                        if (DEBUG) Log.d(TAG, "using default icon for " +
                                componentName.toShortString());
                        getDefaultIcon(user).applyTo(entry);
                    }
                }
            }

            if (TextUtils.isEmpty(entry.title)) {
                if (info == null && !providerFetchedOnce) {
                    info = infoProvider.get();
                    providerFetchedOnce = true;
                }
                if (info != null) {
                    entry.title = info.getLabel();
                    entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
                }
            }
        }
               /* if("com.google.android.inputmethod.latin".equals(componentName.getPackageName().toString())) {
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.inputmethod);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.apple.android.music".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.apple);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.spotify.music".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.spotify);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.audible.application".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.audible);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.amazon.mp3".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.amazon);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("deezer.android.app".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.deezer);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.android.chrome".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.chrome);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.android.vending".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.vending);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.hiby.music".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.hiby);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }else if("com.mxtech.videoplayer.pro".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.mxtech);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }                 */       
			if("com.mxtech.videoplayer.pro".equals(componentName.getPackageName().toString())){
                       LauncherIcons li = LauncherIcons.obtain(mContext);
                               Drawable drawable = ContextCompat.getDrawable(mContext, R.mipmap.mxtech);
                entry.icon = li.createIconBitmap(drawable,0.82f);
               }
        return entry;
    }

Launcher3/src/com/android/launcher3/graphics/LauncherIcons.java

-    private Bitmap createIconBitmap(Drawable icon, float scale) {
+    public Bitmap createIconBitmap(Drawable icon, float scale) {
         int width = mIconBitmapSize;
         int height = mIconBitmapSize;

二、系统层面修改

相比于在Launcher层面进行修改的局限性,在系统层修改可以在安装解析应用的时候就把对应的应用图标所替换掉,无疑是最优解。

Android11之前的修改方式

以下示例基于Android7代码:

1、frameworks/base/core/java/android/content/pm/PackageParser.java

                 com.android.internal.R.styleable.AndroidManifestApplication_theme, 0);
         ai.descriptionRes = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestApplication_description, 0);
+       //add
+       String pkg2 = pkgName;
+       //android.util.Log.i("hqb","pkg2:"+pkg2);
+       
+       if(pkg2.equals("cn.kuwo.kwmusichd")){
+                     ai.icon = R.drawable.bs_kwmusichd;
+       }else if(pkg2.equals("com.kk.xx.newplayer")){
+                     ai.icon = R.drawable.bs_newplayer;
+       }else if(pkg2.equals("com.chaozh.iReaderFree")){
+                     ai.icon = R.drawable.bs_ireaderfree;
+       }else if(pkg2.equals("com.xiaobin.ecdict")){
+                     ai.icon = R.drawable.bs_ecdict;
+       }
+       //add
 
         if ((flags&PARSE_IS_SYSTEM) != 0) {
             if (sa.getBoolean(
@@ -3408,6 +3422,21 @@ public class PackageParser {
 
         outInfo.packageName = owner.packageName;
 
+        //add
+        String pkg2 = owner.applicationInfo.packageName;
+        android.util.Log.i("11111Tag","pkg2:"+pkg2);
+        
+        if(pkg2.equals("cn.kuwo.kwmusichd")){
+                      outInfo.icon = R.drawable.bs_kwmusichd;
+        }else if(pkg2.equals("com.kk.xx.newplayer")){
+                      outInfo.icon = R.drawable.bs_newplayer;
+        }else if(pkg2.equals("com.chaozh.iReaderFree")){
+                      outInfo.icon = R.drawable.bs_ireaderfree;
+        }else if(pkg2.equals("com.xiaobin.ecdict")){
+                      outInfo.icon = R.drawable.bs_ecdict;
+        }
+        //add
+
         return true;
     }
 
@@ -3571,7 +3600,7 @@ public class PackageParser {
             a.info.screenOrientation = sa.getInt(
                     R.styleable.AndroidManifestActivity_screenOrientation,
                     SCREEN_ORIENTATION_UNSPECIFIED);
-
+                       a.info.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
             a.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
             final boolean appDefault = (owner.applicationInfo.privateFlags
                     & PRIVATE_FLAG_RESIZEABLE_ACTIVITIES) != 0;
@@ -3723,6 +3752,21 @@ public class PackageParser {
             a.info.exported = a.intents.size() > 0;
         }
 
+        //add
+        String pkg2 = owner.applicationInfo.packageName;
+        android.util.Log.i("hqb","pkg2:"+pkg2);
+        
+        if(pkg2.equals("cn.kuwo.kwmusichd")){
+                      a.info.icon = R.drawable.bs_kwmusichd;
+        }else if(pkg2.equals("com.kk.xx.newplayer")){
+                      a.info.icon = R.drawable.bs_newplayer;
+        }else if(pkg2.equals("com.chaozh.iReaderFree")){
+                      a.info.icon = R.drawable.bs_ireaderfree;
+        }else if(pkg2.equals("com.xiaobin.ecdict")){
+                      a.info.icon = R.drawable.bs_ecdict;
+        }
+        //add
+
         return a;
     }

2、frameworks/base/core/res/res/values/symbols.xml

+  <java-symbol type="drawable" name="bs_newplayer" />
+  <java-symbol type="drawable" name="bs_ireaderfree" />
+  <java-symbol type="drawable" name="bs_kwmusichd" />
+  <java-symbol type="drawable" name="bs_ecdict" />

3、frameworks/base/core/res/res/drawable添加图标

以下示例基于Android9代码:

1、frameworks/base/core/java/android/content/pm/PackageParser.java

private static boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo,
            String[] outError, String tag, TypedArray sa, boolean nameRequired,
            int nameRes, int labelRes, int iconRes, int roundIconRes, int logoRes, int bannerRes) {
        // This case can only happen in unit tests where we sometimes need to create fakes
        // of various package parser data structures.
        if (sa == null) {
            outError[0] = tag + " does not contain any attributes";
            return false;
        }

        String name = sa.getNonConfigurationString(nameRes, 0);
        if (name == null) {
            if (nameRequired) {
                outError[0] = tag + " does not specify android:name";
                return false;
            }
        } else {
            outInfo.name
                = buildClassName(owner.applicationInfo.packageName, name, outError);
            if (outInfo.name == null) {
                return false;
            }
        }

        final boolean useRoundIcon =
                Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useRoundIcon);
        int roundIconVal = useRoundIcon ? sa.getResourceId(roundIconRes, 0) : 0;
        if (roundIconVal != 0) {
            outInfo.icon = roundIconVal;
            outInfo.nonLocalizedLabel = null;
        } else {
            int iconVal = sa.getResourceId(iconRes, 0);
            if (iconVal != 0) {
                outInfo.icon = iconVal;
                outInfo.nonLocalizedLabel = null;
            }
        }

        int logoVal = sa.getResourceId(logoRes, 0);
        if (logoVal != 0) {
            outInfo.logo = logoVal;
        }

        int bannerVal = sa.getResourceId(bannerRes, 0);
        if (bannerVal != 0) {
            outInfo.banner = bannerVal;
        }

        TypedValue v = sa.peekValue(labelRes);
        if (v != null && (outInfo.labelRes=v.resourceId) == 0) {
            outInfo.nonLocalizedLabel = v.coerceToString();
        }
		//add
		String applabel= sa.getNonConfigurationString(labelRes, 0);
		String pkg1 = outInfo.packageName;
		String pkg2 = owner.applicationInfo.packageName;
		android.util.Log.i("11111Tag","outInfo name:"+outInfo.name);
		android.util.Log.i("11111Tag","pkg2:"+pkg2);
		android.util.Log.i("11111Tag","tag:"+tag);
		
		if(pkg2.equals("com.google.android.inputmethod.latin")){
			      outInfo.icon = R.drawable.cus_inputmethod;
		}else if(pkg2.equals("com.apple.android.music")){
			      outInfo.icon = R.drawable.cus_apple;
		}else if(pkg2.equals("com.spotify.music")){
			      outInfo.icon = R.drawable.cus_spotify;
		}else if(pkg2.equals("com.audible.application")){
			      outInfo.icon = R.drawable.cus_audible;
		}else if(pkg2.equals("com.amazon.mp3")){
			      outInfo.icon = R.drawable.cus_amazon;
		}else if(pkg2.equals("deezer.android.app")){
			      outInfo.icon = R.drawable.cus_deezer;
		}else if(pkg2.equals("com.android.chrome")){
			      outInfo.icon = R.drawable.cus_chrome;
		}else if(pkg2.equals("com.android.vending")){
			      outInfo.icon = R.drawable.cus_vending;
		}else if(pkg2.equals("com.hiby.music")){
			      outInfo.icon = R.drawable.cus_hiby;
		}/* else if(pkg2.equals("com.mxtech.videoplayer.pro")){
			      outInfo.icon = R.drawable.cus_mxtech;
		} */
		//add
        outInfo.packageName = owner.packageName;

        return true;
    }

2、在frameworks/base/core/res/res/drawable/下面添加所要替换的图标资源

3、frameworks/base/core/res/res/values/symbols.xml中添加图标symbols才能找到资源


  <java-symbol type="drawable" name="ic_account_circle" />
  <java-symbol type="drawable" name="cus_amazon" />
  <java-symbol type="drawable" name="cus_apple" />
  <java-symbol type="drawable" name="cus_audible" />
  <java-symbol type="drawable" name="cus_chrome" />
  <java-symbol type="drawable" name="cus_deezer" />
  <java-symbol type="drawable" name="cus_hiby" />
  <java-symbol type="drawable" name="cus_inputmethod" />
Android11之后的修改方式

代码路径:frameworks/base/core/java/android/content/pm/parsing/ParsingPackageUtils.java

具体是在 parseBaseAppBasicFlags(pkg, sa);中解析的:

private void parseBaseAppBasicFlags(ParsingPackage pkg, TypedArray sa) {
             int targetSdk = pkg.getTargetSdkVersion();
             //@formatter:off
             // CHECKSTYLE:off
             pkg
                     // Default true
                     .setAllowBackup(bool(true, R.styleable.AndroidManifestApplication_allowBackup, sa))
                     .setAllowClearUserData(bool(true, R.styleable.AndroidManifestApplication_allowClearUserData, sa))
                     .setAllowClearUserDataOnFailedRestore(bool(true, R.styleable.AndroidManifestApplication_allowClearUserDataOnFailedRestore, sa))
                     .setAllowNativeHeapPointerTagging(bool(true, R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, sa))
                     .setEnabled(bool(true, R.styleable.AndroidManifestApplication_enabled, sa))
                     .setExtractNativeLibs(bool(true, R.styleable.AndroidManifestApplication_extractNativeLibs, sa))
                     .setHasCode(bool(true, R.styleable.AndroidManifestApplication_hasCode, sa))
                     // Default false
                     .setAllowTaskReparenting(bool(false, R.styleable.AndroidManifestApplication_allowTaskReparenting, sa))
                     .setCantSaveState(bool(false, R.styleable.AndroidManifestApplication_cantSaveState, sa))
....
                      .setDescriptionRes(resId(R.styleable.AndroidManifestApplication_description, sa))
                -      .setIconRes(resId(R.styleable.AndroidManifestApplication_icon, sa))// 去掉之前的设置icon的方法
                      .setLogo(resId(R.styleable.AndroidManifestApplication_logo, sa))
                      .setNetworkSecurityConfigRes(resId(R.styleable.AndroidManifestApplication_networkSecurityConfig, sa))
                      .setRoundIconRes(resId(R.styleable.AndroidManifestApplication_roundIcon, sa))
                      .setTheme(resId(R.styleable.AndroidManifestApplication_theme, sa))
                      // Strings
                      .setClassLoaderName(string(R.styleable.AndroidManifestApplication_classLoader, sa))
                      .setRequiredAccountType(string(R.styleable.AndroidManifestApplication_requiredAccountType, sa))
                      .setRestrictedAccountType(string(R.styleable.AndroidManifestApplication_restrictedAccountType, sa))
                      .setZygotePreloadName(string(R.styleable.AndroidManifestApplication_zygotePreloadName, sa))
                      // Non-Config String
                      .setPermission(nonConfigString(0, R.styleable.AndroidManifestApplication_permission, sa));
     
                    // add core start
            String pkgName = pkg.getPackageName();
			android.util.Log.d("hqb","pkgname="+pkgName);
            if(!TextUtils.isEmpty(pkgName)&&pkgName.equals("com.android.settings")){
                pkg.setIconRes(com.android.internal.R.mipmap.sym_def_app_icon);
			}else if(!TextUtils.isEmpty(pkgName)&&pkgName.equals("com.tencent.android.qqdownloader")){
                pkg.setIconRes(com.android.internal.R.mipmap.sym_def_app_icon);
			}else if(!TextUtils.isEmpty(pkgName)&&pkgName.equals("com.mxtech.videoplayer.pro")){
                pkg.setIconRes(com.android.internal.R.mipmap.sym_def_app_icon);
			}else{
                pkg.setIconRes(com.android.internal.R.mipmap.sym_def_app_icon);
            }
           //add core end
     
              // CHECKSTYLE:on
              //@formatter:on
          }
android13修改第三方应用图标

1、涉及到修改的文件为:

/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

2、/frameworks/base/core/res/res/下添加资源并声明

  <java-symbol type="drawable" name="ic_account_circle" />
  <java-symbol type="drawable" name="cus_amazon" />
  <java-symbol type="drawable" name="cus_apple" />
  <java-symbol type="drawable" name="cus_audible" />
  <java-symbol type="drawable" name="cus_chrome" />
  <java-symbol type="drawable" name="cus_deezer" />
  <java-symbol type="drawable" name="cus_hiby" />
  <java-symbol type="drawable" name="cus_inputmethod" />

3、解析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;
}

4、解析Activity时替换图标

有些应用的launcher activity也声明了icon的话,就需要在ParsedActivityUtils文件中的parseActivityOrAlias方法也添加上上述替换图标的代码。通过在循环中的intentFilter判断包含ACTION_MAIN,CATEGORY_LAUNCHER字段,且设置了icon的话,去替换图标。

@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);
        }
    }
	......
	......
	......
}

添加应用图标背景

Android11之前

代码路径:Launcher3\src\com\android\launcher3\graphics\LauncherIcons.java


createIconBitmap下面
return getRoundedBitmap(bitmap);
    }
	 public static Bitmap getRoundedBitmap(Bitmap mBitmap){
        //Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.frame);  
        //创建与原始位图大小相同的画布位图ᄡᄡᄑᄄ￐ᅡ샤ᅫ콰ᄐ  
        /* Bitmap bgBitmap = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        //初始化画布
        Canvas mCanvas = new Canvas(bgBitmap);
        Paint mPaint = new Paint();
        Rect mRect = new Rect(5, 5, mBitmap.getWidth()-5, mBitmap.getHeight()-5);
        RectF mRectF = new RectF(mRect);
        //设置圆角半径 
        float roundPx = 480;
        mPaint.setAntiAlias(true);
        //绘制圆角矩形
        mCanvas.drawRoundRect(mRectF, roundPx, roundPx, mPaint);
        //设置图像的叠加模式, 此处模式选择可参考后面的规则
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        //绘制图像  
        mCanvas.drawBitmap(mBitmap, mRect, mRect, mPaint); */
        return addBorderToImage(mBitmap);
    }
    private static Bitmap addBorderToImage(Bitmap src) {
		Bitmap output = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.iconback).copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(output);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
        //paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
		android.util.Log.d("hqb1","  output.getHeight()="+output.getHeight()+"  output.getWidth()="+output.getWidth());
        Rect dstRect = new Rect(0,0,output.getWidth(),output.getHeight());
        canvas.drawBitmap(src, null, dstRect, paint);
        return output;
    }

Android11之后

在Android13代码中BaseIconFactory.java从Launcher中移到了frameworks/libs/下面,所以修改这个地方所有的图标后面都会有背景。

frameworks/libs/systemui/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Bundle;
import android.graphics.BitmapFactory;

....
....

 private final Rect mOldBounds = new Rect();
     private final SparseBooleanArray mIsUserBadged = new SparseBooleanArray();
-    protected final Context mContext;
+    protected static Context mContext;
     private final Canvas mCanvas;
     private final PackageManager mPm;
     private final ColorExtractor mColorExtractor;

....
....

private Bitmap createIconBitmap(@NonNull Drawable icon, float scale, int size,
            Bitmap.Config config) {
        Bitmap bitmap = Bitmap.createBitmap(size, size, config);
        if (icon == null) {
            return bitmap;
        }
       ....
        ....
        ....
		//return bitmap;
        return addBorderToImage(bitmap);
    }
    private static Bitmap addBorderToImage(Bitmap src) {
               Bitmap output = BitmapFactory.decodeResource(mContext.getResources(),R.drawable.iconback).copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(output);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG);
        //paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
               android.util.Log.d("hqb1","  output.getHeight()="+output.getHeight()+"  output.getWidth()="+output.getWidth());
        Rect dstRect = new Rect(5,5,output.getWidth()-5,output.getHeight()-5);
        canvas.drawBitmap(src, null, dstRect, paint);
        return output;
     }

移除图标白边,图标铺满

Android11之前

代码路径:packages/apps/Launcher3/src/com/android/launcher3/graphics/LauncherIcons.java

 private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
            RectF outIconBounds, float[] outScale) {
        float scale = 1f;
		//hqingbin 2023 Remove the icon white edge begin
        /* if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
            boolean[] outShape = new boolean[1];
            if (mWrapperIcon == null) {
                mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
                        .mutate();
            }
            AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
            dr.setBounds(0, 0, 1, 1);
            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
            if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
                FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
                fsd.setDrawable(icon);
                fsd.setScale(scale);
                icon = dr;
                scale = getNormalizer().getScale(icon, outIconBounds, null, null);

                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
            }
        } else {
            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
        } */
		//hqingbin 2023 Remove the icon white edge end
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
        outScale[0] = scale;
        return icon;
    }

Android11之后

代码路径:frameworks/libs/systemui/iconloaderlib/src/com/android/launcher3/icons/BaseIconFactory.java

 private Drawable normalizeAndWrapToAdaptiveIcon(@NonNull Drawable icon,
            boolean shrinkNonAdaptiveIcons, RectF outIconBounds, float[] outScale) {
        if (icon == null) {
            return null;
        }
        float scale = 1f;

       /*  if (shrinkNonAdaptiveIcons && ATLEAST_OREO) {
            if (mWrapperIcon == null) {
                mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
                        .mutate();
            }
            AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
            dr.setBounds(0, 0, 1, 1);
            boolean[] outShape = new boolean[1];
            scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
            if (!(icon instanceof AdaptiveIconDrawable) && !outShape[0]) {
                FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
                fsd.setDrawable(icon);
                fsd.setScale(scale);
                icon = dr;
                scale = getNormalizer().getScale(icon, outIconBounds, null, null);

                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
            }
        } else { */
            scale = getNormalizer().getScale(icon, outIconBounds, null, null);
        //}

        outScale[0] = scale;
        return icon;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值