修改Launcher3图标,给图标加蒙层或影藏图标

最近项目需求修改Launcher3的源码,需求是给图标加上蒙层。

阅读源码发现图标的缓存是在com/android.launcher3/graphics目录下的LauncherIcons.java类中修改。

在createIconBitmap()方法中

修改之前的代码:

public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
        synchronized (sCanvas) {
            final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
            int width = iconBitmapSize;
            int height = iconBitmapSize;

            if (icon instanceof PaintDrawable) {
                PaintDrawable painter = (PaintDrawable) icon;
                painter.setIntrinsicWidth(width);
                painter.setIntrinsicHeight(height);
            } else if (icon instanceof BitmapDrawable) {
                // Ensure the bitmap has a density.
                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
                Bitmap bitmap = bitmapDrawable.getBitmap();
                if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
                    bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
                }
            }

            int sourceWidth = icon.getIntrinsicWidth();
            int sourceHeight = icon.getIntrinsicHeight();
            if (sourceWidth > 0 && sourceHeight > 0) {
                // Scale the icon proportionally to the icon dimensions
                final float ratio = (float) sourceWidth / sourceHeight;
                if (sourceWidth > sourceHeight) {
                    height = (int) (width / ratio);
                } else if (sourceHeight > sourceWidth) {
                    width = (int) (height * ratio);
                }
            }
            // no intrinsic size --> use default size
            int textureWidth = iconBitmapSize;
            int textureHeight = iconBitmapSize;

            Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
                    Bitmap.Config.ARGB_8888);
            final Canvas canvas = sCanvas;
            canvas.setBitmap(bitmap);

            final int left = (textureWidth - width) / 2;
            final int top = (textureHeight - height) / 2;

            sOldBounds.set(icon.getBounds());
            if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
                int offset = Math.max((int) (ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
                        Math.min(left, top));
                int size = Math.max(width, height);
                icon.setBounds(offset, offset, size, size);
            } else {
                icon.setBounds(left, top, left + width, top + height);
            }
            canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
            icon.draw(canvas);
            canvas.restore();
            icon.setBounds(sOldBounds);
            canvas.setBitmap(null);
            return bitmap;
        }
    }

只需要在绘制完图标后再一个Bitmap就行了:

Bitmap bitmapStop = BitmapFactory.decodeResource(context.getResources(), R.mipmap.icon_stop);
            Rect rect = new Rect(left, top, left + width, top + height);
            canvas.drawBitmap(bitmapStop, null, rect, new Paint());

修改完之后代码:

public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
        synchronized (sCanvas) {
            final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
            int width = iconBitmapSize;
            int height = iconBitmapSize;
            if (icon instanceof PaintDrawable) {
                PaintDrawable painter = (PaintDrawable) icon;
                painter.setIntrinsicWidth(width);
                painter.setIntrinsicHeight(height);
            } else if (icon instanceof BitmapDrawable) {
                // Ensure the bitmap has a density.
                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
                Bitmap bitmap = bitmapDrawable.getBitmap();
                if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
                    bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
                }
            }

            int sourceWidth = icon.getIntrinsicWidth();
            int sourceHeight = icon.getIntrinsicHeight();
            if (sourceWidth > 0 && sourceHeight > 0) {
                // Scale the icon proportionally to the icon dimensions
                final float ratio = (float) sourceWidth / sourceHeight;
                if (sourceWidth > sourceHeight) {
                    height = (int) (width / ratio);
                } else if (sourceHeight > sourceWidth) {
                    width = (int) (height * ratio);
                }
            }
            // no intrinsic size --> use default size
            int textureWidth = iconBitmapSize;
            int textureHeight = iconBitmapSize;

            Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
                    Bitmap.Config.ARGB_8888);
            final Canvas canvas = sCanvas;
            canvas.setBitmap(bitmap);

            final int left = (textureWidth - width) / 2;
            final int top = (textureHeight - height) / 2;

            sOldBounds.set(icon.getBounds());
            if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
                int offset = Math.max((int) (ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
                        Math.min(left, top));
                int size = Math.max(width, height);
                icon.setBounds(offset, offset, size, size);
            } else {
                icon.setBounds(left, top, left + width, top + height);
            }
            canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
            icon.draw(canvas);
            // TODO: 2021/7/27 添加背景
            Bitmap bitmapStop = BitmapFactory.decodeResource(context.getResources(), R.mipmap.icon_stop);
            Rect rect = new Rect(left, top, left + width, top + height);
            canvas.drawBitmap(bitmapStop, null, rect, new Paint());
            canvas.restore();
            icon.setBounds(sOldBounds);
            canvas.setBitmap(null);
            return bitmap;
        }
    }

此处可以通过包名判断哪些应用图标需要添加蒙层。

而我们的需求是需要动态变换图标 通过网络请求获取到需要添加蒙层的应用包名,然后进行区分添加不同的蒙层。

在网络请求中调用LauncherModel的updateSessionDisplayInfo()方法传递的参数是需要修改的图标包名

mModel.updateSessionDisplayInfo("需要修改的包名");
    public void updateSessionDisplayInfo(final String packageName) {
        HashSet<String> packages = new HashSet<>();
        packages.add(packageName);
        enqueueModelUpdateTask(new CacheDataUpdatedTask(
                CacheDataUpdatedTask.OP_SESSION_UPDATE, Process.myUserHandle(), packages));
    }

该方法会调用CacheDataUpdatedTask类的execute()方法

    @Override
    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
        IconCache iconCache = app.getIconCache();

        final ArrayList<AppInfo> updatedApps = new ArrayList<>();

        ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
        synchronized (dataModel) {
            for (ItemInfo info : dataModel.itemsIdMap) {
                if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
                    ShortcutInfo si = (ShortcutInfo) info;
                    ComponentName cn = si.getTargetComponent();
                    if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                            && isValidShortcut(si) && cn != null
                            && mPackages.contains(cn.getPackageName())) {
                        iconCache.getTitleAndIcon(si, si.usingLowResIcon);
                        updatedShortcuts.add(si);
                    }
                }
            }
            apps.updateIconsAndLabels(mPackages, mUser, updatedApps);
        }
        bindUpdatedShortcuts(updatedShortcuts, mUser);

        if (!updatedApps.isEmpty()) {
            scheduleCallbackTask(new CallbackTask() {
                @Override
                public void execute(Callbacks callbacks) {
                    callbacks.bindAppsAddedOrUpdated(updatedApps);
                }
            });
        }
    }

然后就会调用CacheDataUpdatedTask类的

apps.updateIconsAndLabels(mPackages, mUser, updatedApps);方法

在往后就会调用AllAppsList类的updateIconsAndLabels()方法

 public void updateIconsAndLabels(HashSet<String> packages, UserHandle user,
            ArrayList<AppInfo> outUpdates) {
        for (AppInfo info : data) {
            if (info.user.equals(user) && packages.contains(info.componentName.getPackageName())) {
                mIconCache.updateTitleAndIcon(info);
                outUpdates.add(info);
            }
        }
    }

最终走到IconCache类的cacheLocked()方法中

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) {
                    List<DevGetallAppStatus.AppsBean> appsBeans1 = new ArrayList<>();
                    List<DevGetallAppStatus.AppsBean> appsBeans2 = new ArrayList<>();
                    if (Launcher.allAppStatus != null && Launcher.allAppStatus != null && Launcher.allAppStatus.size() > 0) {
                        for (int i = 0; i < Launcher.allAppStatus.size(); i++) {
                            if (Launcher.allAppStatus.get(i).enable.equals("0")) {
                                appsBeans1.add(Launcher.allAppStatus.get(i));
                            } else if (Launcher.allAppStatus.get(i).enable.equals("2")) {
                                appsBeans2.add(Launcher.allAppStatus.get(i));
                            }
                        }
                    }
                    entry.icon = LauncherIcons.createBadgedIconBitmap(
                            getFullResIcon(info), info.getUser(), mContext,
                            infoProvider.get().getApplicationInfo().targetSdkVersion);
                    for (int i = 0; i < appsBeans1.size(); i++) {
                        if (info.getComponentName().getPackageName().equals(appsBeans1.get(i).packageName)) {
                            entry.icon = LauncherIcons.createBadgedIconBitmapRed(
                                    getFullResIcon(info), info.getUser(), mContext,
                                    infoProvider.get().getApplicationInfo().targetSdkVersion);
                        }
                    }
                    for (int i = 0; i < appsBeans2.size(); i++) {
                        if (info.getComponentName().getPackageName().equals(appsBeans2.get(i).packageName)) {
                            entry.icon = LauncherIcons.createBadgedIconBitmapGreen(
                                    getFullResIcon(info), info.getUser(), mContext,
                                    infoProvider.get().getApplicationInfo().targetSdkVersion);
                        }
                    }
                } 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());
                            entry.icon = packageEntry.icon;
                            entry.title = packageEntry.title;
                            entry.contentDescription = packageEntry.contentDescription;
                        }
                    }
                    if (entry.icon == null) {
                        if (DEBUG) Log.d(TAG, "using default icon for " +
                                componentName.toShortString());
                        entry.icon = getDefaultIcon(user);
                    }
                }
            }

            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 (Launcher.changeAppsBeans.size() > 0) {
            if (infoProvider.get() != null && infoProvider.get().getComponentName() != null) {
                LauncherActivityInfo info = infoProvider.get();
                List<DevGetallAppStatus.AppsBean> appsBeans1 = new ArrayList<>();
                List<DevGetallAppStatus.AppsBean> appsBeans2 = new ArrayList<>();
                List<DevGetallAppStatus.AppsBean> appsBeans3 = new ArrayList<>();
                for (int i = 0; i < Launcher.changeAppsBeans.size(); i++) {
                    if (Launcher.changeAppsBeans.get(i).enable.equals("0")) {
                        appsBeans1.add(Launcher.changeAppsBeans.get(i));
                    } else if (Launcher.changeAppsBeans.get(i).enable.equals("2")) {
                        appsBeans2.add(Launcher.changeAppsBeans.get(i));
                    } else {
                        appsBeans3.add(Launcher.changeAppsBeans.get(i));
                    }
                }
                for (int i = 0; i < appsBeans1.size(); i++) {
                    if (info.getComponentName().getPackageName().equals(appsBeans1.get(i).packageName)) {
                        entry.icon = LauncherIcons.createBadgedIconBitmapRed(
                                getFullResIcon(info), info.getUser(), mContext,
                                infoProvider.get().getApplicationInfo().targetSdkVersion);
                        Log.i("lee", "执行完list1禁用修改" + info.getComponentName().getPackageName());
                    }
                }
                for (int i = 0; i < appsBeans2.size(); i++) {
                    if (info.getComponentName().getPackageName().equals(appsBeans2.get(i).packageName)) {
                        entry.icon = LauncherIcons.createBadgedIconBitmapGreen(
                                getFullResIcon(info), info.getUser(), mContext,
                                infoProvider.get().getApplicationInfo().targetSdkVersion);
                        Log.i("lee", "执行完list2限时修改" + info.getComponentName().getPackageName());
                    }
                }
                for (int i = 0; i < appsBeans3.size(); i++) {
                    if (info.getComponentName().getPackageName().equals(appsBeans3.get(i).packageName)) {
                        entry.icon = LauncherIcons.createBadgedIconBitmap(
                                getFullResIcon(info), info.getUser(), mContext,
                                infoProvider.get().getApplicationInfo().targetSdkVersion);
                        Log.i("lee", "执行完list3*修改" + info.getComponentName().getPackageName());
                    }
                }
                Launcher.changeAppsBeans.clear();
            }
        }
        return entry;
    }

主要逻辑在此处通过包名判断修改数据列表

LauncherActivityInfo info = infoProvider.get();
                List<DevGetallAppStatus.AppsBean> appsBeans1 = new ArrayList<>();
                List<DevGetallAppStatus.AppsBean> appsBeans2 = new ArrayList<>();
                List<DevGetallAppStatus.AppsBean> appsBeans3 = new ArrayList<>();
                for (int i = 0; i < Launcher.changeAppsBeans.size(); i++) {
                    if (Launcher.changeAppsBeans.get(i).enable.equals("0")) {
                        appsBeans1.add(Launcher.changeAppsBeans.get(i));
                    } else if (Launcher.changeAppsBeans.get(i).enable.equals("2")) {
                        appsBeans2.add(Launcher.changeAppsBeans.get(i));
                    } else {
                        appsBeans3.add(Launcher.changeAppsBeans.get(i));
                    }
                }

然后复制两个LauncherIcons类中的createBadgedIconBitmap()方法 如果有3中蒙层就复制3个(比较笨和懒的方法)

就会调用以下方法

public static Bitmap createBadgedIconBitmap(
            Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {

        IconNormalizer normalizer;
        float scale = 1f;
        if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
            normalizer = IconNormalizer.getInstance(context);
            if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
                boolean[] outShape = new boolean[1];
                AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
                        context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
                dr.setBounds(0, 0, 1, 1);
                scale = normalizer.getScale(icon, null, dr.getIconMask(), outShape);
                if (FeatureFlags.LEGACY_ICON_TREATMENT &&
                        !outShape[0]) {
                    Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
                    if (wrappedIcon != icon) {
                        icon = wrappedIcon;
                        scale = normalizer.getScale(icon, null, null, null);
                    }
                }
            } else {
                scale = normalizer.getScale(icon, null, null, null);
            }
        }
        Bitmap bitmap = createIconBitmap(icon, context, scale);
        if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
                icon instanceof AdaptiveIconDrawable) {
            bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
        }
        return badgeIconForUser(bitmap, user, context);
    }

最终调用一开始说的方法:

public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
        synchronized (sCanvas) {
            final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
            int width = iconBitmapSize;
            int height = iconBitmapSize;

            if (icon instanceof PaintDrawable) {
                PaintDrawable painter = (PaintDrawable) icon;
                painter.setIntrinsicWidth(width);
                painter.setIntrinsicHeight(height);
            } else if (icon instanceof BitmapDrawable) {
                // Ensure the bitmap has a density.
                BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
                Bitmap bitmap = bitmapDrawable.getBitmap();
                if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
                    bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
                }
            }

            int sourceWidth = icon.getIntrinsicWidth();
            int sourceHeight = icon.getIntrinsicHeight();
            if (sourceWidth > 0 && sourceHeight > 0) {
                // Scale the icon proportionally to the icon dimensions
                final float ratio = (float) sourceWidth / sourceHeight;
                if (sourceWidth > sourceHeight) {
                    height = (int) (width / ratio);
                } else if (sourceHeight > sourceWidth) {
                    width = (int) (height * ratio);
                }
            }
            // no intrinsic size --> use default size
            int textureWidth = iconBitmapSize;
            int textureHeight = iconBitmapSize;

            Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
                    Bitmap.Config.ARGB_8888);
            final Canvas canvas = sCanvas;
            canvas.setBitmap(bitmap);

            final int left = (textureWidth - width) / 2;
            final int top = (textureHeight - height) / 2;

            sOldBounds.set(icon.getBounds());
            if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
                int offset = Math.max((int) (ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
                        Math.min(left, top));
                int size = Math.max(width, height);
                icon.setBounds(offset, offset, size, size);
            } else {
                icon.setBounds(left, top, left + width, top + height);
            }
            canvas.save(Canvas.MATRIX_SAVE_FLAG);
            canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
            icon.draw(canvas);
            canvas.restore();
            icon.setBounds(sOldBounds);
            canvas.setBitmap(null);
            return bitmap;
        }
    }

这样就完成了对Launcher3图标的动态添加蒙层。

在提一句影藏某些包名的应用图标就只需要在:
IconCache的cacheLocked()方法中通过包名过滤是否需要展示某些应用图标。

上一篇:0909


下一篇:qtawesome的Icon Browser使用方法