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
  }
}

七、修改完的样式

相关推荐
_祝你今天愉快33 分钟前
Java-JVM探析
android·java·jvm
飞天卡兹克1 小时前
forceStop流程会把对应进程的pendingIntent给cancel掉
android
Monkey-旭9 小时前
Android Bitmap 完全指南:从基础到高级优化
android·java·人工智能·计算机视觉·kotlin·位图·bitmap
Mike_Wuzy14 小时前
【Android】发展历程
android
开酒不喝车14 小时前
安卓Gradle总结
android
阿华的代码王国15 小时前
【Android】PopupWindow实现长按菜单
android·xml·java·前端·后端
稻草人不怕疼16 小时前
Android 15 全屏模式适配:A15TopView 自定义组件分享
android
静默的小猫16 小时前
LiveDataBus消息事件总线之二-(不含反射和hook)
android
~央千澈~17 小时前
05百融云策略引擎项目交付-laravel实战完整交付定义常量分文件配置-独立建立lib类处理-成功导出pdf-优雅草卓伊凡
android·laravel·软件开发·金融策略
_一条咸鱼_17 小时前
Android Runtime冷启动与热启动差异源码级分析(99)
android·面试·android jetpack