Android13 Launcher3 桌面图标添加 HUAWEI HiCar 和 ICCOA 角标

在 Android13 Launcher3 中桌面图标添加 HiCar 和 ICCOA 角标需要对 Launcher3 源码进行定制开发。先展示效果:

以下是实现这一功能的步骤和代码示例:

一、准备工作

  1. 获取 Launcher3 源码 从 AOSP 获取 Android13 Launcher3 源码。
  2. 理解 Launcher3 架构 重点关注 BitmapInfoBaseIconFactoryFastBitmapDrawableIconCache 类。

二、创建角标资源

  1. res/drawable 目录下添加 HiCar 和 ICCOA 角标图片(如 ic_hicar_badge.pngic_iccoa_badge.png)。

三、修改 BitmapInfo 类添加角标标识

  • BitmapInfo.java 中添加角标状态字段
  • BitmapInfo.java 类中修改 applyFlags 方法,向 icon 中添加 HiCar 或 ICCOA 角标资源
java 复制代码
public class BitmapInfo {
    static final int FLAG_WORK = 1 << 0;
    static final int FLAG_INSTANT = 1 << 1;
    // Add:HiCar & ICCOA badge flags
    static final int FLAG_ICCOA = 1 << 2;
    static final int FLAG_HICAR = 1 << 3;

    @IntDef(flag = true, value = {
        FLAG_WORK,
        FLAG_INSTANT,
        // Add: HiCar & ICCOA badge flags
        FLAG_ICCOA,
        FLAG_HICAR,
    })
        
    ...
    
    // Update applyFlags function
    protected void applyFlags(Context context, FastBitmapDrawable drawable,
                              @DrawableCreationFlags int creationFlags) {
    drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f);
    if ((creationFlags & FLAG_NO_BADGE) == 0) {
        if (badgeInfo != null) {
            drawable.setBadge(badgeInfo.newIcon(context, creationFlags));
        } else if ((flags & FLAG_INSTANT) != 0) {
            drawable.setBadge(AppCompatResources.getDrawable(context, R.drawable.ic_instant_app_badge));
        } else if ((flags & FLAG_WORK) != 0) {
            drawable.setBadge(AppCompatResources.getDrawable(context, R.drawable.ic_work_app_badge));
        }

        // Add: HiCar & ICCOA badge resources
        if ((flags & FLAG_ICCOA) != 0) {
            Log.d(TAG, "applyFlags: set the iccoa badge");
            drawable.setBadge(AppCompatResources.getDrawable(context, R.drawable.ic_iccoa_badge));
        }

        if ((flags & FLAG_HICAR) != 0) {
            Log.d(TAG, "applyFlags: set the hicar badge");
            drawable.setBadge(AppCompatResources.getDrawable(context, R.drawable.ic_hicar_badge));
        }
    }
}

四、修改 BaseIconFactory 类处理角标逻辑

  • BaseIconFactory.java 中修改常量 ICON_BADGE_SCALE,控制 badge 的大小
  • BaseIconFactory.java 内部类 IconOptions 中添加 isIccoaAppisHiCarApp 字段,暴露设置 HiCar 或 ICCOA 标识方法
  • BaseIconFactory.java 中修改方法 getBitmapFlagOp,添加 HiCar 和 ICCOA flag

中方法判断应用是否需要角标:

java 复制代码
public class BaseIconFactory implements AutoCloseable {

  // Update the badge scale size to resize the badge
  private static final float ICON_BADGE_SCALE = 0.444f;
  ...

  /**
   * Returns the correct badge size given an icon size
   */
  public static int getBadgeSizeForIconSize(int iconSize) {
    return (int) (ICON_BADGE_SCALE * iconSize);
  }

  // Update getBitmapFlagOp function
  public FlagOp getBitmapFlagOp(@Nullable IconOptions options) {
    FlagOp op = FlagOp.NO_OP;
    if (options != null) {
      if (options.mIsInstantApp) {
          op = op.addFlag(FLAG_INSTANT);
      }

      if (options.mIsHiCarApp) {
          op = op.addFlag(FLAG_HICAR);
      }

      if (options.mIsIccoaApp) {
          op = op.addFlag(FLAG_ICCOA);
      }
    }
  }


  public static class IconOptions {
    boolean mShrinkNonAdaptiveIcons = true;
    boolean mIsInstantApp;
    UserHandle mUserHandle;

    // Add is hicar & is iccoa app vars
    boolean mIsIccoaApp;
    boolean mIsHiCarApp;

    ...
    public IconOptions setIccoaApp(boolean iccoaApp) {
        mIsIccoaApp = iccoaApp;
        return this;
    }

    public IconOptions setHiCarApp(boolean hicarApp) {
        mIsHiCarApp = hicarApp;
        return this;
    }
  }
}

五、FastBitmapDrawable 绘制角标(按需修改)

FastBitmapDrawable.java 中已有角标绘制逻辑,可按需修改:

java 复制代码
public class FastBitmapDrawable extends Drawable implements Drawable.Callback {

  // Set badge, we've set in the BitmapInfo class before
  public void setBadge(Drawable badge) {
    if (mBadge != null) {
        mBadge.setCallback(null);
    }
    mBadge = badge;
    if (mBadge != null) {
        mBadge.setCallback(this);
    }
    updateBadgeBounds(getBounds());
    updateFilter();
  }

  private void updateBadgeBounds(Rect bounds) {
    if (mBadge != null) {
      setBadgeBounds(mBadge, bounds);
    }
  }

  /**
   * Sets the bounds for the badge drawable based on the main icon bounds
   */
  public static void setBadgeBounds(Drawable badge, Rect iconBounds) {
    // The size controlled by the ICON_BADGE_SCALE constant field in the 
    // BaseIconFactory class
    int size = getBadgeSizeForIconSize(iconBounds.width());
    badge.setBounds(iconBounds.right - size, iconBounds.bottom - size,
          iconBounds.right, iconBounds.bottom);
  }

  @Override
  public final void draw(Canvas canvas) {
    if (mScale != 1f) {
      int count = canvas.save();
      Rect bounds = getBounds();
      canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
      drawInternal(canvas, bounds);
      // Draw the badge logic
      if (mBadge != null) {
          mBadge.draw(canvas);
      }
      canvas.restoreToCount(count);
    } else {
      drawInternal(canvas, getBounds());
      // Draw the badge logic
      if (mBadge != null) {
          mBadge.draw(canvas);
      }
    }
}

六、使用方式

在对桌面图标替换 Icon 资源时,我们可以同时添加 IconOptions 属性:

kotlin 复制代码
fun swapIcon(context: Context, info: ItemInfoWithIcon, source: Bitmap) {
  val launcherIcons = LauncherIcons.obtain(context)
  val targetDrawable = source.toDrawable(context.resources)
  // Set icon option as iccoa app, show the iccoa badge
  val iconOptions = IconOptions().setIccoaApp(true) // or .setHiCarApp(true)
  val bitmapInfo = launcherIcons.createBadgedIconBitmap(targetDrawable, iconOptions)
  bitmapInfo?.let {
    info.bitmap = bitmapInfo
  }
}

七、修改完的样式

相关推荐
独行soc18 分钟前
2026年渗透测试面试题总结-20(题目+回答)
android·网络·安全·web安全·渗透测试·安全狮
常利兵31 分钟前
2026年,Android开发已死?不,它正迎来黄金时代!
android
Risehuxyc1 小时前
备份三个PHP程序
android·开发语言·php
Doro再努力10 小时前
【Linux操作系统10】Makefile深度解析:从依赖推导到有效编译
android·linux·运维·服务器·编辑器·vim
Daniel李华11 小时前
echarts使用案例
android·javascript·echarts
做人不要太理性12 小时前
CANN Runtime 运行时组件深度解析:任务调度机制、存储管理策略与维测体系构建逻辑
android·运维·魔珐星云
我命由我1234512 小时前
Android 广播 - 静态注册与动态注册对广播接收器实例创建的影响
android·java·开发语言·java-ee·android studio·android-studio·android runtime
朗迹 - 张伟13 小时前
Tauri2 导出 Android 详细教程
android
lpruoyu13 小时前
【Android第一行代码学习笔记】Android架构_四大组件_权限_持久化_通知_异步_服务
android·笔记·学习
独自破碎E14 小时前
【BISHI15】小红的夹吃棋
android·java·开发语言