03 · 氛围灯系统专题

氛围灯是 Light 模块最复杂、设计最精巧的子系统。本章是 Light 知识库的核心。

一、三层心智模型

氛围灯采用三层级联架构(总开关 → 效果模式 → 颜色/主题/动态参数):

Layer 0 总开关   MOOD_LIGHT_TRIGGER          [OFF / ON / AUTO]
                  └─ dependKey 级联所有子项显隐 + 每个子 Controller 观察它判可用性

Layer 1 效果模式  MOOD_LIGHT_MODE_SETTINGS    [常亮 STATIC_LIGHTING / 呼吸 BREATH / 音乐 MUSIC]
                  └─ ModeData ViewModel 决定 Layer 2/3 哪组显示

Layer 2 颜色配置
  ┌─ 非 26A(单层)──────────────────────────────────┐
  │  DisplayAtmosphereTheme (6 圆形: THEME1-5 + CUSTOM) │
  │  + ColorPickController (单层取色板)                │
  │     CUSTOM 选中时显示取色板                        │
  └──────────────────────────────────────────────────┘
  ┌─ 26A(双层)─────────────────────────────────────┐
  │  AtmosphereColorMode (单色/双色/自定义)            │
  │     ├─ SingleColorTab (THEME1-5)                  │
  │     ├─ DoubleColorTab (双色 THEME1-5)             │
  │     └─ DoubleColorPickController (双层取色板+同步) │
  └──────────────────────────────────────────────────┘

Layer 3 动态参数(跟随 Layer 1 模式)
  ├─ 常亮 → AtmosphereBrightness (MOOD_LIGHT_BRIGHTNESS 1-100)
  ├─ 呼吸 → LightBreathState (慢/快)
  └─ 音乐 → MusicRhythm (保持当前色/跟随封面)

独立项(跟随总开关 dependKey,不随模式切换)
  ├─ WelcomeLightSwitch (上下车迎宾)
  ├─ AmbientLightDimmingDown (夜间减弱)
  └─ FootLight (照脚灯,无 dependKey 独立)

关键解耦AtmosphereData ViewModel 用 6 个 LiveData(Theme/TabChange/ModeData/MusicRhythmType/ColorModeData/ThemeName)在 Controller 间传递状态,避免直接互调。

二、MOOD_LIGHT_TRIGGER 为何引用 27 次

总开关身兼三职

  1. 父信号驱动子项显隐AtmosphereLightTabLayoutPrefController.getSubDependPropertyId()=TRIGGER,trigger=ON/AUTO 时 updatePreferencesVisibleWhenStatusChange(true) 级联所有子项。
  2. 每个子 Controller 观察它判可用性:几乎所有子 Controller 的 getObservedPropertyIdSet() 都加 TRIGGER,isInnerLogicAvailable()... && isPropAvailable(TRIGGER)。trigger 不可用 → 整条链路置灰。
  3. Helper 静态可见性判断LightPreferenceVisibleStatusHelper.isAtmosphereModePreferenceVisible() 读 TRIGGER 缓存值,OFF 则隐藏整组(resume 重建时的”无信号快速判定”)。

三、两套并行 UI(非 26A vs 26A)

维度非 26A(单层)26A(双层,Modena-26A)
判定!isDualLayerAtmosphereLightEnableisDualLayerAtmosphereLightEnable(多区色彩=ZONE_2_UPPER_LOWER_TUNING)
颜色模式入口无(直接主题)AtmosphereColorModeTab(单/双/自定义)
主题DisplayAtmosphereTheme(6 圆形:THEME1-5+CUSTOM)SingleColorTab(5 圆形)/ DoubleColorTab(5 圆形双色)
取色板ColorPickController(单层 1 个色盘)DoubleColorPickController(上下 2 个色盘 + 同步按钮)
色信号ATMOSPHERE_COLORATMOSPHERE_COLOR(上) + ATMOSPHERE_COLOR_LOWER(下)
主题信号MOOD_LIGHT_THEME_SETTINGSMOOD_LIGHT_THEME_SETTINGS(单) + AMBIENT_LIGHT_BICOLOUR_THEME_STATUS(双)
XML 容器pk_settings_display_atmosphere_light_defaultpk_settings_display_atmosphere_light_26A

两组在 LightsSettingsFragment.getPreferenceKeyResIdsToRemove() 互斥裁剪。

四、256 色色盘机制

色盘是自定义 View ColorPickView(base/settingsBaseUi),核心机制:

  1. 256 色硬编码色表ColorTables.sColorList):ColorBean RGB 数组,从纯白渐变到黑再循环色相。
  2. 相似度算法:用户触摸点取像素 RGB → ColorTableUtils.findColor 在 256 色表里找最接近的索引。
  3. 索引 ↔ 信号值:信号值 = 索引 + 1(范围 1-256)。下发 setVehicleProperty(ATMOSPHERE_COLOR, colorIndex + 1)
  4. 索引 ↔ 屏幕坐标ColorTables.sColorPointMap 维护 ARGB→dp 坐标,getColorPositionByIndex 按 dp→px 放大。
  5. 主题预设 ↔ 索引sThemeColorIndexMap 把 THEME1-5 映射到 R.integer.atmosphere_theme_*_color_index(RRO 可覆盖);双色用 sDoubleThemeColorIndexMap(Pair<upper, lower>)。

五、双拼色同步(精巧设计)

ColorPickManager(object 单例)是双拼色同步中枢,让上下两层取色板既能独立操作,又能联动。

三件套机制

  1. 按下标路由 propIdSingleColorPickController 不知道自己代表上层还是下层,所有差异(propId、profile key、View)由 ColorPickManager 注入。handleColorChange/handleActionUp 按下标路由到正确 propId。
  2. 位掩码屏蔽上行ignorePropFlag 按位记录每个板是否屏蔽上行信号(避免反馈环)。ignorePropertyChange() 满足「同步且有任一板屏蔽 / 未同步且本板屏蔽 / 信号值=本板当前值」之一就忽略。
  3. 2s 延时回弹:用户拖动板 → setIgnorePropFlag(this, true) 屏蔽上行;onActionUp → 发 2s 延时任务,2s 后 setIgnorePropFlag(false) + resetPoint() 主动读信号回弹。

同步触发路径

(1) 应用内点同步按钮DoubleColorPickPreference):

点同步 → 翻转 mIsAssociate → ColorPickManager.setIsAssociate + onAssociateChanged
  → if(isAssociate) 把上层颜色灌到下层 + setIgnorePropFlag(lower, true)
  → setVehicleProperty(ATMOSPHERE_COLOR_SYNCHRONIZATION_SET, ON/OFF)

(2) ColorPickManager 同步路由(开启 isAssociate 后):

  • handleColorChange(sender) forEach 所有 controller 同步更新
  • handleActionUp(sender) 广播给所有
  • setInteractingFlag:触摸一板时另一板 setIsInteractable(false) 防误触

(3) 跨进程广播AtmosphereLightSyncReceiver):

监听 mi.car.settings.action.ATMOSPHERE_LIGHT_SYNCHRONIZE
  → 校验 colorIndexProperty == 当前 ATMOSPHERE_COLOR(防陈旧广播)
  → ColorPickManager.handleAtmosphereColorSync: 把上层 profile copy 到下层 + resetPoint

六、音乐律动真相(不监听音频)

关键事实:app 端无任何 AudioManager / MediaController / 录音逻辑(全模块 grep 证实)。

实际机制:

  • 车端硬件做音频分析,结果以两个信号上报:
    • MUSIC_RHYTHM_STATE(Light.RHYTHM_FLAG) —— 是否处于律动态(MUSICAL_RHYTHM)
    • ATMOSPHERE_COLOR_IN_RHYTHM(Light.MUSIC_ALBUM_COLOR) —— 当前专辑封面色 ARGB int
  • App 仅消费这两个信号更新右栏车模预览颜色遮罩(updateAtmosphereColor):
    • 律动态(MUSIC_RHYTHM_STATE=1 && 模式=MUSIC && 律动=跟随封面)→ 用专辑色
    • 否则 → 用主题色
  • 用户能配置的只有 MOOD_LIGHT_RHYTHM_TYPE(用自定义色 vs 跟随封面),实际律动效果完全由车端实现。

七、三主题信号的区别(易混)

信号含义范围
MOOD_LIGHT_MODE_SETTINGS效果模式(动态行为)常亮/呼吸/音乐(所有车型)
AMBIENT_LIGHT_THEME_MODE_STATUS颜色模式(颜色怎么选)单色/双色/自定义(仅 26A
MOOD_LIGHT_THEME_SETTINGS单色主题预设编号THEME1-5 + CUSTOM(所有车型)
AMBIENT_LIGHT_BICOLOUR_THEME_STATUS双色主题预设编号THEME1-5(仅 26A)

逻辑层级:效果模式 ⊃ 颜色模式 ⊃ 主题。单色主题(THEME1-5)与双色主题(也是 THEME1-5)走不同 propId,因为双色每个”主题号”对应一对(上+下)配色。

八、动态效果三件套(BaseAtmosphereModeSubItemTabPrefController)

呼吸/律动/亮度都继承(或模仿)BaseAtmosphereModeSubItemTabPrefController,子类实现 getVisibleAtmosphereMode() 返回自己该出现的模式:

Controller信号可见模式取值
AtmosphereBrightnessMOOD_LIGHT_BRIGHTNESSSTATIC_LIGHTING1-100(@PropertyRange offset=-1)
LightBreathStateMOOD_LIGHT_BREATH_STATEBREATHSLOW_BREATH / FAST_BREATH
MusicRhythmMOOD_LIGHT_RHYTHM_TYPEMUSICFOLLOW_CUSTOM / FOLLOW_ALBUM_COVER

基类提供:①集中订阅 ModeData LiveData ②统一可见性规则(总开关开 + 模式匹配)③接收父项可见性透传(IVisibleInterceptor)④默认隐藏防闪现 ⑤onHandlePropertyManagerPrepared 主动读模式校正。

异常:AtmosphereBrightness 因是 Progress 非 Tab,不能继承 BaseAtmosphereModeSubItem,只能复制相同逻辑(代码异味)。

九、右栏车模氛围灯可视化

LightsRightFragment(滚动联动)按氛围灯状态渲染:

  • setAtmosphereMaskColor(color) —— 单层遮罩上色(ColorMatrixColorFilter
  • setUpperAtmosphereMaskColor / setLowerAtmosphereMaskColor —— 双层分别管理
  • setAtmosphereAnim(propVal) —— 按 MOOD_LIGHT_BREATH_STATE 播呼吸动画(慢 2000ms/快 1000ms)
  • updateAtmosphereModeSettings(value) —— 常亮/呼吸/音乐三模式控制呼吸动画开关 + 音乐律动 Lottie 可见性

颜色映射(LightsRightFragmentController.updateAtmosphereColor)用 ColorTableUtils 把 propIndex 转 Color:

  • 双层:按 AMBIENT_LIGHT_THEME_MODE_STATUS 分 SINGLE/BICOLOR/CUSTOM,分别用主题/双色/上下层索引
  • 单层:用 MOOD_LIGHT_THEME_SETTINGS(CUSTOM 用 ATMOSPHERE_COLOR)