@ziztechnology/dial-library 0.0.15 → 0.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +382 -250
- package/dist/index.d.mts +132 -3
- package/dist/index.mjs +815 -432
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,37 +1,57 @@
|
|
|
1
1
|
# Toooony 表盘 SDK
|
|
2
2
|
|
|
3
|
-
`@ziztechnology/dial-library`
|
|
3
|
+
`@ziztechnology/dial-library` 是 Toooony 表盘的前端 SDK,主要做几件事:
|
|
4
|
+
|
|
5
|
+
1. 读取设备的基础传感器和电池、网络信息;
|
|
6
|
+
2. 把传感器数据分析成“静止、加速、转弯”等行车状态;
|
|
7
|
+
3. 根据行车状态显示 Emoji、图片、视频、Live Photo 或 TGS 动画。
|
|
8
|
+
4. 把网页播放器接入 Runtime 的车载媒体按键和状态同步能力。
|
|
9
|
+
|
|
10
|
+
SDK 是 ESM 包,自带 TypeScript 类型。
|
|
4
11
|
|
|
5
12
|
> [!NOTE]
|
|
6
|
-
> SDK 最低支持 Toooony Runtime `v1.5.22`。行车状态传感器 Bridge
|
|
13
|
+
> SDK 最低支持 Toooony Runtime `v1.5.22`。行车状态传感器 Bridge 只向审核通过的 `PACKAGED_H5` 开放。表盘应通过本 SDK 使用这些能力,不要直接调用 Runtime 注入到 `window` 上的内部方法。
|
|
7
14
|
|
|
8
15
|
## 安装
|
|
9
16
|
|
|
10
|
-
使用 npm、pnpm 或 yarn 安装:
|
|
11
|
-
|
|
12
17
|
```bash
|
|
13
18
|
npm install @ziztechnology/dial-library
|
|
14
19
|
```
|
|
15
20
|
|
|
16
|
-
|
|
21
|
+
也可以使用 pnpm 或 yarn。
|
|
22
|
+
|
|
23
|
+
## 功能模块
|
|
24
|
+
|
|
25
|
+
文档按照 `src` 的公开导出顺序划分:
|
|
26
|
+
|
|
27
|
+
| 顺序 | 模块 | 解决什么问题 |
|
|
28
|
+
| ---- | -------------- | ---------------------------------------------------- |
|
|
29
|
+
| 1 | 基础传感器 | 一次读取陀螺仪、加速度、电池、温度、Wi-Fi 等设备信息 |
|
|
30
|
+
| 2 | 行车状态 | 持续分析传感器数据,得到八种稳定的行车状态 |
|
|
31
|
+
| 3 | 单次分析 | 查看某一帧传感器数据的车辆坐标和瞬时候选状态 |
|
|
32
|
+
| 4 | 行车表情配置 | 读取、解析和校验 Runtime 注入的素材配置 |
|
|
33
|
+
| 5 | 媒体 URL | 判断素材地址能否作为长期有效的公网 HTTPS URL 使用 |
|
|
34
|
+
| 6 | 行车表情播放器 | 根据状态显示 Emoji、图片、视频、Live Photo 或 TGS |
|
|
35
|
+
| 7 | 页面生命周期 | 页面隐藏或 Runtime 暂停时,自动暂停传感器和播放器 |
|
|
36
|
+
| 8 | 车载多媒体 | 接收车载播放按键,并把网页播放器状态同步给 Runtime |
|
|
17
37
|
|
|
18
38
|
## 快速开始
|
|
19
39
|
|
|
20
|
-
|
|
40
|
+
下面的例子只监听行车状态,并把中文状态显示在页面中:
|
|
21
41
|
|
|
22
42
|
```html
|
|
23
43
|
<p id="driving-status">正在读取行车状态…</p>
|
|
24
44
|
```
|
|
25
45
|
|
|
26
46
|
```ts
|
|
27
|
-
import { CAR_RUNNING_LABELS,
|
|
47
|
+
import { CAR_RUNNING_LABELS, DrivingStatusController } from '@ziztechnology/dial-library';
|
|
28
48
|
|
|
29
|
-
const statusElement = document.querySelector('#driving-status');
|
|
49
|
+
const statusElement = document.querySelector<HTMLElement>('#driving-status');
|
|
30
50
|
if (!statusElement) throw new Error('找不到 #driving-status');
|
|
31
51
|
|
|
32
|
-
const controller =
|
|
52
|
+
const controller = new DrivingStatusController();
|
|
33
53
|
|
|
34
|
-
//
|
|
54
|
+
// subscribe 只通知后续变化,所以先显示一次当前状态。
|
|
35
55
|
statusElement.textContent = CAR_RUNNING_LABELS[controller.getSnapshot().status];
|
|
36
56
|
|
|
37
57
|
const unsubscribe = controller.subscribe((event) => {
|
|
@@ -40,62 +60,20 @@ const unsubscribe = controller.subscribe((event) => {
|
|
|
40
60
|
|
|
41
61
|
controller.start();
|
|
42
62
|
|
|
43
|
-
//
|
|
63
|
+
// 页面销毁时释放资源。
|
|
44
64
|
const destroy = () => {
|
|
45
65
|
unsubscribe();
|
|
46
66
|
controller.destroy();
|
|
47
67
|
};
|
|
48
68
|
```
|
|
49
69
|
|
|
50
|
-
`start()`
|
|
51
|
-
|
|
52
|
-
控制器默认会跟随 Runtime 和页面生命周期自动暂停、恢复,因此大多数表盘不需要额外监听 `visibilitychange`。
|
|
70
|
+
`start()` 开始轮询传感器,`stop()` 临时停止,`destroy()` 永久释放资源。三者都可以安全地重复调用,但 `destroy()` 之后不能重新启动。
|
|
53
71
|
|
|
54
|
-
##
|
|
72
|
+
## 1. 基础传感器
|
|
55
73
|
|
|
56
|
-
###
|
|
74
|
+
### 读取设备快照
|
|
57
75
|
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
| 常量值 | `CAR_RUNNING_LABELS` 中文名称 | 默认优先级 |
|
|
61
|
-
| -------------------- | ----------------------------- | ---------: |
|
|
62
|
-
| `STOPPED` | 静止 | 0 |
|
|
63
|
-
| `STEADY_DRIVING` | 匀速行驶 | 0 |
|
|
64
|
-
| `ACCELERATION` | 加速 | 1 |
|
|
65
|
-
| `BRAKING` | 刹车 | 2 |
|
|
66
|
-
| `LEFT_TURN` | 左转 | 3 |
|
|
67
|
-
| `RIGHT_TURN` | 右转 | 3 |
|
|
68
|
-
| `RAPID_ACCELERATION` | 急加速 | 4 |
|
|
69
|
-
| `SUDDEN_BRAKING` | 急刹车 | 5 |
|
|
70
|
-
|
|
71
|
-
相关导出:
|
|
72
|
-
|
|
73
|
-
- `CAR_RUNNING_STATUSES`:按协议顺序保存全部八种状态,适合遍历。
|
|
74
|
-
- `CAR_RUNNING_LABELS`:状态到中文名称的映射。
|
|
75
|
-
- `CAR_RUNNING_STATUS_PRIORITY`:状态优先级。高优先级动作可以抢占正在展示的低优先级状态。
|
|
76
|
-
|
|
77
|
-
```ts
|
|
78
|
-
import { CAR_RUNNING_LABELS, CAR_RUNNING_STATUSES, type CarRunningStatus } from '@ziztechnology/dial-library';
|
|
79
|
-
|
|
80
|
-
const renderStatus = (status: CarRunningStatus) => {
|
|
81
|
-
console.log(status, CAR_RUNNING_LABELS[status]);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
CAR_RUNNING_STATUSES.forEach(renderStatus);
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 电池状态
|
|
88
|
-
|
|
89
|
-
统一传感器信息中的电池字段使用以下字符串联合类型:
|
|
90
|
-
|
|
91
|
-
- `BatteryStatus`:`CHARGING`、`DISCHARGING`、`FULL`、`NOT_CHARGING`、`UNKNOWN`。
|
|
92
|
-
- `BatteryPlugged`:`AC`、`USB`、`WIRELESS`、`DOCK`、`NONE`。
|
|
93
|
-
- `BatteryHealth`:`GOOD`、`OVERHEAT`、`DEAD`、`OVER_VOLTAGE`、`UNSPECIFIED_FAILURE`、`COLD`、`UNKNOWN`。
|
|
94
|
-
- `BATTERY_HEALTH_LABELS`:`BatteryHealth` 到中文名称的映射。
|
|
95
|
-
|
|
96
|
-
## 统一的传感器
|
|
97
|
-
|
|
98
|
-
`unifiedSensorInfo()` 一次返回设备当前的统一快照。快照包含陀螺仪、加速度、电池、温度、Wi-Fi、方向和磁场等信息:
|
|
76
|
+
`unifiedSensorInfo()` 会一次返回当前设备信息:
|
|
99
77
|
|
|
100
78
|
```ts
|
|
101
79
|
import { unifiedSensorInfo } from '@ziztechnology/dial-library';
|
|
@@ -114,99 +92,83 @@ if (info.linearAcceleration.available) {
|
|
|
114
92
|
}
|
|
115
93
|
```
|
|
116
94
|
|
|
117
|
-
|
|
95
|
+
每项数据都带有 `available`。先判断它,再读取 `value`:
|
|
118
96
|
|
|
119
97
|
```ts
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
value: T;
|
|
124
|
-
unit: string | null;
|
|
125
|
-
source: string;
|
|
126
|
-
sampledAtMs: number;
|
|
127
|
-
unavailableReason: null;
|
|
128
|
-
}
|
|
129
|
-
| {
|
|
130
|
-
available: false;
|
|
131
|
-
value: null;
|
|
132
|
-
unit: string | null;
|
|
133
|
-
source: string;
|
|
134
|
-
sampledAtMs: number | null;
|
|
135
|
-
unavailableReason: UnifiedSensorUnavailableReason | null;
|
|
136
|
-
};
|
|
98
|
+
if (info.wifiSsid.available) {
|
|
99
|
+
console.log(info.wifiSsid.value);
|
|
100
|
+
}
|
|
137
101
|
```
|
|
138
102
|
|
|
139
|
-
|
|
103
|
+
不要只用 `value === null` 判断设备是否支持某项能力,因为不可用的原因可能不同。
|
|
140
104
|
|
|
141
|
-
|
|
105
|
+
### 可读取的字段
|
|
142
106
|
|
|
143
|
-
|
|
|
144
|
-
|
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
107
|
+
| 字段 | 内容 | 单位 |
|
|
108
|
+
| -------------------- | ------------------------------------ | -------- |
|
|
109
|
+
| `gyroscope` | 陀螺仪 `{ x, y, z }` | `rad/s` |
|
|
110
|
+
| `accelerometer` | 加速度计 `{ x, y, z }` | `m/s²` |
|
|
111
|
+
| `linearAcceleration` | 去除重力后的线性加速度 `{ x, y, z }` | `m/s²` |
|
|
112
|
+
| `gravity` | 重力向量 `{ x, y, z }` | `m/s²` |
|
|
113
|
+
| `magneticField` | 磁场 `{ x, y, z }` | `µT` |
|
|
114
|
+
| `orientation` | 方向 `{ azimuth, pitch, roll }` | `degree` |
|
|
115
|
+
| `motionIntensity` | 运动强度;旧 Runtime 可能没有此字段 | `m/s²` |
|
|
116
|
+
| `temperature` | 电池温度 | `°C` |
|
|
117
|
+
| `wifiSsid` | 当前 Wi-Fi 名称 | 无 |
|
|
118
|
+
| `batteryCapacity` | 电量百分比和电荷计数 | 无 |
|
|
119
|
+
| `battery` | 是否有电池、充电状态、健康度、电压等 | 无 |
|
|
150
120
|
|
|
151
|
-
`capturedAtMs`
|
|
121
|
+
`capturedAtMs` 是整个快照的生成时间,单项数据中的 `sampledAtMs` 是该传感器的采样时间。
|
|
152
122
|
|
|
153
|
-
###
|
|
123
|
+
### 数据为什么不可用
|
|
154
124
|
|
|
155
|
-
|
|
156
|
-
| -------------------- | -------------------------------------- | -------- |
|
|
157
|
-
| `gyroscope` | `{ x, y, z }` | `rad/s` |
|
|
158
|
-
| `accelerometer` | `{ x, y, z }` | `m/s²` |
|
|
159
|
-
| `linearAcceleration` | `{ x, y, z }` | `m/s²` |
|
|
160
|
-
| `gravity` | `{ x, y, z }` | `m/s²` |
|
|
161
|
-
| `magneticField` | `{ x, y, z }` | `µT` |
|
|
162
|
-
| `orientation` | `{ azimuth, pitch, roll }` | `degree` |
|
|
163
|
-
| `motionIntensity` | 数值;旧 Runtime 中可能不存在 | `m/s²` |
|
|
164
|
-
| `temperature` | 数值 | `°C` |
|
|
165
|
-
| `wifiSsid` | 字符串 | 无 |
|
|
166
|
-
| `batteryCapacity` | `{ levelPercent, chargeCounterUah }` | 无 |
|
|
167
|
-
| `battery` | 电池是否存在、充电状态、健康度和电压等 | 无 |
|
|
125
|
+
`unavailableReason` 可能是:
|
|
168
126
|
|
|
169
|
-
|
|
127
|
+
| 值 | 含义 |
|
|
128
|
+
| ---------------------- | ------------------------ |
|
|
129
|
+
| `SENSOR_MISSING` | 设备没有对应传感器 |
|
|
130
|
+
| `NO_SAMPLE` | 有传感器,但暂时没有样本 |
|
|
131
|
+
| `PLATFORM_UNAVAILABLE` | 当前平台不提供这项能力 |
|
|
132
|
+
| `PERMISSION_DENIED` | 没有读取权限 |
|
|
133
|
+
| `NOT_CONNECTED` | 设备或网络没有连接 |
|
|
170
134
|
|
|
171
|
-
|
|
172
|
-
import { classifyDrivingSnapshot, unifiedSensorInfo } from '@ziztechnology/dial-library';
|
|
135
|
+
电池相关类型包括 `BatteryStatus`、`BatteryPlugged` 和 `BatteryHealth`。`BATTERY_HEALTH_LABELS` 可以把电池健康状态转换成中文。
|
|
173
136
|
|
|
174
|
-
|
|
137
|
+
## 2. 行车状态
|
|
175
138
|
|
|
176
|
-
|
|
177
|
-
console.log('传感器数据不可用、非法或已经过期');
|
|
178
|
-
} else {
|
|
179
|
-
console.log('纵向加速度:', analysis.metrics.longitudinal);
|
|
180
|
-
console.log('偏航角速度:', analysis.metrics.yaw);
|
|
181
|
-
console.log('瞬时候选:', analysis.candidateStatus);
|
|
182
|
-
console.log('拒绝原因:', analysis.rejectionReason);
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
该函数不返回最终的 `STOPPED` 或 `STEADY_DRIVING`,也不保存时间窗口。`candidateStatus` 只能用于诊断,面向 UI 必须使用下一节的状态控制器。
|
|
139
|
+
### 八种状态
|
|
187
140
|
|
|
188
|
-
|
|
141
|
+
| 状态 | 中文 | 默认优先级 |
|
|
142
|
+
| -------------------- | -------- | ---------: |
|
|
143
|
+
| `STOPPED` | 静止 | 0 |
|
|
144
|
+
| `STEADY_DRIVING` | 匀速行驶 | 0 |
|
|
145
|
+
| `ACCELERATION` | 加速 | 1 |
|
|
146
|
+
| `BRAKING` | 刹车 | 2 |
|
|
147
|
+
| `LEFT_TURN` | 左转 | 3 |
|
|
148
|
+
| `RIGHT_TURN` | 右转 | 3 |
|
|
149
|
+
| `RAPID_ACCELERATION` | 急加速 | 4 |
|
|
150
|
+
| `SUDDEN_BRAKING` | 急刹车 | 5 |
|
|
189
151
|
|
|
190
|
-
|
|
191
|
-
right = 将设备 +X 投影到水平面
|
|
192
|
-
up = 归一化后的 TYPE_GRAVITY
|
|
193
|
-
forward = right × up
|
|
194
|
-
```
|
|
152
|
+
相关导出:
|
|
195
153
|
|
|
196
|
-
|
|
154
|
+
- `CAR_RUNNING_STATUSES`:全部状态,适合遍历;
|
|
155
|
+
- `CAR_RUNNING_LABELS`:状态对应的中文名称;
|
|
156
|
+
- `CAR_RUNNING_STATUS_PRIORITY`:状态优先级;
|
|
157
|
+
- `CarRunningStatus`:八种状态的 TypeScript 联合类型。
|
|
197
158
|
|
|
198
|
-
|
|
159
|
+
高优先级动作可以抢占正在显示的低优先级状态。
|
|
199
160
|
|
|
200
|
-
###
|
|
161
|
+
### 持续监听稳定状态
|
|
201
162
|
|
|
202
|
-
`
|
|
163
|
+
页面展示行车状态时,推荐使用 `new DrivingStatusController()`。它会持续读取传感器,并通过时间窗口过滤瞬时噪声:
|
|
203
164
|
|
|
204
165
|
```ts
|
|
205
|
-
import { CAR_RUNNING_LABELS,
|
|
166
|
+
import { CAR_RUNNING_LABELS, DrivingStatusController } from '@ziztechnology/dial-library';
|
|
206
167
|
|
|
207
|
-
const controller =
|
|
168
|
+
const controller = new DrivingStatusController({
|
|
208
169
|
initialStatus: 'STOPPED',
|
|
209
170
|
pollIntervalMs: 200,
|
|
171
|
+
unavailableFallbackMs: 3_000,
|
|
210
172
|
});
|
|
211
173
|
|
|
212
174
|
const unsubscribe = controller.subscribe((event) => {
|
|
@@ -215,73 +177,206 @@ const unsubscribe = controller.subscribe((event) => {
|
|
|
215
177
|
|
|
216
178
|
controller.start();
|
|
217
179
|
|
|
218
|
-
//
|
|
180
|
+
// 临时暂停
|
|
219
181
|
controller.stop();
|
|
220
182
|
|
|
221
|
-
//
|
|
183
|
+
// 需要时可以再次启动
|
|
184
|
+
controller.start();
|
|
185
|
+
|
|
186
|
+
// 永久释放
|
|
222
187
|
unsubscribe();
|
|
223
188
|
controller.destroy();
|
|
224
189
|
```
|
|
225
190
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
静止和匀速使用 accelerometer 的 1 秒三轴 MAD 稳健波动窗口;正常 5Hz 采样至少需要 5 个唯一样本,慢 Bridge 使用相同的时间覆盖规则。运动量不高于 `0.12 m/s²` 且陀螺仪足够稳定,连续 1.5 秒才进入 `STOPPED`;运动量达到 `0.18 m/s²` 或陀螺仪出现持续运动后,从当前运动样本开始连续 0.6 秒才进入 `STEADY_DRIVING`。中间区间保持原基础状态,停车时一次短促晃动不会立即切回行驶。
|
|
229
|
-
|
|
230
|
-
普通加速和刹车要求 0.8 秒内至少 4/5 的样本方向一致;急加速和急刹要求峰值、0.6 秒持续均值及方向一致性同时成立,并拒绝垂直能量占主导或短时间反号的颠簸。车辆刚出现方向稳定的物理偏航后,强转向路径要求 0.4 秒内至少 3 个唯一样本,窗口均值达到 `0.08 rad/s`;在默认轮询频率下通常会在 0.4–0.6 秒内触发。转向也可以由累计转角触发,并锁存到回正结束。算法不会预测车辆尚未产生转动时的驾驶意图。最终优先级为:急加减速、已锁存转向、普通加减速、基础状态。
|
|
191
|
+
控制器默认每 `200ms` 读取一次,并保证同一时间只有一个读取任务。它不会因为一次抖动就改变状态,而是等多个独立样本在一段时间内保持一致后再通知页面。
|
|
231
192
|
|
|
232
|
-
|
|
193
|
+
如果传感器短暂失败,控制器会先保留最后状态。连续不可用达到 `unavailableFallbackMs` 后,才回退到 `STOPPED`。
|
|
233
194
|
|
|
234
|
-
###
|
|
235
|
-
|
|
236
|
-
订阅只通知后续变化。初次渲染或调试时,可以读取快照:
|
|
195
|
+
### 读取控制器当前情况
|
|
237
196
|
|
|
238
197
|
```ts
|
|
239
198
|
const snapshot = controller.getSnapshot();
|
|
240
199
|
|
|
241
|
-
console.log(snapshot.status); //
|
|
200
|
+
console.log(snapshot.status); // 当前已经确认的状态
|
|
242
201
|
console.log(snapshot.candidate); // 正在确认的候选状态,可能为 null
|
|
243
|
-
console.log(snapshot.candidateSinceMs); // 候选首次出现的传感器时间
|
|
244
|
-
console.log(snapshot.activeTurn); // 已锁存的转向事件
|
|
245
202
|
console.log(snapshot.baseStatus); // STOPPED 或 STEADY_DRIVING
|
|
203
|
+
console.log(snapshot.activeTurn); // 当前锁定的转向,可能为 null
|
|
246
204
|
console.log(snapshot.lifecycle); // stopped、running 或 destroyed
|
|
247
|
-
console.log(snapshot.tuningVersion);
|
|
248
205
|
```
|
|
249
206
|
|
|
250
207
|
### 调整识别参数
|
|
251
208
|
|
|
252
|
-
|
|
209
|
+
默认参数保存在 `DEFAULT_DRIVING_STATUS_TUNING`。通常不需要修改;真车测试发现识别过慢或过于敏感时,只覆盖需要调整的字段:
|
|
253
210
|
|
|
254
211
|
```ts
|
|
255
|
-
const controller =
|
|
212
|
+
const controller = new DrivingStatusController({
|
|
256
213
|
tuning: {
|
|
257
214
|
turnYawThreshold: 0.08,
|
|
258
215
|
turnConfirmationMs: 400,
|
|
259
|
-
turnMinimumSamples: 3,
|
|
260
|
-
motionWindowMs: 1_200,
|
|
261
|
-
motionMinimumSamples: 6,
|
|
262
|
-
drivingMotionThreshold: 0.2,
|
|
263
|
-
stoppedMotionThreshold: 0.11,
|
|
264
|
-
stoppedConfirmationMs: 1_500,
|
|
265
216
|
longitudinalThreshold: 0.75,
|
|
266
|
-
|
|
267
|
-
|
|
217
|
+
stoppedConfirmationMs: 1_500,
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
可以先调用 `resolveDrivingStatusTuning(overrides)` 检查参数并得到完整配置。阈值和时间窗口必须是有效的正数,样本数必须是正整数。调整后应使用完整路测数据验证,不要只用一两帧数据判断效果。
|
|
223
|
+
|
|
224
|
+
### 诊断识别过程
|
|
225
|
+
|
|
226
|
+
`onDiagnostic` 适合调试和真车调参:
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
const controller = new DrivingStatusController({
|
|
230
|
+
onDiagnostic(event) {
|
|
231
|
+
if (event.type === 'detector_transition') {
|
|
232
|
+
console.log(event.detector, event.phase, event.status, event.statistics);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (event.type === 'fallback_to_stopped') {
|
|
236
|
+
console.warn(`传感器已连续不可用 ${event.unavailableForMs}ms`);
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
诊断事件会告诉你样本是否有效、各检测器看到了什么证据、候选状态为什么进入或退出,以及何时因为传感器不可用而回退。
|
|
243
|
+
|
|
244
|
+
## 3. 单次分析
|
|
245
|
+
|
|
246
|
+
`classifyDrivingSnapshot()` 分析一份传感器快照,适合开发和调试:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { classifyDrivingSnapshot, unifiedSensorInfo } from '@ziztechnology/dial-library';
|
|
250
|
+
|
|
251
|
+
const analysis = classifyDrivingSnapshot(await unifiedSensorInfo());
|
|
252
|
+
|
|
253
|
+
if (!analysis) {
|
|
254
|
+
console.log('必要的传感器数据缺失、非法或已经过期');
|
|
255
|
+
} else {
|
|
256
|
+
console.log('纵向加速度:', analysis.metrics.longitudinal);
|
|
257
|
+
console.log('横向加速度:', analysis.metrics.lateral);
|
|
258
|
+
console.log('偏航角速度:', analysis.metrics.yaw);
|
|
259
|
+
console.log('瞬时候选状态:', analysis.candidateStatus);
|
|
260
|
+
console.log('是否因颠簸被拒绝:', analysis.rejectionReason);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
这个函数只看当前快照,不保存时间窗口,因此不会给出最终的 `STOPPED` 或 `STEADY_DRIVING`。页面 UI 应使用状态控制器,不能把 `candidateStatus` 当成最终结果。
|
|
265
|
+
|
|
266
|
+
更底层的分析接口还有:
|
|
267
|
+
|
|
268
|
+
- `extractDrivingSnapshotMetrics()`:提取车辆坐标系下的纵向、横向、垂直和偏航数据;
|
|
269
|
+
- `buildVehicleFrame()`:根据重力方向建立车辆坐标系。
|
|
270
|
+
|
|
271
|
+
这两个接口主要用于算法调试。设备安装时应固定正反面和车头方向;安装倾角可以变化,SDK 会根据重力方向进行校正。
|
|
272
|
+
|
|
273
|
+
## 4. 行车表情配置
|
|
274
|
+
|
|
275
|
+
Runtime 会把配置注入到 `__TOOOONY_DRIVING_EXPRESSIONS__`。正常情况下直接读取:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import { readDrivingExpressionsConfig } from '@ziztechnology/dial-library';
|
|
279
|
+
|
|
280
|
+
const config = readDrivingExpressionsConfig();
|
|
281
|
+
|
|
282
|
+
if (!config) {
|
|
283
|
+
console.log('Runtime 没有提供完整、有效的行车表情配置');
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
配置不存在或无效时返回 `null`,SDK 不会偷偷补上默认素材。
|
|
288
|
+
|
|
289
|
+
### 配置格式
|
|
290
|
+
|
|
291
|
+
`schemaVersion: 1` 要求 `states` 恰好包含八种状态,每种状态都可以使用以下一种素材:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
type StructuredDrivingExpressionMedia =
|
|
295
|
+
| { kind: 'emoji'; text: string }
|
|
296
|
+
| { kind: 'image'; mediaAssetId?: number; url: string; mimeType?: string }
|
|
297
|
+
| { kind: 'video'; mediaAssetId?: number; url: string; coverUrl?: string; mimeType?: string }
|
|
298
|
+
| { kind: 'live_photo'; mediaAssetId?: number; coverUrl: string; motionUrl: string }
|
|
299
|
+
| { kind: 'tgs'; mediaAssetId?: number; url: string; coverUrl?: string };
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
最小示例:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
const rawConfig = {
|
|
306
|
+
schemaVersion: 1,
|
|
307
|
+
states: {
|
|
308
|
+
STOPPED: { kind: 'emoji', text: '😐' },
|
|
309
|
+
STEADY_DRIVING: { kind: 'emoji', text: '😊' },
|
|
310
|
+
ACCELERATION: { kind: 'emoji', text: '😄' },
|
|
311
|
+
RAPID_ACCELERATION: { kind: 'emoji', text: '😲' },
|
|
312
|
+
BRAKING: { kind: 'emoji', text: '😣' },
|
|
313
|
+
SUDDEN_BRAKING: { kind: 'emoji', text: '😱' },
|
|
314
|
+
LEFT_TURN: { kind: 'emoji', text: '🤨' },
|
|
315
|
+
RIGHT_TURN: { kind: 'emoji', text: '🤨' },
|
|
268
316
|
},
|
|
317
|
+
};
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### 解析配置
|
|
321
|
+
|
|
322
|
+
| API | 结果 |
|
|
323
|
+
| --------------------------------------- | ---------------------------------------------- |
|
|
324
|
+
| `parseDrivingExpressionsConfig(raw)` | 配置无效时抛出 `DrivingExpressionsConfigError` |
|
|
325
|
+
| `tryParseDrivingExpressionsConfig(raw)` | 配置无效时返回 `null` |
|
|
326
|
+
| `readDrivingExpressionsConfig()` | 读取 Runtime 注入值;不存在或无效时返回 `null` |
|
|
327
|
+
| `waitForDrivingExpressionsConfig()` | 等待晚到的注入;超时返回 `null` |
|
|
328
|
+
|
|
329
|
+
Runtime 通常会在页面脚本运行前完成注入。只有确实需要兼容晚注入时,才使用异步等待:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import { waitForDrivingExpressionsConfig } from '@ziztechnology/dial-library';
|
|
333
|
+
|
|
334
|
+
const abortController = new AbortController();
|
|
335
|
+
window.addEventListener('pagehide', () => abortController.abort(), { once: true });
|
|
336
|
+
|
|
337
|
+
const config = await waitForDrivingExpressionsConfig({
|
|
338
|
+
timeoutMs: 3_000,
|
|
339
|
+
pollIntervalMs: 100,
|
|
340
|
+
signal: abortController.signal,
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
取消时 Promise 会以 `AbortError` 结束。
|
|
345
|
+
|
|
346
|
+
旧版配置允许直接写图片 URL。只在迁移旧配置时开启兼容:
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
const config = parseDrivingExpressionsConfig(rawConfig, {
|
|
350
|
+
allowLegacyImageUrls: true,
|
|
269
351
|
});
|
|
270
352
|
```
|
|
271
353
|
|
|
272
|
-
`
|
|
354
|
+
`resolveDrivingExpression()` 会在某个旧版状态缺失时使用 `STEADY_DRIVING` 素材;`resolveDrivingExpressionStrict()` 不会自动这样做,缺失且没有显式回退时会抛出错误。播放器使用严格规则。
|
|
355
|
+
|
|
356
|
+
## 5. 媒体 URL
|
|
357
|
+
|
|
358
|
+
图片、视频、Live Photo 和 TGS 使用的地址必须是长期有效的公网 HTTPS URL。
|
|
273
359
|
|
|
274
|
-
|
|
360
|
+
```ts
|
|
361
|
+
import { isPublicDrivingMediaUrl } from '@ziztechnology/dial-library';
|
|
362
|
+
|
|
363
|
+
console.log(isPublicDrivingMediaUrl('https://cdn.example.com/expression.png')); // true
|
|
364
|
+
console.log(isPublicDrivingMediaUrl('http://cdn.example.com/expression.png')); // false
|
|
365
|
+
console.log(isPublicDrivingMediaUrl('https://localhost/expression.png')); // false
|
|
366
|
+
```
|
|
275
367
|
|
|
276
|
-
|
|
368
|
+
SDK 会拒绝:
|
|
277
369
|
|
|
278
|
-
|
|
370
|
+
- `http:`、`data:`、`blob:` 等非 HTTPS 地址;
|
|
371
|
+
- 带用户名或密码的地址;
|
|
372
|
+
- localhost、内网 IP 和保留地址;
|
|
373
|
+
- 带常见临时签名参数的地址。
|
|
279
374
|
|
|
280
|
-
|
|
375
|
+
前端 URL 校验只能防止明显错误。服务端仍应根据 `mediaAssetId` 和用户身份检查素材所有权,并重新生成可信地址。
|
|
281
376
|
|
|
282
|
-
|
|
377
|
+
## 6. 行车表情播放器
|
|
283
378
|
|
|
284
|
-
|
|
379
|
+
### 把状态和素材连起来
|
|
285
380
|
|
|
286
381
|
```html
|
|
287
382
|
<div id="expression"></div>
|
|
@@ -290,7 +385,7 @@ Runtime 可以为八种状态注入图片、视频、Live Photo、TGS 或 Emoji
|
|
|
290
385
|
```ts
|
|
291
386
|
import {
|
|
292
387
|
createDrivingExpressionPlayer,
|
|
293
|
-
|
|
388
|
+
DrivingStatusController,
|
|
294
389
|
readDrivingExpressionsConfig,
|
|
295
390
|
} from '@ziztechnology/dial-library';
|
|
296
391
|
|
|
@@ -301,7 +396,7 @@ const config = readDrivingExpressionsConfig();
|
|
|
301
396
|
if (!config) throw new Error('Runtime 未提供有效的行车表情配置');
|
|
302
397
|
|
|
303
398
|
const player = createDrivingExpressionPlayer(container, { config });
|
|
304
|
-
const controller =
|
|
399
|
+
const controller = new DrivingStatusController();
|
|
305
400
|
|
|
306
401
|
await player.show(controller.getSnapshot().status);
|
|
307
402
|
|
|
@@ -318,53 +413,13 @@ const destroy = () => {
|
|
|
318
413
|
};
|
|
319
414
|
```
|
|
320
415
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
TGS 使用 SDK 内置的 SVG light 播放器,并且只在首次显示 TGS 素材时动态加载。表盘项目不需要安装、导入或配置 `lottie-web`,也不需要为构建工具添加 Lottie alias。
|
|
324
|
-
|
|
325
|
-
## 行车状态的回退
|
|
326
|
-
|
|
327
|
-
行车状态有两类彼此独立的回退:传感器不可用时的**状态回退**,以及表情素材失败时的**媒体回退**。
|
|
328
|
-
|
|
329
|
-
### 传感器不可用时回退到静止
|
|
330
|
-
|
|
331
|
-
控制器遇到一次读取失败、非法快照或过期样本时,会暂时保持最后一个已提交状态和仍在有效时间范围内的识别窗口,避免页面闪烁或丢失转向、刹车证据。连续不可用达到 3 秒后,控制器才清空窗口并提交 `STOPPED`:
|
|
332
|
-
|
|
333
|
-
```ts
|
|
334
|
-
const controller = createDrivingStatusController({
|
|
335
|
-
unavailableFallbackMs: 3_000,
|
|
336
|
-
onDiagnostic(event) {
|
|
337
|
-
if (event.type === 'fallback_to_stopped') {
|
|
338
|
-
console.warn(`传感器已连续不可用 ${event.unavailableForMs}ms`);
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
controller.subscribe((event) => {
|
|
344
|
-
if (event.reason === 'unavailable_fallback') {
|
|
345
|
-
console.log('已回退到 STOPPED');
|
|
346
|
-
}
|
|
347
|
-
});
|
|
348
|
-
```
|
|
349
|
-
|
|
350
|
-
传感器恢复后,控制器会清除不可用状态;短缺样前后的时间窗仍需满足独立观测数、持续时间和小于 1 秒的相邻间隔约束,不会把跨越长空洞的数据或单个样本提交给 UI。`unavailableFallbackMs` 必须是大于 0 的有限数值。
|
|
416
|
+
播放器会复用视频元素,并保证同一时间只有一个动态素材在播放。TGS 播放能力已包含在 SDK 中,表盘项目不需要额外安装或配置 `lottie-web`。
|
|
351
417
|
|
|
352
|
-
|
|
418
|
+
播放器提供 `show()`、`pause()`、`resume()`、`destroy()` 和 `getSnapshot()`。快照中的 `phase` 可能是 `idle`、`loading`、`ready`、`fallback` 或 `error`。
|
|
353
419
|
|
|
354
|
-
|
|
420
|
+
### 素材失败时使用回退
|
|
355
421
|
|
|
356
|
-
|
|
357
|
-
| -------------------- | -------- |
|
|
358
|
-
| `STOPPED` | 😐 |
|
|
359
|
-
| `STEADY_DRIVING` | 😊 |
|
|
360
|
-
| `ACCELERATION` | 😄 |
|
|
361
|
-
| `RAPID_ACCELERATION` | 😲 |
|
|
362
|
-
| `BRAKING` | 😣 |
|
|
363
|
-
| `SUDDEN_BRAKING` | 😱 |
|
|
364
|
-
| `LEFT_TURN` | 🤨 |
|
|
365
|
-
| `RIGHT_TURN` | 🤨 |
|
|
366
|
-
|
|
367
|
-
官方母版必须显式把它传给播放器:
|
|
422
|
+
官方 Emoji 回退不会自动启用,需要显式传入:
|
|
368
423
|
|
|
369
424
|
```ts
|
|
370
425
|
import { OFFICIAL_DRIVING_EXPRESSION_FALLBACKS, createDrivingExpressionPlayer } from '@ziztechnology/dial-library';
|
|
@@ -380,79 +435,156 @@ const player = createDrivingExpressionPlayer(container, {
|
|
|
380
435
|
});
|
|
381
436
|
```
|
|
382
437
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
也可以只为部分状态提供自己的回退:
|
|
438
|
+
也可以只为部分状态提供自己的 Emoji 或图片回退:
|
|
386
439
|
|
|
387
440
|
```ts
|
|
388
441
|
const player = createDrivingExpressionPlayer(container, {
|
|
389
442
|
config,
|
|
390
443
|
fallbacks: {
|
|
391
444
|
STOPPED: { kind: 'emoji', text: '😐' },
|
|
392
|
-
STEADY_DRIVING: { kind: '
|
|
445
|
+
STEADY_DRIVING: { kind: 'image', url: 'https://cdn.example.com/steady.png' },
|
|
393
446
|
},
|
|
394
447
|
});
|
|
395
448
|
```
|
|
396
449
|
|
|
397
|
-
|
|
450
|
+
只有 Emoji 和图片可以作为回退素材。网络失败、加载超时、解码失败或配置无效时,`onDiagnostic` 会收到 `media_load_failed`;如果有可用回退,还会收到 `media_fallback_applied`。
|
|
398
451
|
|
|
399
|
-
|
|
452
|
+
默认加载超时是 5 秒,失败后 15 秒再重试。可以通过 `loadTimeoutMs` 和 `retryBackoffMs` 修改。图片和视频的显示方式可以通过 `fit: 'contain' | 'cover'` 设置。
|
|
400
453
|
|
|
401
|
-
|
|
454
|
+
## 7. 页面生命周期
|
|
402
455
|
|
|
403
|
-
|
|
404
|
-
const config = readDrivingExpressionsConfig();
|
|
456
|
+
状态控制器和播放器默认都会跟随以下情况自动暂停和恢复:
|
|
405
457
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
```
|
|
458
|
+
- 页面隐藏或重新显示;
|
|
459
|
+
- `pagehide`、`pageshow`、`freeze`、`resume`;
|
|
460
|
+
- Runtime 的传感器暂停和恢复通知。
|
|
410
461
|
|
|
411
|
-
|
|
462
|
+
因此大多数表盘不需要自己监听 `visibilitychange`。如果页面已经有统一的生命周期管理,可以关闭自动管理,再手动调用相应方法:
|
|
412
463
|
|
|
413
464
|
```ts
|
|
414
|
-
|
|
465
|
+
const controller = new DrivingStatusController({
|
|
466
|
+
managePageLifecycle: false,
|
|
467
|
+
});
|
|
415
468
|
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
pollIntervalMs: 100,
|
|
420
|
-
signal: abortController.signal,
|
|
469
|
+
const player = createDrivingExpressionPlayer(container, {
|
|
470
|
+
config,
|
|
471
|
+
managePageLifecycle: false,
|
|
421
472
|
});
|
|
422
473
|
```
|
|
423
474
|
|
|
424
|
-
|
|
475
|
+
关闭后,页面需要自行调用 `controller.start()` / `controller.stop()` 和 `player.resume()` / `player.pause()`。页面最终销毁时仍应调用两者的 `destroy()`。
|
|
476
|
+
|
|
477
|
+
## 8. 车载多媒体
|
|
478
|
+
|
|
479
|
+
多媒体控制器把网页播放器接入 Runtime 的车载媒体控制能力。页面只需要提供当前状态和播放回调,不要直接创建或调用 `window.AndroidCarBridge`、`window.__carBridge`,也不要自行监听 Runtime 的内部媒体事件。
|
|
425
480
|
|
|
426
|
-
|
|
481
|
+
`AndroidCarBridge` 不依赖 `customFields`,Runtime 会为加载的本地 H5 和直接打开的外部网址注入该能力。页面可以直接创建控制器:
|
|
427
482
|
|
|
428
483
|
```ts
|
|
429
|
-
|
|
430
|
-
| { kind: 'emoji'; text: string }
|
|
431
|
-
| { kind: 'image'; mediaAssetId?: number; url: string; mimeType?: string }
|
|
432
|
-
| {
|
|
433
|
-
kind: 'video';
|
|
434
|
-
mediaAssetId?: number;
|
|
435
|
-
url: string;
|
|
436
|
-
coverUrl?: string;
|
|
437
|
-
mimeType?: string;
|
|
438
|
-
}
|
|
439
|
-
| {
|
|
440
|
-
kind: 'live_photo';
|
|
441
|
-
mediaAssetId?: number;
|
|
442
|
-
coverUrl: string;
|
|
443
|
-
motionUrl: string;
|
|
444
|
-
}
|
|
445
|
-
| { kind: 'tgs'; mediaAssetId?: number; url: string; coverUrl?: string };
|
|
446
|
-
```
|
|
484
|
+
import { MultimediaController } from '@ziztechnology/dial-library';
|
|
447
485
|
|
|
448
|
-
|
|
486
|
+
const tracks = [
|
|
487
|
+
{ title: 'First Song', artist: 'Toooony', url: 'https://cdn.example.com/first.mp3' },
|
|
488
|
+
{ title: 'Second Song', artist: 'Toooony', url: 'https://cdn.example.com/second.mp3' },
|
|
489
|
+
];
|
|
449
490
|
|
|
450
|
-
|
|
491
|
+
const audio = new Audio();
|
|
492
|
+
let currentIndex = 0;
|
|
451
493
|
|
|
452
|
-
|
|
453
|
-
|
|
494
|
+
const loadCurrentTrack = () => {
|
|
495
|
+
audio.src = tracks[currentIndex].url;
|
|
496
|
+
audio.load();
|
|
497
|
+
};
|
|
454
498
|
|
|
455
|
-
|
|
456
|
-
|
|
499
|
+
let multimedia: MultimediaController;
|
|
500
|
+
|
|
501
|
+
const changeTrack = async (offset: number) => {
|
|
502
|
+
currentIndex = (currentIndex + offset + tracks.length) % tracks.length;
|
|
503
|
+
loadCurrentTrack();
|
|
504
|
+
await audio.play();
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
loadCurrentTrack();
|
|
508
|
+
|
|
509
|
+
multimedia = new MultimediaController({
|
|
510
|
+
getState() {
|
|
511
|
+
const track = tracks[currentIndex];
|
|
512
|
+
return {
|
|
513
|
+
isPlaying: !audio.paused && !audio.ended,
|
|
514
|
+
title: track.title,
|
|
515
|
+
artist: track.artist,
|
|
516
|
+
canNext: tracks.length > 1,
|
|
517
|
+
canPrevious: tracks.length > 1,
|
|
518
|
+
};
|
|
519
|
+
},
|
|
520
|
+
controls: {
|
|
521
|
+
play: () => audio.play(),
|
|
522
|
+
pause: () => audio.pause(),
|
|
523
|
+
next: () => changeTrack(1),
|
|
524
|
+
previous: () => changeTrack(-1),
|
|
525
|
+
seekToMs: (positionMs) => {
|
|
526
|
+
audio.currentTime = positionMs / 1_000;
|
|
527
|
+
},
|
|
528
|
+
},
|
|
457
529
|
});
|
|
530
|
+
|
|
531
|
+
// 播放状态变化时立即同步;播放期间 SDK 还会每秒补报一次。
|
|
532
|
+
const reportState = () => multimedia.reportState();
|
|
533
|
+
audio.addEventListener('playing', reportState);
|
|
534
|
+
audio.addEventListener('pause', reportState);
|
|
535
|
+
audio.addEventListener('ended', reportState);
|
|
536
|
+
|
|
537
|
+
window.addEventListener(
|
|
538
|
+
'pagehide',
|
|
539
|
+
() => {
|
|
540
|
+
audio.removeEventListener('playing', reportState);
|
|
541
|
+
audio.removeEventListener('pause', reportState);
|
|
542
|
+
audio.removeEventListener('ended', reportState);
|
|
543
|
+
multimedia.destroy();
|
|
544
|
+
},
|
|
545
|
+
{ once: true },
|
|
546
|
+
);
|
|
458
547
|
```
|
|
548
|
+
|
|
549
|
+
### 播放器状态
|
|
550
|
+
|
|
551
|
+
`getState()` 必须同步返回以下五个字段。SDK 只会把这些字段交给 Runtime:
|
|
552
|
+
|
|
553
|
+
| 字段 | 含义 |
|
|
554
|
+
| ------------- | ------------------------ |
|
|
555
|
+
| `isPlaying` | 当前是否正在播放 |
|
|
556
|
+
| `title` | 当前内容标题 |
|
|
557
|
+
| `artist` | 当前作者或歌手 |
|
|
558
|
+
| `canNext` | 当前是否允许切换到下一项 |
|
|
559
|
+
| `canPrevious` | 当前是否允许切换到上一项 |
|
|
560
|
+
|
|
561
|
+
当前 Runtime 不读取 `positionMs`、`durationMs`、`album` 或 `artworkUrl` 等扩展字段,因此它们不属于 SDK 的状态契约。曲目或导航能力发生变化时,即使播放状态没有变化,也应调用 `reportState()`。
|
|
562
|
+
|
|
563
|
+
### 控制回调和状态上报
|
|
564
|
+
|
|
565
|
+
`play` 和 `pause` 是必需回调;`toggle`、`next`、`previous`、`seekToMs` 可以省略。没有提供 `toggle` 时,SDK 会根据 `getState().isPlaying` 自动选择 `play` 或 `pause`。Runtime 只会给 `seekToMs` 传入有限且非负的毫秒值,SDK 也会拒绝非法值。
|
|
566
|
+
|
|
567
|
+
控制回调可以返回 Promise。回调成功后 SDK 会再上报一次状态;异步失败会写入 SDK 和 Android 日志,不会形成未处理的 Promise rejection。业务仍应在真实的播放器事件中调用 `reportState()`,因为播放开始、缓冲或曲目加载可能晚于控制回调。
|
|
568
|
+
|
|
569
|
+
首次创建时,SDK 会先读取并校验初始状态,再通知 Runtime 控制器已经 ready,随后立即上报该状态。初始状态无效或读取失败时不会向 Runtime 留下可接收按键的 ready 控制器。只要最新状态中的 `isPlaying` 为 `true`,SDK 就会默认每 `1_000ms` 补报一次;可以用 `reportIntervalMs` 设置其他正数间隔。一次临时的状态读取失败不会停止心跳,后续周期会继续重试。暂停或销毁后心跳会停止。
|
|
570
|
+
|
|
571
|
+
同一个页面同一时间只能存在一个多媒体控制器。SDK 不会覆盖已有的内部 `__carBridge`;不再使用时必须调用 `destroy()`,该方法可以安全地重复调用。支持媒体生命周期协议的 Runtime 会在销毁时立即清除 ready、最后播放状态和按键路由状态,因此旧控制器不会继续吞掉车载按键。旧版 Runtime 不支持显式销毁通知时,SDK 会尽力补报一次暂停状态,页面切换或 Runtime 停用 WebView 时仍会完成最终清理。初始化异常可以通过 `MultimediaBridgeError.code` 区分:
|
|
572
|
+
|
|
573
|
+
| code | 含义 |
|
|
574
|
+
| ----------------------------------------- | -------------------------------------- |
|
|
575
|
+
| `MULTIMEDIA_BRIDGE_CONFLICT` | 页面已经存在媒体命令入口或另一个控制器 |
|
|
576
|
+
| `MULTIMEDIA_BRIDGE_INITIALIZATION_FAILED` | Ready 通知或初始状态上报失败 |
|
|
577
|
+
|
|
578
|
+
## 常用导出速查
|
|
579
|
+
|
|
580
|
+
| 需求 | 推荐 API |
|
|
581
|
+
| --------------------- | ----------------------------------- |
|
|
582
|
+
| 读取一次设备信息 | `unifiedSensorInfo()` |
|
|
583
|
+
| 持续获取稳定行车状态 | `new DrivingStatusController()` |
|
|
584
|
+
| 分析一帧传感器数据 | `classifyDrivingSnapshot()` |
|
|
585
|
+
| 读取 Runtime 素材配置 | `readDrivingExpressionsConfig()` |
|
|
586
|
+
| 等待晚注入的配置 | `waitForDrivingExpressionsConfig()` |
|
|
587
|
+
| 校验一份原始配置 | `parseDrivingExpressionsConfig()` |
|
|
588
|
+
| 检查媒体 URL | `isPublicDrivingMediaUrl()` |
|
|
589
|
+
| 根据状态播放素材 | `createDrivingExpressionPlayer()` |
|
|
590
|
+
| 接入车载多媒体控制 | `new MultimediaController()` |
|