【高密级】Alive Status(灵动岛)技术接入文档

📌 需求概述

  • 灵动的智能的车辆状态变化,以 Alive Status 的方式告诉用户
  • 后台运行的任务,以最轻量的方式显示最核心的状态,并提供不离开用户当前任务的快捷操作

产品文档:需求方案PRD-大屏-状态栏-Alive Status

基础配置

Alive Status 基于通知接口实现,先参考 通知的介绍及使用 配置必需的权限,并了解一下通知的常规使用。

引入 HyperX

按照文档 HyperX Core 引入 HyperX Core 组件。

灵动岛类型

按照文档 通知扩展 API 设置灵动岛类型和显示方式。

显示内容

4.1 迷你岛(状态栏)

  • 图标:使用 Notification.Builder#setLargeIcon 传入。
    • eg: setLargeIcon(Icon.createWithResource(context, R.drawable.xxx))
  • 边框颜色:使用 Notification.Builder#setColor 设置,不传入则按默认背景显示,色值不要带透明度
  • 进度条样式:
    • 进度使用 Notification.Builder#setProgress
    • 进度条颜色使用 Notification.Builder#setColor
    • 第二段进度:Notification.Builder#setSecondProgress
    • 第二段进度颜色:Notification.Builder#setSecondColor
  • 点击行为(按顺序处理):
    • 变成通知:只要通知渠道的 importance 设置成 IMPORTANCE_HIGH,点击会变成通知。
    • 打开应用界面:如果通知渠道的 importance 设置成 IMPORTANCE_DEFAULT,使用 Notification.Builder#setContentIntent 设置页面,则打开页面。
    • 无响应:以上条件皆不满足,则无响应。

4.2 小岛(状态栏)

  • 左侧图标:使用 Notification.Builder#setLargeIcon 传入。
    • eg: setLargeIcon(Icon.createWithResource(context, R.drawable.xxx))
  • 文本:使用 Notification.Builder#setSubText 传入。
  • 边框/进度条颜色:使用 Notification.Builder#setColor 设置,不传入则按默认背景显示,色值不要带透明度
  • 自定义布局:使用 Notification.Builder#setCustomContentView
  • 进度条样式:
    • 进度使用 Notification.Builder#setProgress
    • 进度条颜色使用 Notification.Builder#setColor
    • 第二段进度:Notification.Builder#setSecondProgress
    • 第二段进度颜色:Notification.Builder#setSecondColor
  • 计时器样式:
    • 计时器类型:Notification.Builder#setTimerType
      • 正计时:HyperNotificationCompat.ALIVE_TIMER_TYPE_COUNT_UP
      • 正计时暂停:HyperNotificationCompat.ALIVE_TIMER_TYPE_COUNT_UP_PAUSE
      • 倒计时:HyperNotificationCompat.ALIVE_TIMER_TYPE_COUNT_DOWN
      • 倒计时暂停:HyperNotificationCompat.ALIVE_TIMER_TYPE_COUNT_DOWN_PAUSE
    • 计时起点:Notification.Builder#setTimerWhen
    • 计时进度(倒计时使用):Notification.Builder#setTimerTotal
  • 点击行为(按顺序处理):
    • 变成通知:只要通知渠道的 importance 设置成 IMPORTANCE_HIGH,点击会变成通知。
    • 打开应用界面:如果通知渠道的 importance 设置成 IMPORTANCE_DEFAULT,使用 Notification.Builder#setContentIntent 设置页面,则打开页面。
    • 无响应:以上条件皆不满足,则无响应。
  • 如果要只显示小岛,通知渠道的 importance 设置成 IMPORTANCE_DEFAULT 即可。

4.3 中岛(通知)

  • 自定义布局:使用 Notification.Builder#setCustomHeadsUpContentView
    • 不提供标准模板,不要使用 setContentTitle、setContentText 等接口去设置。
    • 卡片最大宽度 632dp,可以使用 match_parent 填满宽度,也可以使用 wrap_content 根据内容自适应宽度,也可以自定义宽度,但不能超过卡片最大宽度。高度按内容自适应。
  • 边框颜色:使用 Notification.Builder#setColor 设置,不传入则按默认背景显示,色值不要带透明度。
  • 进度条样式:
    • 进度使用 Notification.Builder#setProgress
    • 进度条颜色使用 Notification.Builder#setColor
    • 第二段进度:Notification.Builder#setSecondProgress
    • 第二段进度颜色:Notification.Builder#setSecondColor
    • 光效动画是否顺时针旋转,默认 true:Notification.Builder#setClockwise
  • 点击行为:
    • 打开应用界面:使用 Notification.Builder#setContentIntent 设置。
    • 无响应:以上未设置则无响应。

4.4 巨岛(PHUD)

  • 方式一:自定义布局:使用 Notification.Builder#setCustomBigContentView。此方法对实现复杂 UI 和动效有约束。

  • 方式二:使用通知扩展 API 中的 1.11 状态变化回调,业务监听灵动岛的状态变化,调用 PHUDNotificationManager 显示和消失。接入方式:

    1. 如果是 Bp 编译需要额外引入 SDK:

      static_libs: [
          "phud-card-dynamic",
          "phud-scene-kit",
          "phud-card-common",
      ]
    2. 添加权限(android.permission.INTERACT_ACROSS_USERS 是 privileged permission):

      <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
          tools:ignore="ProtectedPermissions" />
      <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"
          tools:ignore="ProtectedPermissions" />
    3. 示例代码:

      // 获取单例
      PHUDNotificationManager phudNotificationManager
              = PHUDNotificationManager.getInstance(context);
      // 显示巨岛
      phudNotificationManager.show(view);
      // 移除巨岛
      phudNotificationManager.remove(view);

必传内容(研发必看!!!)

为了方便产品埋点分析,要求业务方必须传入如下字段。

5.1 岛名字

业务需要按照 通知扩展 APIsetOneTrack() 传递埋点字段,字段内容按文档 Alive Status 业务-岛-setOneTrack 查询字典 规范使用,如果没有的可以联系 牛航 确认。

5.2 时间戳

为了统计每个功能灵动岛存在的时长,要求业务通过 android.app.Notification.Builder#setWhen 接口传入时间戳。

该字段要求传入第一次发送灵动岛的当前时间,后续更新灵动岛请务必仍然使用这个第一次发送的时间。单位 ms。

提示音

首次出现在通知/状态栏及强调时可发出音效,默认为通知提示音,如需定制提示音或静音,通过通知渠道 NotificationChannel#setSound 设置。

其它配置

  • Notification.Builder#setOnlyAlertOnce
    • true:当灵动岛进入状态栏时,更新通知不会变成浮动通知。
    • false(默认):当灵动岛进入状态栏时,更新通知会变成浮动通知。
  • Notification.Builder#setOngoing(无论什么类型的中岛都不能左右滑动消失了)
    • true持续活动类中岛如需要左右滑动不消失,需要配置为 true。
    • false默认值,左右滑动可消失。
  • Notification.Builder#setFullScreenIntent(PendingIntent intent, boolean highPriority)
    • intent:通知默认显示 8 秒消失,如果需要通知显示后不自动消失,可以将通知点击事件的 PendingIntent 传入这个字段,如果没有点击跳转行为,可以传入一个空实现,但传入 null 一样会自动消失。另外点击卡片后也会消失。
    • highPriority:true/false 皆可,无影响。
    • 需要加权限:<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
  • Notification.Builder#setDeleteIntent:用户取消通知时会收到回调。

动效

灵动岛在通知和状态栏之间切换动画、卡片内部标准模板的动画由 SystemUI 内部实现。

业务如需自定义动画效果,通过 RemoteViews 自定义布局,并采用如下可实现的方案,这里提供博客供参考:

调试与测试

9.1 前提

车机上的通知需要满足以下条件才能显示,请确保都满足:

  • 电源模式需处于 ACCRUN,可以通过 mock 修改:
adb root
adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle --mock_from_car 0x61407207 -i 2

最后一个数字是 2 或 3 皆可。

也给大家提供一个脚本,方便台架上直接使用:

📎 [附件: mock 电源模式 shell 脚本](token: GAGhboQywodcn0xhcRRc0mfNnXc,下载 403,未保存到本地)

  • 确保在”设置 - 系统 - 应用管理 - 右上角三个点 - 权限管理 - 通知”里你的应用的通知开关是打开的状态。

另外请使用 user-debuguser-root 包,并保证日志级别至少是 debug,方便排查问题时输出有效日志。

如果还未出现按 通知的介绍及使用 10.1 节排查一下。

9.2 日志

通知通用的日志请参考 通知的介绍及使用 第 9 节。

灵动岛相关日志:

TAG: MiCarNotificationListener

# 进入状态栏
postStatusBarNotification: [userId|pkg|id|tag|uid], isSeen=[true/false]
# 从状态栏移除
removeStatusBarNotification: [userId|pkg|id|tag|uid]
# PHUD 显示
postPHUDNotification: [userId|pkg|id|tag|uid]
# PHUD 移除
removePHUDNotification: [userId|pkg|id|tag|uid]

TAG:CarHeadsUpNotificationManager

# 状态栏点击显示通知
Force show as HUN
# 仅在状态栏显示
Unable to show as HUN: only show in status bar

模板代码

val chargeRemoteView =
    RemoteViews(packageName, R.layout.remote_view_alive_status_charge)
val channelId = "CHANNEL_HIGH"
val category = HyperNotificationCompat.CATEGORY_CAR_ALIVE_ACTIVE_0
//CATEGORY_CAR_ALIVE_ACTIVE_0 需要参考 PRD:https://xiaomi.f.mioffice.cn/docx/doxk4DW5CQL62kfHL0et3nNfbXh
// 内接入"接入业务"的优先级来确定,引用参考"灵动岛"类型:https://xiaomi.f.mioffice.cn/wiki/IHlPwIj3HiGeEek2UQikTAyq4Tb
chargeRemoteView.setProgressBar(R.id.remote_progress,100, sbCharge.progress, false);
val builder = Notification.Builder(requireContext(), channelId)
    .setCategory(category)
    // 这行必须要有,否则通知无法传递到systemui
    .setSmallIcon(R.drawable.ic_charge)
    // 设置小岛图标
    .setLargeIcon(Icon.createWithResource(requireContext(), R.drawable.ic_charge))
    // 设置小岛文字和边框颜色
    .setSubText("1h 23min").setColor(Color.parseColor("#11A658"))
    // 设置中岛view
    .setCustomHeadsUpContentView(chargeRemoteView)
    .setOnlyAlertOnce(true)
    .setProgress(sbCharge.max, sbCharge.progress, false)
    .setAliveShowType(HyperNotificationCompat.ALIVE_SHOW_TYPE_DEFAULT)
NotificationManagerCompat.from(requireContext())
    // tag参考5.1配置
    .notify("tag", chargeNotificationId, builder.build())

超高负载监听接入(必看!!!)

当负载处于 SYS_LOAD_LEVEL_CRITICAL 时,灵动岛各业务线需要进行动效降级,暂停所有动画

接入参考:XCD MiCarPerfservice系统负载状态通知设计,任何问题咨询 陆晓鸽

详细可查看:灵动岛-系统超高(Critical)负载降级方案

高频更新场景接入(研发必看!!!)

对于高频更新灵动岛通知的场景(例如 1 秒发多次),因为原生通知服务有发送频率限制,可能会丢弃部分数据,所以不能保证 UI 更新的实时性。

如果需要保证 UI 更新的准确性,需要另外接入实时数据更新的 API,参考 灵动岛IAliveDataService方案设计及接入说明


📥 抓取自飞书 wiki 空间 7541192071952990209,obj_token: DD7LdX3Quog6FuxulhWcYDbQnhg