RearProjectionChildLockController 拆解
源码:
settingsPage/micarDisplaySettings/src/main/java/com/android/car/settings/miauto/display/RearProjectionChildLockController.kt父类:base/settingsVehicleLib/.../vehicle/BaseVehicleNewSwitchPrefController.kt
一、定位
后排投影的童锁开关控制器,与模式 Tab 并列挂在「后排投影」分组下。它驱动一个 MiCarNewSwitchPreference(开关),开启后锁定后排乘客对投影幕布的物理操控,防止儿童误操作。
二、继承链(与模式 Tab 对比)
classDiagram class PreferenceController~V~ { <<AOSP 顶层>> } class CarPropertyMgrPreferenceController~V~ { <<信号管理>> +getPropertyIdSet() +obtainSetPropertyId() +onHandlePropertyChange() } class BaseVehicleImageTabPreferenceController { <<Tab 模板>> } class BaseVehicleNewSwitchPrefController { <<开关模板>> +handlePreferenceChanged() #isPropertyOpen() #getPropertyVal() #isValidPropVal() #needSecondConfirm() #needIntermediateState() } class RearProjectionModeTabController { 模式 Tab } class RearProjectionChildLockController { 童锁开关 } class RearTvChildLockSwitchController { 后排电视童锁(对照) } PreferenceController~V~ <|-- CarPropertyMgrPreferenceController~V~ CarPropertyMgrPreferenceController~V~ <|-- BaseVehicleImageTabPreferenceController CarPropertyMgrPreferenceController~V~ <|-- BaseVehicleNewSwitchPrefController BaseVehicleImageTabPreferenceController <|-- RearProjectionModeTabController BaseVehicleNewSwitchPrefController <|-- RearProjectionChildLockController BaseVehicleNewSwitchPrefController <|-- RearTvChildLockSwitchController
关键:童锁和模式 Tab 是 CarPropertyMgrPreferenceController 的两条平行分支——一个走 Tab 模板,一个走 Switch 模板,共享底层信号管理基础设施。
三、UI 类型
驱动 MiCarNewSwitchPreference(小米自研开关组件)。用户看到的是一个开关(非 Tab、非进入二级页)。
XML(miauto_display_atmosphere_settings_fragment.xml:258-262):
<com.android.car.settings.miauto.preferences.MiCarNewSwitchPreference
android:key="@string/pk_settings_display_rear_projection_child_lock"
android:title="@string/settings_display_rear_projection_child_lock"
android:summary="@string/settings_display_rear_projection_child_lock_summary"
settings:controller="...RearProjectionChildLockController" />四、信号:收发同信号(与模式 Tab 不同)
| 维度 | 童锁 | 模式 Tab(对照) |
|---|---|---|
| 上行监听 | PROJECTION_CHILD_LOCK (0x634) | PROJECTION_SCREEN_STATUS (0x62b) |
| 下行写入 | 同 0x634(默认) | SCREEN_CONTROL_COMMAND (0x637) |
| 模式 | 收发共用 | 收发分离 |
童锁未重写 obtainSetPropertyId(),走基类默认实现(返回 getPropertyIdSet() 第一项,即 0x634 自身)。getPreferenceType() 返回 MiCarNewSwitchPreference(BaseVehicleNewSwitchPrefController.kt:37-39)。
五、值映射(注意:命名反向于直觉)
flowchart LR L["信号 LOCKED (0x1)"] -->|isPropertyOpen=true| ON["开关 ON 童锁启用"] U["信号 UNLOCKED (0x2)"] -->|isPropertyOpen=false| OFF["开关 OFF"] I["信号 INVALID (0x0)"] -->|isValidPropVal=false| DIS["置灰,不刷新勾选"]
RearProjectionChildLockController.kt:28-40:
override fun isPropertyOpen(propVal: Int) =
propVal in arrayOf(Peripheral.ProjectionChildLockStatus.LOCKED)
override fun getPropertyVal(isChecked: Boolean) =
if (isChecked) Peripheral.ProjectionChildLockStatus.LOCKED
else Peripheral.ProjectionChildLockStatus.UNLOCKED⚠️
LOCKED(已锁定)= 开关 ON。即开关打开 = 童锁启用。命名上LOCKED对应”开”,容易混淆,但符合业务语义(锁定状态 = 功能生效)。
六、业务语义:车辆物理锁,非 UI 锁
文案证据(strings.xml):
- 标题:后排投影童锁
- 副标题:开启后,后排投影控制将被锁定(英文:Lock rear projector controls)
开启后,锁定的是后排乘客(含儿童)对幕布/投影仪的物理操控(如后排物理按键折叠/展开幕布),防止儿童误操作。设置 App 内的开关本身仍可由驾驶员操作——这是车辆侧行为锁,不是 UI 层禁用。
Settings 侧只负责下发
LOCKED/UNLOCKED,具体锁定后排哪些物理部件由车控侧实现,Settings 不感知。
七、与 RearProjectionModeTabController 的关系
结论:完全无联动,相互独立。
| 证据 | 说明 |
|---|---|
XML 无 settings:dependKey | 两者平级挂在 category 下,无依赖关系 |
| 信号无交叉 | ModeTab 不监听 PROJECTION_CHILD_LOCK;童锁不监听模式 Tab 的 5 个依赖信号 |
| 代码 grep 无命中 | ModeTab 及其父类中无 CHILD_LOCK 引用 |
业务理解:童锁锁的是后排物理操控;模式 Tab 是驾驶员通过中控屏发的 SCREEN_CONTROL_COMMAND,走不同通路,故不受童锁影响。这与典型 UI dependKey 联动不同。
八、可用性 / 异常态
童锁只有一道闸:isValidPropVal()(RearProjectionChildLockController.kt:42-49):
override fun isValidPropVal(vehicleProperty: CarPropertyValue<*>?) =
vehicleProperty?.let {
val s = VehiclePropertyUtil.getIntPropertyValue(it)
s == ProjectionChildLockStatus.LOCKED || s == ProjectionChildLockStatus.UNLOCKED
} ?: false- 信号值
INVALID(0x0)或 null →isValidPropVal=false→ 两条效果(父类BaseVehicleNewSwitchPrefController.kt:58-59,86-92):shouldUpdateUIByPropVal=false:不刷新勾选状态(保持上一次)isInnerLogicAvailable=false:配合isPropertyAvailable共同决定preference.isEnabled(置灰)
未启用的能力(父类默认 false,本类未重写):
needIntermediateState()→ 无 loading 中间态needSecondConfirm()→ 无二次确认弹窗isPreventSwitchSlide()→ 不拦截滑动
九、信号流时序
sequenceDiagram autonumber participant U as 驾驶员 participant UI as MiCarNewSwitchPreference participant C as ChildLockController participant B as BaseSwitchController participant M as SettingsCarPropertyManager participant V as 车控(0x634) V->>M: 初始 LOCKED(0x1) M->>C: onPropertyChanged C->>UI: setChecked(isPropertyOpen=true) U->>UI: 点击关 UI->>B: handlePreferenceChanged(false) B->>M: setVehicleProperty(0x634, UNLOCKED, areaId) M->>V: 写 PROJECTION_CHILD_LOCK_STATUS V-->>M: 回灌 UNLOCKED(0x2) M->>C: onHandlePropertyChange C->>UI: setChecked(false) Note over V: 异常: 上报 INVALID(0x0) V-->>M: INVALID C->>UI: isEnabled=false(置灰), 不刷新勾选
十、对照:RearTvChildLockSwitchController
同目录的「后排电视童锁」结构几乎一致,差异仅在:
- 值映射用
CommonParams.Lock.LOCK/UNLOCK而非ProjectionChildLockStatus。 - 重写了
getAreaId() = SEAT_REAR_ROW(后排区域);而投影童锁用默认GLOBAL。
同一套
BaseVehicleNewSwitchPrefController模板,通过重写几个钩子即可适配不同童锁场景。