rvis-aiui-kit 1.1.1 → 1.2.2
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 +21 -19
- package/package.json +1 -2
- package/sdk/README.md +32 -2
- package/sdk/api-map.js +2 -0
- package/sdk/core/client.js +52 -15
- package/sdk/core/constants.js +1 -0
- package/sdk/core/transport.js +21 -2
- package/sdk/modules/status-bar/index.js +78 -0
- package/sdk/modules/status-bar/readme.md +69 -0
- package/components/status-bar/index.ink +0 -139
- package/components/status-bar/readme.md +0 -45
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# rvis-aiui-kit
|
|
2
2
|
|
|
3
|
-
面向 Rokid AIUI / JSUI 应用的组件库与 Glass3 扩展 SDK。包内包含
|
|
3
|
+
面向 Rokid AIUI / JSUI 应用的组件库与 Glass3 扩展 SDK。包内包含 6 个 Ink UI 组件、10 组 Host RPC 能力,以及一个 Rokid OpenAPI HTTPS/SSE 客户端。
|
|
4
4
|
|
|
5
5
|
## 文档导航
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
- [Glass3 SDK API](#glass3-sdk-api)
|
|
10
10
|
- [API 总览](#api-总览)
|
|
11
11
|
- [Device Context](#device-context)
|
|
12
|
+
- [Status Bar](#status-bar)
|
|
12
13
|
- [Screen](#screen)
|
|
13
14
|
- [Notification](#notification)
|
|
14
15
|
- [TTS](#tts)
|
|
@@ -26,7 +27,6 @@
|
|
|
26
27
|
- [Markdown](#markdown)
|
|
27
28
|
- [Model List](#model-list)
|
|
28
29
|
- [Paragraph](#paragraph)
|
|
29
|
-
- [Status Bar](#status-bar)
|
|
30
30
|
- [Table](#table)
|
|
31
31
|
|
|
32
32
|
## 安装与运行环境
|
|
@@ -64,7 +64,6 @@ npm install rvis-aiui-kit
|
|
|
64
64
|
}
|
|
65
65
|
},
|
|
66
66
|
"usingComponents": {
|
|
67
|
-
"rvis-status-bar": "rvis-aiui-kit/status-bar",
|
|
68
67
|
"rvis-paragraph": "rvis-aiui-kit/paragraph",
|
|
69
68
|
"rvis-table": "rvis-aiui-kit/table"
|
|
70
69
|
}
|
|
@@ -83,6 +82,11 @@ export default {
|
|
|
83
82
|
resultText: '准备就绪'
|
|
84
83
|
},
|
|
85
84
|
|
|
85
|
+
async onLoad() {
|
|
86
|
+
await glass3.statusBar.setTitle({ title: '巡检' });
|
|
87
|
+
await glass3.statusBar.setVisible({ visible: true });
|
|
88
|
+
},
|
|
89
|
+
|
|
86
90
|
onMessage(messageEvent) {
|
|
87
91
|
// Host RPC 的 Promise 和流式 onEvent 都依赖这一步。
|
|
88
92
|
glass3.handleMessage(messageEvent);
|
|
@@ -108,7 +112,6 @@ export default {
|
|
|
108
112
|
|
|
109
113
|
<page>
|
|
110
114
|
<view class="page">
|
|
111
|
-
<rvis-status-bar title="巡检"></rvis-status-bar>
|
|
112
115
|
<view class="content">
|
|
113
116
|
<rvis-paragraph text="{{ resultText }}" width="424px"></rvis-paragraph>
|
|
114
117
|
</view>
|
|
@@ -127,7 +130,7 @@ export default {
|
|
|
127
130
|
|
|
128
131
|
.content {
|
|
129
132
|
position: absolute;
|
|
130
|
-
top:
|
|
133
|
+
top: 150px;
|
|
131
134
|
left: 28px;
|
|
132
135
|
width: 424px;
|
|
133
136
|
}
|
|
@@ -169,6 +172,7 @@ import glass3, {
|
|
|
169
172
|
| `deviceContext` | `get(options?)` | 获取设备上下文原子快照 |
|
|
170
173
|
| `deviceContext` | `observe(options?, callOptions?)` | 订阅设备上下文快照和变化 |
|
|
171
174
|
| `deviceContext` | `stopObserve({ requestId })` | 停止设备上下文订阅 |
|
|
175
|
+
| `statusBar` | `setTitle({ title })` / `setVisible({ visible })` | 设置标题并显示 / 隐藏 Native 状态栏 |
|
|
172
176
|
| `screen` | `turnOff()` / `turnOn()` | 息屏 / 亮屏 |
|
|
173
177
|
| `notification` | `show(options)` / `hide({ requestId })` | 显示 / 隐藏原生通知 |
|
|
174
178
|
| `tts` | `speak(options, callOptions?)` / `stop({ requestId })` | TTS 播报 / 停止 |
|
|
@@ -217,6 +221,18 @@ await glass3.deviceContext.stopObserve({ requestId: observer.requestId });
|
|
|
217
221
|
|
|
218
222
|
`get()` 等待 `snapshot` 事件后完成,返回 `{ requestId, ok, eventType: 'snapshot', snapshot }`。`observe()` 在订阅启动后返回 `{ requestId, ok, state: 'started' }`;首个业务事件为 `snapshot`,后续为 `changed` 或 `invalidated`。SDK 不自动合并 patch。
|
|
219
223
|
|
|
224
|
+
### Status Bar
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
await glass3.statusBar.setTitle({ title: '设备巡检' });
|
|
228
|
+
const result = await glass3.statusBar.setVisible({ visible: true });
|
|
229
|
+
// { updated: true, visible: true }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`title` 必须是字符串,空字符串或全空白字符串用于清理标题;`visible` 必须是 Boolean。建议先设置标题,再显示状态栏。该能力直接解析 Host RPC HTTP response body,不依赖 `onMessage`。
|
|
233
|
+
|
|
234
|
+
Native 状态栏可能占用屏幕 y=0 到 y=149。全屏页面根节点仍从 y=0 开始,只将页面内容放到 y=150 或更低位置。
|
|
235
|
+
|
|
220
236
|
### Screen
|
|
221
237
|
|
|
222
238
|
```js
|
|
@@ -658,20 +674,6 @@ data: {
|
|
|
658
674
|
<rvis-paragraph text="{{ text }}" width="424px" segments="{{ segments }}"></rvis-paragraph>
|
|
659
675
|
```
|
|
660
676
|
|
|
661
|
-
### Status Bar
|
|
662
|
-
|
|
663
|
-
注册路径:`rvis-aiui-kit/status-bar`
|
|
664
|
-
|
|
665
|
-
| 属性 | 类型 | 默认值 | 说明 |
|
|
666
|
-
| --- | --- | --- | --- |
|
|
667
|
-
| `title` | `String` | `''` | 左侧应用标题 |
|
|
668
|
-
|
|
669
|
-
```html
|
|
670
|
-
<rvis-status-bar title="应用名称"></rvis-status-bar>
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
组件尺寸固定为 `480px × 20px`,显示应用标题、本地日期时间和实时电量。页面应将其放在全屏绘制区域的 `(0, 0)`。组件自动管理时钟和 `power` 订阅,但 Page 必须把 `onMessage` 转发给共享的 `glass3` 实例。
|
|
674
|
-
|
|
675
677
|
### Table
|
|
676
678
|
|
|
677
679
|
注册路径:`rvis-aiui-kit/table`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvis-aiui-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "RVIS AIUI components and Glass3 extension SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./sdk/index.js",
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
"./markdown": "./components/markdown/index",
|
|
19
19
|
"./model-list": "./components/model-list/index",
|
|
20
20
|
"./paragraph": "./components/paragraph/index",
|
|
21
|
-
"./status-bar": "./components/status-bar/index",
|
|
22
21
|
"./table": "./components/table/index"
|
|
23
22
|
}
|
|
24
23
|
},
|
package/sdk/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- [API 总览](#api-总览)
|
|
14
14
|
- [通用调用约定](#通用调用约定)
|
|
15
15
|
- [Device Context 设备上下文](#device-context-设备上下文)
|
|
16
|
+
- [Status Bar 原生状态栏](#status-bar-原生状态栏)
|
|
16
17
|
- [Screen 屏幕控制](#screen-屏幕控制)
|
|
17
18
|
- [Notification 原生通知](#notification-原生通知)
|
|
18
19
|
- [TTS 语音播报](#tts-语音播报)
|
|
@@ -65,6 +66,8 @@ import glass3, {
|
|
|
65
66
|
|
|
66
67
|
| 模块 | 方法 | 用途 |
|
|
67
68
|
| --- | --- | --- |
|
|
69
|
+
| `statusBar` | `setTitle({ title })` | 设置或清理当前 AIX 页面的状态栏标题。 |
|
|
70
|
+
| `statusBar` | `setVisible({ visible })` | 显示或重新隐藏 Native 状态栏。 |
|
|
68
71
|
| `deviceContext` | `get(options?)` | 一次性获取设备上下文原子快照。 |
|
|
69
72
|
| `deviceContext` | `observe(options?, callOptions?)` | 订阅设备上下文快照和增量变化。 |
|
|
70
73
|
| `deviceContext` | `stopObserve({ requestId })` | 停止指定的设备上下文订阅。 |
|
|
@@ -218,6 +221,28 @@ const result = await glass3.deviceContext.stopObserve({
|
|
|
218
221
|
|
|
219
222
|
详见 [`modules/device-context/readme.md`](./modules/device-context/readme.md)。
|
|
220
223
|
|
|
224
|
+
## Status Bar 原生状态栏
|
|
225
|
+
|
|
226
|
+
状态栏是 Host 管理的系统 UI,可在屏幕顶部显示标题、时间、网络和电量。该能力只在
|
|
227
|
+
通过 `OPEN_AIX_INK` 外部入口打开的 AIX 页面中注册,页面创建后默认隐藏。
|
|
228
|
+
|
|
229
|
+
Native 状态栏可能占用屏幕 y=0 到 y=149。页面根节点仍从 y=0 开始,
|
|
230
|
+
只将页面内容放到 y=150 或更低位置。
|
|
231
|
+
|
|
232
|
+
建议先设置标题,再显示:
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
await glass3.statusBar.setTitle({ title: '设备巡检' });
|
|
236
|
+
const result = await glass3.statusBar.setVisible({ visible: true });
|
|
237
|
+
// { updated: true, visible: true }
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`title` 必须是字符串;空字符串或全空白字符串用于清理标题。`visible` 必须是
|
|
241
|
+
Boolean。两个方法直接解析 Host RPC HTTP response body,不依赖 `onMessage`,并有
|
|
242
|
+
5 秒本地等待上限。
|
|
243
|
+
|
|
244
|
+
详见 [`modules/status-bar/readme.md`](./modules/status-bar/readme.md)。
|
|
245
|
+
|
|
221
246
|
## Screen 屏幕控制
|
|
222
247
|
|
|
223
248
|
### `glass3.screen.turnOff()`
|
|
@@ -986,8 +1011,8 @@ export default {
|
|
|
986
1011
|
|
|
987
1012
|
## Native 映射
|
|
988
1013
|
|
|
989
|
-
|
|
990
|
-
`method: "invoke"`。
|
|
1014
|
+
除 `statusBar` 外的 Host RPC 业务调用使用 `version: "2.0.0"`、
|
|
1015
|
+
`namespace: "rokid.tools"`、`method: "invoke"`。
|
|
991
1016
|
|
|
992
1017
|
| JavaScript API | `toolName` | `toolAction` |
|
|
993
1018
|
| --- | --- | --- |
|
|
@@ -1014,12 +1039,16 @@ export default {
|
|
|
1014
1039
|
| `motion.startDetect` | `motionDetect` | `start` |
|
|
1015
1040
|
| `motion.stopDetect` | `motionDetect` | `stop` |
|
|
1016
1041
|
|
|
1042
|
+
`statusBar.setTitle` 和 `statusBar.setVisible` 使用 `namespace: "rokid.statusBar"`,
|
|
1043
|
+
Native method 分别为 `setTitle` 和 `setVisible`,业务参数直接放在 `params` 中。
|
|
1044
|
+
|
|
1017
1045
|
业务代码应调用公开 JavaScript API,不要直接依赖这张 Native 映射表发 RPC。
|
|
1018
1046
|
|
|
1019
1047
|
## 详细文档与 Demo
|
|
1020
1048
|
|
|
1021
1049
|
### 模块文档
|
|
1022
1050
|
|
|
1051
|
+
- [Status Bar](./modules/status-bar/readme.md)
|
|
1023
1052
|
- [Device Context](./modules/device-context/readme.md)
|
|
1024
1053
|
- [Screen](./modules/screen/readme.md)
|
|
1025
1054
|
- [Notification](./modules/notification/readme.md)
|
|
@@ -1032,6 +1061,7 @@ export default {
|
|
|
1032
1061
|
|
|
1033
1062
|
### 示例页面
|
|
1034
1063
|
|
|
1064
|
+
- [Status Bar Demo](../pages/sdk-demo/status-bar/index.ink)
|
|
1035
1065
|
- [Device Context Demo](../pages/sdk-demo/device-context/index.ink)
|
|
1036
1066
|
- [Screen Demo](../pages/sdk-demo/screen/index.ink)
|
|
1037
1067
|
- [Notification Demo Suite](../pages/sdk-demo/notification/index.ink)
|
package/sdk/api-map.js
CHANGED
|
@@ -6,11 +6,13 @@ import motion from './modules/motion/index.js';
|
|
|
6
6
|
import notification from './modules/notification/index.js';
|
|
7
7
|
import offlineCommand from './modules/offline-command/index.js';
|
|
8
8
|
import screen from './modules/screen/index.js';
|
|
9
|
+
import statusBar from './modules/status-bar/index.js';
|
|
9
10
|
import tts from './modules/tts/index.js';
|
|
10
11
|
|
|
11
12
|
// This map is the only mapping between the public JavaScript API and Host RPC.
|
|
12
13
|
// Public names are never inferred from the native namespace or method strings.
|
|
13
14
|
export const API_MAP = [].concat(
|
|
15
|
+
statusBar,
|
|
14
16
|
deviceContext,
|
|
15
17
|
notification,
|
|
16
18
|
screen,
|
package/sdk/core/client.js
CHANGED
|
@@ -43,12 +43,14 @@ export class Glass3Client {
|
|
|
43
43
|
requestId,
|
|
44
44
|
namespace: definition.namespace,
|
|
45
45
|
method: definition.method,
|
|
46
|
-
params:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
params: typeof definition.buildRequestParams === 'function'
|
|
47
|
+
? definition.buildRequestParams(nativeArgs)
|
|
48
|
+
: {
|
|
49
|
+
toolName: definition.toolName,
|
|
50
|
+
toolAction: definition.toolAction,
|
|
51
|
+
args: nativeArgs,
|
|
52
|
+
extra: {}
|
|
53
|
+
}
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
if (definition.completion === COMPLETION_EVENT) {
|
|
@@ -59,7 +61,12 @@ export class Glass3Client {
|
|
|
59
61
|
return this.invokeUntilReadyEventStream(definition, request, callOptions);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
return this.invokeUntilAccepted(
|
|
64
|
+
return this.invokeUntilAccepted(
|
|
65
|
+
definition,
|
|
66
|
+
request,
|
|
67
|
+
callOptions,
|
|
68
|
+
timeoutMs
|
|
69
|
+
);
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
sendRequest(definition, request) {
|
|
@@ -104,7 +111,7 @@ export class Glass3Client {
|
|
|
104
111
|
return true;
|
|
105
112
|
}
|
|
106
113
|
|
|
107
|
-
invokeUntilAccepted(definition, request, callOptions) {
|
|
114
|
+
invokeUntilAccepted(definition, request, callOptions, timeoutMs) {
|
|
108
115
|
if (typeof callOptions.onEvent === 'function') {
|
|
109
116
|
this.activeInvocations.set(request.requestId, {
|
|
110
117
|
requestId: request.requestId,
|
|
@@ -113,13 +120,37 @@ export class Glass3Client {
|
|
|
113
120
|
});
|
|
114
121
|
}
|
|
115
122
|
|
|
116
|
-
|
|
123
|
+
let timeoutId = null;
|
|
124
|
+
const responsePromise = this.sendRequest(definition, request);
|
|
125
|
+
|
|
126
|
+
if (timeoutMs !== undefined) {
|
|
127
|
+
timeoutId = setTimeout(() => {
|
|
128
|
+
this.rejectPendingResponse(request.requestId, new Glass3Error(
|
|
129
|
+
`Glass3 call timed out after ${timeoutMs}ms: ${definition.publicModule}.${definition.publicMethod}`,
|
|
130
|
+
{
|
|
131
|
+
code: 'CALL_TIMEOUT',
|
|
132
|
+
stage: 'timeout',
|
|
133
|
+
requestId: request.requestId,
|
|
134
|
+
namespace: definition.namespace,
|
|
135
|
+
method: definition.method,
|
|
136
|
+
details: { timeout: timeoutMs }
|
|
137
|
+
}
|
|
138
|
+
));
|
|
139
|
+
}, timeoutMs);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return responsePromise.then(response => {
|
|
143
|
+
if (timeoutId !== null) clearTimeout(timeoutId);
|
|
117
144
|
this.validateAcceptedResponse(definition, request.requestId, response);
|
|
145
|
+
if (typeof definition.selectAcceptedResult === 'function') {
|
|
146
|
+
return definition.selectAcceptedResult(response);
|
|
147
|
+
}
|
|
118
148
|
return {
|
|
119
149
|
requestId: response.requestId,
|
|
120
150
|
ok: response.ok
|
|
121
151
|
};
|
|
122
152
|
}).catch(error => {
|
|
153
|
+
if (timeoutId !== null) clearTimeout(timeoutId);
|
|
123
154
|
this.activeInvocations.delete(request.requestId);
|
|
124
155
|
throw this.normalizeInvocationError(error, definition, request.requestId);
|
|
125
156
|
});
|
|
@@ -235,22 +266,28 @@ export class Glass3Client {
|
|
|
235
266
|
acceptedResult.toolName === definition.toolName &&
|
|
236
267
|
acceptedResult.toolAction === definition.toolAction
|
|
237
268
|
);
|
|
238
|
-
const
|
|
269
|
+
const validChannel = !definition.responseChannel || (
|
|
270
|
+
response && response.channel === definition.responseChannel
|
|
271
|
+
);
|
|
272
|
+
const validEnvelope = response &&
|
|
273
|
+
validChannel &&
|
|
239
274
|
response.version === RPC_VERSION &&
|
|
240
275
|
response.kind === 'response' &&
|
|
241
|
-
response.requestId === requestId
|
|
242
|
-
|
|
243
|
-
validAcceptedResult;
|
|
276
|
+
response.requestId === requestId;
|
|
277
|
+
const validResponse = validEnvelope &&
|
|
278
|
+
response.ok === true && validAcceptedResult;
|
|
244
279
|
|
|
245
280
|
if (validResponse) return;
|
|
246
281
|
|
|
247
|
-
const nativeError =
|
|
282
|
+
const nativeError = validEnvelope && response.ok === false
|
|
283
|
+
? response.error
|
|
284
|
+
: null;
|
|
248
285
|
throw new Glass3Error(
|
|
249
286
|
nativeError && nativeError.message
|
|
250
287
|
? nativeError.message
|
|
251
288
|
: `Native request was not accepted: ${definition.publicModule}.${definition.publicMethod}`,
|
|
252
289
|
{
|
|
253
|
-
code: nativeError && nativeError.code
|
|
290
|
+
code: nativeError && nativeError.code !== undefined
|
|
254
291
|
? nativeError.code
|
|
255
292
|
: 'NATIVE_REQUEST_NOT_ACCEPTED',
|
|
256
293
|
stage: 'response',
|
package/sdk/core/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const RPC_URL = 'https://ink-host-rpc.invalid/rpc';
|
|
2
2
|
export const RPC_ORIGIN = 'rokid://host-rpc';
|
|
3
3
|
export const RPC_VERSION = '2.0.0';
|
|
4
|
+
export const RPC_CHANNEL = 'rokid.host.rpc';
|
|
4
5
|
|
|
5
6
|
export const RPC_SUCCESS_STATUS_MIN = 200;
|
|
6
7
|
export const RPC_SUCCESS_STATUS_MAX = 299;
|
package/sdk/core/transport.js
CHANGED
|
@@ -22,10 +22,20 @@ export class HostRpcTransport {
|
|
|
22
22
|
JSON.stringify(redactSensitiveDataForLog(request))
|
|
23
23
|
);
|
|
24
24
|
|
|
25
|
-
return requestFetch(this.url, {
|
|
25
|
+
return Promise.resolve().then(() => requestFetch(this.url, {
|
|
26
26
|
method: 'POST',
|
|
27
27
|
headers: { 'content-type': 'application/json' },
|
|
28
28
|
body: requestBody
|
|
29
|
+
})).catch(error => {
|
|
30
|
+
if (error instanceof Glass3Error) throw error;
|
|
31
|
+
throw new Glass3Error('Host RPC transport failed', {
|
|
32
|
+
code: 'TRANSPORT_ERROR',
|
|
33
|
+
stage: 'transport',
|
|
34
|
+
requestId: request.requestId,
|
|
35
|
+
namespace: request.namespace,
|
|
36
|
+
method: request.method,
|
|
37
|
+
cause: error
|
|
38
|
+
});
|
|
29
39
|
}).then(response => {
|
|
30
40
|
if (
|
|
31
41
|
response.status < RPC_SUCCESS_STATUS_MIN ||
|
|
@@ -43,7 +53,16 @@ export class HostRpcTransport {
|
|
|
43
53
|
);
|
|
44
54
|
}
|
|
45
55
|
|
|
46
|
-
return response.json()
|
|
56
|
+
return Promise.resolve().then(() => response.json()).catch(error => {
|
|
57
|
+
throw new Glass3Error('Host RPC response is not valid JSON', {
|
|
58
|
+
code: 'TRANSPORT_INVALID_RESPONSE',
|
|
59
|
+
stage: 'transport',
|
|
60
|
+
requestId: request.requestId,
|
|
61
|
+
namespace: request.namespace,
|
|
62
|
+
method: request.method,
|
|
63
|
+
cause: error
|
|
64
|
+
});
|
|
65
|
+
});
|
|
47
66
|
}).then(result => {
|
|
48
67
|
console.log(
|
|
49
68
|
'[glass3] Native fetch response:',
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { RPC_CHANNEL } from '../../core/constants.js';
|
|
2
|
+
import { Glass3Error } from '../../utils/errors.js';
|
|
3
|
+
|
|
4
|
+
const STATUS_BAR_NAMESPACE = 'rokid.statusBar';
|
|
5
|
+
const STATUS_BAR_TIMEOUT_MS = 5000;
|
|
6
|
+
|
|
7
|
+
function createParameterError(message, method) {
|
|
8
|
+
return new Glass3Error(message, {
|
|
9
|
+
code: 'INVALID_PARAMS',
|
|
10
|
+
stage: 'params',
|
|
11
|
+
namespace: STATUS_BAR_NAMESPACE,
|
|
12
|
+
method
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function buildSingleFieldArgs(field, type, method) {
|
|
17
|
+
return args => {
|
|
18
|
+
if (!args || typeof args !== 'object' || Array.isArray(args)) {
|
|
19
|
+
throw createParameterError(
|
|
20
|
+
`statusBar.${method} parameters must be an object`,
|
|
21
|
+
method
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const fields = Object.keys(args);
|
|
26
|
+
if (fields.length !== 1 || fields[0] !== field) {
|
|
27
|
+
throw createParameterError(
|
|
28
|
+
`statusBar.${method} only supports ${field}`,
|
|
29
|
+
method
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (typeof args[field] !== type) {
|
|
34
|
+
throw createParameterError(
|
|
35
|
+
`statusBar.${method} ${field} must be a ${type}`,
|
|
36
|
+
method
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return { [field]: args[field] };
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function directParams(args) {
|
|
45
|
+
return args;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function selectResult(response) {
|
|
49
|
+
return response.result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createStatusBarDefinition(publicMethod, field, type) {
|
|
53
|
+
return {
|
|
54
|
+
publicModule: 'statusBar',
|
|
55
|
+
publicMethod,
|
|
56
|
+
|
|
57
|
+
namespace: STATUS_BAR_NAMESPACE,
|
|
58
|
+
method: publicMethod,
|
|
59
|
+
responseChannel: RPC_CHANNEL,
|
|
60
|
+
|
|
61
|
+
buildArgs: buildSingleFieldArgs(field, type, publicMethod),
|
|
62
|
+
buildRequestParams: directParams,
|
|
63
|
+
getTimeoutMs() {
|
|
64
|
+
return STATUS_BAR_TIMEOUT_MS;
|
|
65
|
+
},
|
|
66
|
+
isAcceptedResponseResult(result) {
|
|
67
|
+
if (!result || typeof result.updated !== 'boolean') return false;
|
|
68
|
+
return publicMethod !== 'setVisible' ||
|
|
69
|
+
typeof result.visible === 'boolean';
|
|
70
|
+
},
|
|
71
|
+
selectAcceptedResult: selectResult
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default [
|
|
76
|
+
createStatusBarDefinition('setTitle', 'title', 'string'),
|
|
77
|
+
createStatusBarDefinition('setVisible', 'visible', 'boolean')
|
|
78
|
+
];
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Status Bar 原生状态栏
|
|
2
|
+
|
|
3
|
+
`glass3.statusBar` 通过同步 HTTP response body 的 Host RPC 控制 AIX 页面顶部的
|
|
4
|
+
系统状态栏。状态栏由 Native 绘制,可显示标题、时间、网络和电量;调用结果不依赖
|
|
5
|
+
页面 `onMessage`。
|
|
6
|
+
|
|
7
|
+
该能力只在通过外部 `OPEN_AIX_INK` 入口打开的 AIX 页面中注册。页面建立后默认隐藏
|
|
8
|
+
状态栏,普通浏览器、普通 WebView 和普通 AI 卡片中调用会失败。
|
|
9
|
+
|
|
10
|
+
Native 状态栏可能占用屏幕 y=0 到 y=149。保持全屏页面根节点从 y=0
|
|
11
|
+
开始,只把页面内容放到 y=150 或更低位置。
|
|
12
|
+
|
|
13
|
+
## API
|
|
14
|
+
|
|
15
|
+
### `glass3.statusBar.setTitle({ title })`
|
|
16
|
+
|
|
17
|
+
设置当前页面的状态栏标题:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
const result = await glass3.statusBar.setTitle({
|
|
21
|
+
title: '设备巡检'
|
|
22
|
+
});
|
|
23
|
+
// { updated: true }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- `title` 必须是字符串,SDK 不会隐式转换类型。
|
|
27
|
+
- Native 会去除首尾空白,当前最多展示前 40 个字符。
|
|
28
|
+
- 空字符串或全空白字符串表示清理标题。
|
|
29
|
+
- 状态栏隐藏时会保存标题,并在下次显示时应用。
|
|
30
|
+
|
|
31
|
+
### `glass3.statusBar.setVisible({ visible })`
|
|
32
|
+
|
|
33
|
+
申请显示或重新隐藏状态栏:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const result = await glass3.statusBar.setVisible({ visible: true });
|
|
37
|
+
// { updated: true, visible: true }
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`visible` 必须是 Boolean,字符串 `"true"` 和数字 `1` 均会被 SDK 拒绝。
|
|
41
|
+
|
|
42
|
+
建议先设置标题,再显示状态栏:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
await glass3.statusBar.setTitle({ title: '设备巡检' });
|
|
46
|
+
await glass3.statusBar.setVisible({ visible: true });
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
`visible: false` 会隐藏当前状态栏,但不会删除页面保存的标题。再次显示时,Native 会
|
|
50
|
+
恢复此前的标题。
|
|
51
|
+
|
|
52
|
+
## 返回、超时与错误
|
|
53
|
+
|
|
54
|
+
两个方法都直接返回 Host RPC `result`,并严格校验响应的 `channel`、`version`、
|
|
55
|
+
`kind` 和 `requestId`。每次调用的本地等待上限为 5 秒;`CALL_TIMEOUT` 只表示 SDK
|
|
56
|
+
停止等待,不能据此判断 Native 一定没有执行。
|
|
57
|
+
|
|
58
|
+
- 参数错误:`INVALID_PARAMS`,`stage: "params"`。
|
|
59
|
+
- fetch 失败:`TRANSPORT_ERROR`,`stage: "transport"`。
|
|
60
|
+
- HTTP 非 2xx:`TRANSPORT_REJECTED`,`stage: "transport"`。
|
|
61
|
+
- body 不是合法 JSON:`TRANSPORT_INVALID_RESPONSE`,`stage: "transport"`。
|
|
62
|
+
- Native RPC 错误:保留 Native `error.code`,`stage: "response"`。
|
|
63
|
+
- 响应信封不合法:`NATIVE_REQUEST_NOT_ACCEPTED`,`stage: "response"`。
|
|
64
|
+
|
|
65
|
+
## 生命周期
|
|
66
|
+
|
|
67
|
+
Native 按 AIX 页面 owner 保存和回收状态栏资源。页面进入后台、释放或进程结束时,
|
|
68
|
+
Native 会清理对其他页面可能产生的影响。页面可以主动调用
|
|
69
|
+
`setVisible({ visible: false })`,但不需要用全局 JavaScript 状态恢复资源。
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
<script def>
|
|
2
|
-
{
|
|
3
|
-
"component": true
|
|
4
|
-
}
|
|
5
|
-
</script>
|
|
6
|
-
|
|
7
|
-
<script setup>
|
|
8
|
-
import glass3 from 'rvis-aiui-kit';
|
|
9
|
-
|
|
10
|
-
function logError(error) {
|
|
11
|
-
if (error && error.code === 'CALL_DISPOSED') return;
|
|
12
|
-
console.error('[Status Bar]', error);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default {
|
|
16
|
-
properties: {
|
|
17
|
-
title: {
|
|
18
|
-
type: String,
|
|
19
|
-
value: ''
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
|
|
23
|
-
data: {
|
|
24
|
-
dateText: '',
|
|
25
|
-
timeText: '',
|
|
26
|
-
batteryText: '--%',
|
|
27
|
-
active: false,
|
|
28
|
-
clockTimer: null,
|
|
29
|
-
powerObserveRequestId: ''
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
methods: {
|
|
33
|
-
updateTime() {
|
|
34
|
-
const now = new Date();
|
|
35
|
-
const hours = now.getHours();
|
|
36
|
-
const minutes = now.getMinutes();
|
|
37
|
-
this.setData({
|
|
38
|
-
dateText: `${now.getMonth() + 1}/${now.getDate()}`,
|
|
39
|
-
timeText: `${hours < 10 ? '0' : ''}${hours}:${minutes < 10 ? '0' : ''}${minutes}`
|
|
40
|
-
});
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
updateBattery(event) {
|
|
44
|
-
if (!this.data.active) return;
|
|
45
|
-
if (event.eventType === 'invalidated') {
|
|
46
|
-
this.data.powerObserveRequestId = '';
|
|
47
|
-
this.setData({ batteryText: '--%' });
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const power = event.eventType === 'snapshot'
|
|
52
|
-
? event.snapshot && event.snapshot.power
|
|
53
|
-
: event.patch && event.patch.power;
|
|
54
|
-
if (!power) return;
|
|
55
|
-
|
|
56
|
-
this.setData({
|
|
57
|
-
batteryText: power.levelPercent === undefined
|
|
58
|
-
? '--%'
|
|
59
|
-
: `${power.levelPercent}%`
|
|
60
|
-
});
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
observePower() {
|
|
64
|
-
glass3.deviceContext.observe(
|
|
65
|
-
{ domains: ['power'] },
|
|
66
|
-
{ onEvent: event => this.updateBattery(event) }
|
|
67
|
-
).then(result => {
|
|
68
|
-
if (this.data.active) {
|
|
69
|
-
this.data.powerObserveRequestId = result.requestId;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
glass3.deviceContext.stopObserve({
|
|
74
|
-
requestId: result.requestId
|
|
75
|
-
}).catch(logError);
|
|
76
|
-
}).catch(logError);
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
lifetimes: {
|
|
81
|
-
attached() {
|
|
82
|
-
this.data.active = true;
|
|
83
|
-
this.updateTime();
|
|
84
|
-
this.data.clockTimer = setInterval(() => this.updateTime(), 1000);
|
|
85
|
-
this.observePower();
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
detached() {
|
|
89
|
-
this.data.active = false;
|
|
90
|
-
|
|
91
|
-
if (this.data.clockTimer !== null) {
|
|
92
|
-
clearInterval(this.data.clockTimer);
|
|
93
|
-
this.data.clockTimer = null;
|
|
94
|
-
}
|
|
95
|
-
if (this.data.powerObserveRequestId) {
|
|
96
|
-
glass3.deviceContext.stopObserve({
|
|
97
|
-
requestId: this.data.powerObserveRequestId
|
|
98
|
-
}).catch(logError);
|
|
99
|
-
this.data.powerObserveRequestId = '';
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
</script>
|
|
105
|
-
|
|
106
|
-
<page>
|
|
107
|
-
<view class="status-bar">
|
|
108
|
-
<text class="status-text">{{ title }}</text>
|
|
109
|
-
<view class="status-right">
|
|
110
|
-
<text class="status-text">{{ dateText }}</text>
|
|
111
|
-
<text class="status-text">{{ timeText }}</text>
|
|
112
|
-
<text class="status-text">{{ batteryText }}</text>
|
|
113
|
-
</view>
|
|
114
|
-
</view>
|
|
115
|
-
</page>
|
|
116
|
-
|
|
117
|
-
<style>
|
|
118
|
-
.status-bar {
|
|
119
|
-
display: flex;
|
|
120
|
-
flex-direction: row;
|
|
121
|
-
align-items: center;
|
|
122
|
-
justify-content: space-between;
|
|
123
|
-
width: 480px;
|
|
124
|
-
height: 20px;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
.status-right {
|
|
128
|
-
display: flex;
|
|
129
|
-
flex-direction: row;
|
|
130
|
-
align-items: center;
|
|
131
|
-
gap: 10px;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
.status-text {
|
|
135
|
-
color: #40ff5e;
|
|
136
|
-
font-size: 14px;
|
|
137
|
-
line-height: 20px;
|
|
138
|
-
}
|
|
139
|
-
</style>
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# Status Bar 组件
|
|
2
|
-
|
|
3
|
-
标准 Glass3 HUD 状态栏,左侧显示应用标题,右侧显示本地月/日、时间和实时电量。
|
|
4
|
-
|
|
5
|
-
组件会在 `attached` 时启动时钟和 `power` 订阅,在 `detached` 时清理计时器并停止订阅。
|
|
6
|
-
|
|
7
|
-
## 注册与使用
|
|
8
|
-
|
|
9
|
-
```json
|
|
10
|
-
{
|
|
11
|
-
"usingComponents": {
|
|
12
|
-
"rvis-status-bar": "rvis-aiui-kit/status-bar"
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
```html
|
|
18
|
-
<rvis-status-bar title="应用名称"></rvis-status-bar>
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
属性:
|
|
22
|
-
|
|
23
|
-
- `title: String`,默认 `''`,显示在状态栏左侧。
|
|
24
|
-
|
|
25
|
-
组件固定为 `480px × 20px`,使用 `14px / 20px` 的绿色文本;页面负责将它放在全屏绘制区域的 `(0, 0)`。
|
|
26
|
-
|
|
27
|
-
## Host 消息转发
|
|
28
|
-
|
|
29
|
-
Ink 当前只把 Host 消息交给 Page,不会直接调用自定义组件的 `onMessage`。使用该组件的页面必须把消息转发到与组件共享的 `glass3` 单例:
|
|
30
|
-
|
|
31
|
-
```js
|
|
32
|
-
import glass3 from 'rvis-aiui-kit';
|
|
33
|
-
|
|
34
|
-
export default {
|
|
35
|
-
onMessage(messageEvent) {
|
|
36
|
-
glass3.handleMessage(messageEvent);
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
onUnload() {
|
|
40
|
-
glass3.dispose();
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
除这条 Page 级消息入口外,时间刷新、电量显示、订阅和清理逻辑均由组件管理。电量不可读时显示 `--%`。
|