deveco_hmigbot 0.21.5
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/LICENSE +26 -0
- package/README.md +50 -0
- package/agents/hmigbot-worker.md +61 -0
- package/agents/hmigbot.md +22 -0
- package/agents/workflow-subagent.md +55 -0
- package/commands/hmigbot.md +17 -0
- package/dist/index.js +1 -0
- package/manifest.json +19 -0
- package/package.json +29 -0
- package/skills/migrate-core/FILES.md +26 -0
- package/skills/migrate-core/SKILL.md +484 -0
- package/skills/migrate-core/references/README.md +64 -0
- package/skills/migrate-core/references/flow/arkts-vector-gate.md +55 -0
- package/skills/migrate-core/references/flow/build-error-patterns.md +52 -0
- package/skills/migrate-core/references/flow/conventions-template.md +244 -0
- package/skills/migrate-core/references/flow/navigation-migration.md +42 -0
- package/skills/migrate-core/references/flow/platform-api-guards.md +59 -0
- package/skills/migrate-core/references/flow/platform-model-gaps.md +43 -0
- package/skills/migrate-core/references/flow/resource-conversion.md +46 -0
- package/skills/migrate-core/references/flow/ui-layout-semantics.md +124 -0
- package/skills/migrate-core/references/flow/unit-breakdown.md +42 -0
- package/skills/migrate-core/references/host-capabilities.md +24 -0
- package/skills/migrate-core/references/topics/app-identity.md +214 -0
- package/skills/migrate-core/references/topics/env-doctor.md +245 -0
- package/skills/migrate-core/references/topics/i18n/README.md +458 -0
- package/skills/migrate-core/references/topics/i18n/references/code-examples.md +304 -0
- package/skills/migrate-core/references/topics/i18n/references/common-pitfalls.md +354 -0
- package/skills/migrate-core/references/topics/i18n/references/dynamic-language-switch.md +464 -0
- package/skills/migrate-core/references/topics/i18n/references/language-codes.md +104 -0
- package/skills/migrate-core/references/topics/icon-sizing.md +98 -0
- package/skills/migrate-core/references/topics/library-migration/README.md +234 -0
- package/skills/migrate-core/references/topics/library-migration/closed-source-sdk.md +128 -0
- package/skills/migrate-core/references/topics/library-migration/download-api-decision.md +84 -0
- package/skills/migrate-core/references/topics/library-migration/library-mapping-table.md +100 -0
- package/skills/migrate-core/references/topics/library-migration/napi-compile-guide.md +84 -0
- package/skills/migrate-core/references/topics/library-migration/ohpm-search-guide.md +73 -0
- package/skills/migrate-core/references/topics/library-migration/stdlib-mapping-table.md +34 -0
- package/skills/migrate-core/references/topics/resources/aar-decompile.md +25 -0
- package/skills/migrate-core/references/topics/resources/conversion-rules.md +625 -0
- package/skills/migrate-core/references/topics/resources/dependency-analysis-rules.md +328 -0
- package/skills/migrate-core/references/topics/resources/material-design-icons.md +173 -0
- package/skills/migrate-core/references/topics/resources/svg-fix-patterns.md +175 -0
- package/skills/migrate-core/references/topics/resources/xml-drawable-to-svg-rules.md +513 -0
- package/skills/migrate-core/references/topics/system-capabilities/README.md +331 -0
- package/skills/migrate-core/references/topics/system-capabilities/avplayer-guide.md +161 -0
- package/skills/migrate-core/references/topics/system-capabilities/background-tasks.md +403 -0
- package/skills/migrate-core/references/topics/system-capabilities/browser-intent.md +121 -0
- package/skills/migrate-core/references/topics/system-capabilities/camera-picker.md +118 -0
- package/skills/migrate-core/references/topics/system-capabilities/document-picker.md +246 -0
- package/skills/migrate-core/references/topics/system-capabilities/file-utils.md +131 -0
- package/skills/migrate-core/references/topics/system-capabilities/permission-helper.md +112 -0
- package/skills/migrate-core/references/topics/system-capabilities/photo-access-helper.md +208 -0
- package/skills/migrate-core/references/topics/system-capabilities/print-management.md +213 -0
- package/skills/migrate-core/references/topics/system-capabilities/share-panel.md +177 -0
- package/skills/migrate-core/references/topics/system-capabilities/system-settings.md +322 -0
- package/skills/migrate-core/references/topics/system-capabilities/telephony-dial.md +49 -0
- package/skills/migrate-core/references/topics/system-capabilities/video-playback.md +42 -0
- package/skills/migrate-core/references/topics/system-capabilities/webview-patterns.md +38 -0
- package/skills/migrate-core/references/topics/ui-alignment/README.md +344 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/dark-mode.md +47 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/layout-mapping.md +301 -0
- package/skills/migrate-core/references/topics/ui-alignment/references/visual-patterns.md +411 -0
- package/skills/migrate-core/scripts/closure/check-anchors.mjs +186 -0
- package/skills/migrate-core/scripts/closure/check-api-guards.mjs +175 -0
- package/skills/migrate-core/scripts/closure/check-consumers.mjs +301 -0
- package/skills/migrate-core/scripts/closure/check-permissions.mjs +165 -0
- package/skills/migrate-core/scripts/closure/check-resources.mjs +130 -0
- package/skills/migrate-core/scripts/closure/check-routes.mjs +527 -0
- package/skills/migrate-core/scripts/closure/check-safearea.mjs +122 -0
- package/skills/migrate-core/scripts/closure/check-stubs.mjs +69 -0
- package/skills/migrate-core/scripts/closure/closure-suite.mjs +256 -0
- package/skills/migrate-core/scripts/closure/idioms.json +105 -0
- package/skills/migrate-core/scripts/convert/convert-resources.mjs +437 -0
- package/skills/migrate-core/scripts/feasibility/feasibility.mjs +235 -0
- package/skills/migrate-core/scripts/feasibility/tables/cross-platform.json +11 -0
- package/skills/migrate-core/scripts/feasibility/tables/deprecated-api.json +10 -0
- package/skills/migrate-core/scripts/feasibility/tables/imported-arkts-core.json +425 -0
- package/skills/migrate-core/scripts/feasibility/tables/lib-equivalence.json +206 -0
- package/skills/migrate-core/scripts/feasibility/tables/system-capabilities.json +22 -0
- package/skills/migrate-core/scripts/front.mjs +107 -0
- package/skills/migrate-core/scripts/interface/ark-extract.mjs +172 -0
- package/skills/migrate-core/scripts/interface/interface.mjs +152 -0
- package/skills/migrate-core/scripts/ledger/ledger.mjs +383 -0
- package/skills/migrate-core/scripts/ledger/parse-cards.mjs +98 -0
- package/skills/migrate-core/scripts/lib/literals.mjs +37 -0
- package/skills/migrate-core/scripts/lib/scan.mjs +315 -0
- package/skills/migrate-core/scripts/smoke/align-sdk.mjs +118 -0
- package/skills/migrate-core/scripts/smoke/ensure-sign.mjs +53 -0
- package/skills/migrate-core/scripts/smoke/smoke.mjs +238 -0
- package/skills/migrate-core/scripts/smoke/verdict.mjs +31 -0
- package/skills/migrate-core/scripts/smoke/walk.mjs +480 -0
- package/skills/migrate-core/scripts/transpile/mapping.json +76 -0
- package/skills/migrate-core/scripts/transpile/transpile-layout.mjs +404 -0
- package/skills/migrate-core/scripts/vectors/run-arkts-vectors.mjs +107 -0
- package/skills/migrate-core/scripts/vectors/setup-arkts-test.mjs +90 -0
- package/skills/migrate-core/scripts/wire/extractors.mjs +258 -0
- package/skills/migrate-core/scripts/wire/wire-routes.mjs +507 -0
- package/skills/migrate-core/templates/acceptance.js +365 -0
- package/skills/migrate-core/templates/explore.js +86 -0
- package/skills/migrate-core/templates/implement.js +211 -0
- package/skills/migrate-core/templates/mig_slices.js +491 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# 系统打印管理
|
|
2
|
+
|
|
3
|
+
> ⚠ 本文样例中 `getContext(this)` 为已废弃写法:组件内一律改 `this.getUIContext().getHostContext()`;Ability 内用 `this.context`。
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
> 使用 `@kit.BasicServicesKit` 的 `print` 模块拉起系统打印服务,替代 Android 的 `WebView.print()` 和 `PrintDocumentAdapter`。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 基本导入
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { print } from '@kit.BasicServicesKit';
|
|
14
|
+
import { common } from '@kit.AbilityKit';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 打印当前 Web 页面
|
|
20
|
+
|
|
21
|
+
> 对应 Android:`WebView.print()`
|
|
22
|
+
|
|
23
|
+
### 基础用法
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { print } from '@kit.BasicServicesKit';
|
|
27
|
+
|
|
28
|
+
function printWebPage(context: common.UIAbilityContext, webController: WebviewController, jobName: string): void {
|
|
29
|
+
// 创建 Web 打印文档适配器
|
|
30
|
+
const adapter = webController.createWebPrintDocumentAdapter(jobName);
|
|
31
|
+
|
|
32
|
+
// 发起打印
|
|
33
|
+
print.print(jobName, adapter, null, context)
|
|
34
|
+
.then(() => {
|
|
35
|
+
hilog.info(DOMAIN, TAG, `Print job ${jobName} sent successfully`);
|
|
36
|
+
})
|
|
37
|
+
.catch((err: Error) => {
|
|
38
|
+
hilog.error(DOMAIN, TAG, `Print failed: ${err.message}`);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 在 BrowserPage 中的调用示例
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { print } from '@kit.BasicServicesKit';
|
|
47
|
+
import { promptAction } from '@kit.ArkUI';
|
|
48
|
+
|
|
49
|
+
savePdf(): void {
|
|
50
|
+
const adapter = this.webController.createWebPrintDocumentAdapter('savePdf');
|
|
51
|
+
print.print('printJob', adapter, null, getContext(this))
|
|
52
|
+
.then(() => {
|
|
53
|
+
promptAction.showToast({ message: '已发送至打印服务', duration: 2000 });
|
|
54
|
+
})
|
|
55
|
+
.catch((err: Error) => {
|
|
56
|
+
promptAction.showToast({ message: '打印失败: ' + err.message, duration: 2000 });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 打印自定义内容(PrintDocumentAdapter)
|
|
64
|
+
|
|
65
|
+
> 用于打印非 Web 内容,如自定义绘制的页面、生成的 PDF 等
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { print, BusinessError } from '@kit.BasicServicesKit'
|
|
69
|
+
import { common } from '@kit.AbilityKit'
|
|
70
|
+
import { fileIo as fs } from '@kit.CoreFileKit'
|
|
71
|
+
|
|
72
|
+
// PrintDocumentAdapter 是接口(不是类),只有两个回调:
|
|
73
|
+
// onStartLayoutWrite —— 系统给出目标 fd,把要打印的内容(PDF)写进去,再回调文件生成状态
|
|
74
|
+
// onJobStateChanged —— 任务状态变化(PREVIEW_DESTROY / PRINT_TASK_SUCCEED / _FAIL / _CANCEL / _BLOCK)
|
|
75
|
+
class PdfPrintAdapter implements print.PrintDocumentAdapter {
|
|
76
|
+
private srcPdfPath: string
|
|
77
|
+
constructor(srcPdfPath: string) { this.srcPdfPath = srcPdfPath }
|
|
78
|
+
|
|
79
|
+
onStartLayoutWrite(jobId: string, oldAttrs: print.PrintAttributes, newAttrs: print.PrintAttributes, fd: number,
|
|
80
|
+
writeResultCallback: (jobId: string, writeResult: print.PrintFileCreationState) => void): void {
|
|
81
|
+
try {
|
|
82
|
+
const src = fs.openSync(this.srcPdfPath, fs.OpenMode.READ_ONLY)
|
|
83
|
+
fs.copyFileSync(src.fd, fd)
|
|
84
|
+
fs.closeSync(src)
|
|
85
|
+
writeResultCallback(jobId, print.PrintFileCreationState.PRINT_FILE_CREATED)
|
|
86
|
+
} catch (e) {
|
|
87
|
+
writeResultCallback(jobId, print.PrintFileCreationState.PRINT_FILE_CREATION_FAILED)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onJobStateChanged(jobId: string, state: print.PrintDocumentAdapterState): void {
|
|
92
|
+
if (state === print.PrintDocumentAdapterState.PRINT_TASK_FAIL) { /* 给用户可见提示 */ }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function startCustomPrint(context: common.UIAbilityContext, pdfPath: string): Promise<void> {
|
|
97
|
+
const attrs: print.PrintAttributes = {
|
|
98
|
+
copyNumber: 1,
|
|
99
|
+
directionMode: print.PrintDirectionMode.DIRECTION_MODE_PORTRAIT,
|
|
100
|
+
colorMode: print.PrintColorMode.COLOR_MODE_COLOR,
|
|
101
|
+
duplexMode: print.PrintDuplexMode.DUPLEX_MODE_NONE,
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const task: print.PrintTask = await print.print('CustomPrintJob', new PdfPrintAdapter(pdfPath), attrs, context)
|
|
105
|
+
task.on('succeed', () => { /* 完成 */ })
|
|
106
|
+
task.on('fail', () => { /* 失败:可见提示 */ })
|
|
107
|
+
} catch (e) {
|
|
108
|
+
const err = e as BusinessError // 201 未声明 ohos.permission.PRINT / 401 参数错
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## print.print() 参数说明
|
|
116
|
+
|
|
117
|
+
| 参数 | 类型 | 说明 |
|
|
118
|
+
|------|------|------|
|
|
119
|
+
| `jobName` | `string` | 打印任务名称,显示在系统打印管理器中 |
|
|
120
|
+
| `printAdapter` | `PrintDocumentAdapter` | 实现 `onStartLayoutWrite` + `onJobStateChanged` 的对象 |
|
|
121
|
+
| `printAttributes` | `PrintAttributes` | 打印属性(份数/页范围/纸张/方向/色彩/双面),必填对象,字段均可选 |
|
|
122
|
+
| `context` | `Context` | Ability 上下文 |
|
|
123
|
+
| 返回 | `Promise<PrintTask>` | `task.on('succeed' \| 'fail' \| 'cancel' \| 'block', cb)` 监听结果 |
|
|
124
|
+
|
|
125
|
+
另一种形态 `print.print(files: string[], context)`:直接打印文件路径列表(只支持 PDF 与图片)。
|
|
126
|
+
|
|
127
|
+
### PrintAttributes 常用配置
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const attributes: print.PrintAttributes = {
|
|
131
|
+
copyNumber: 1,
|
|
132
|
+
pageRange: { startPage: 0, endPage: 0 }, // PrintPageRange
|
|
133
|
+
pageSize: print.PrintPageType.PAGE_ISO_A4, // 或 PrintPageSize { id, name, width, height }
|
|
134
|
+
directionMode: print.PrintDirectionMode.DIRECTION_MODE_PORTRAIT, // AUTO / PORTRAIT / LANDSCAPE
|
|
135
|
+
colorMode: print.PrintColorMode.COLOR_MODE_COLOR, // MONOCHROME / COLOR
|
|
136
|
+
duplexMode: print.PrintDuplexMode.DUPLEX_MODE_NONE, // NONE / LONG_EDGE / SHORT_EDGE
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 状态枚举
|
|
141
|
+
|
|
142
|
+
| 枚举 | 值 |
|
|
143
|
+
|----|------|
|
|
144
|
+
| `PrintFileCreationState`(onStartLayoutWrite 回调) | `PRINT_FILE_CREATED` / `PRINT_FILE_CREATION_FAILED` / `PRINT_FILE_CREATED_UNRENDERED` |
|
|
145
|
+
| `PrintDocumentAdapterState`(onJobStateChanged) | `PREVIEW_DESTROY` / `PRINT_TASK_SUCCEED` / `PRINT_TASK_FAIL` / `PRINT_TASK_CANCEL` / `PRINT_TASK_BLOCK` |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## module.json5 配置
|
|
150
|
+
|
|
151
|
+
`print.print()` 与适配器回调都标注 `@permission ohos.permission.PRINT`(normal 级、system_grant:声明即自动授权,不用运行时请求);未声明抛 201。
|
|
152
|
+
|
|
153
|
+
```json5
|
|
154
|
+
{
|
|
155
|
+
"requestPermissions": [
|
|
156
|
+
{ "name": "ohos.permission.PRINT" }
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 常见错误
|
|
164
|
+
|
|
165
|
+
### 错误 1:Web 打印适配器未创建
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// 错误 — webController 可能为 null
|
|
169
|
+
const adapter = this.webController.createWebPrintDocumentAdapter('job');
|
|
170
|
+
|
|
171
|
+
// 正确 — 确保 webController 已初始化
|
|
172
|
+
if (this.webController) {
|
|
173
|
+
const adapter = this.webController.createWebPrintDocumentAdapter('job');
|
|
174
|
+
print.print('job', adapter, null, getContext(this));
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 错误 2:未处理 print.print() 的 Promise rejection
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// 错误 — 可能静默失败
|
|
182
|
+
print.print(jobName, adapter, null, context);
|
|
183
|
+
|
|
184
|
+
// 正确 — 必须处理错误
|
|
185
|
+
print.print(jobName, adapter, null, context)
|
|
186
|
+
.catch((err: Error) => {
|
|
187
|
+
hilog.error(DOMAIN, TAG, `Print failed: ${err.message}`);
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Android 对应关系
|
|
194
|
+
|
|
195
|
+
| Android | HarmonyOS |
|
|
196
|
+
|---------|-----------|
|
|
197
|
+
| `WebView.print()` | `webController.createWebPrintDocumentAdapter()` + `print.print()` |
|
|
198
|
+
| `PrintDocumentAdapter` | `class CustomPrintDocumentAdapter extends print.PrintDocumentAdapter` |
|
|
199
|
+
| `PrintManager.print()` | `print.print()` |
|
|
200
|
+
| `PrintAttributes` | `print.PrintAttributes` |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 与 documentPicker.save() 的区别
|
|
205
|
+
|
|
206
|
+
| 维度 | 打印 (`print.print`) | 保存文件 (`DocumentViewPicker.save`) |
|
|
207
|
+
|------|---------------------|-------------------------------------|
|
|
208
|
+
| 用途 | 发送给打印服务 | 保存到文件系统 |
|
|
209
|
+
| 输出 | 纸质打印 / 虚拟 PDF | 文件(.mht / .pdf 等) |
|
|
210
|
+
| 用户操作 | 选择打印机、份数 | 选择保存位置和文件名 |
|
|
211
|
+
| Web 内容 | ✅ `WebPrintDocumentAdapter` | ❌ 不支持 |
|
|
212
|
+
| 自定义内容 | ✅ `PrintDocumentAdapter` | ❌ 不支持 |
|
|
213
|
+
| module.json5 配置 | 通常不需要 | 不需要 |
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# 分享面板 ShareKit
|
|
2
|
+
|
|
3
|
+
> 使用 `@kit.ShareKit` 拉起系统分享面板,替代 Android 的 `Intent.ACTION_SEND`。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 基本导入
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import { systemShare } from '@kit.ShareKit'
|
|
11
|
+
import { uniformTypeDescriptor as utd } from '@kit.ArkData'
|
|
12
|
+
import { common } from '@kit.AbilityKit'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 完整调用模板
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
async function shareMedia(
|
|
21
|
+
context: common.UIAbilityContext,
|
|
22
|
+
uri: string,
|
|
23
|
+
title: string,
|
|
24
|
+
mimeType: string
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
// 1. 求该文件自己的 UTD 类型 ID(按后缀推断,不要硬塞 belongsTo)
|
|
27
|
+
const ext = uri.substring(uri.lastIndexOf('.') + 1).toLowerCase()
|
|
28
|
+
const utdTypeId = utd.getUniformDataTypeByFilenameExtension('.' + ext)
|
|
29
|
+
|
|
30
|
+
// 2. 构建分享数据
|
|
31
|
+
const shareData: systemShare.SharedData = new systemShare.SharedData({
|
|
32
|
+
utd: utdTypeId,
|
|
33
|
+
uri: uri,
|
|
34
|
+
title: title,
|
|
35
|
+
description: '分享 ' + title
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// 3. 创建分享控制器
|
|
39
|
+
const controller: systemShare.ShareController = new systemShare.ShareController(shareData)
|
|
40
|
+
|
|
41
|
+
// 4. 展示分享面板
|
|
42
|
+
controller.show(context, {
|
|
43
|
+
selectionMode: systemShare.SelectionMode.SINGLE,
|
|
44
|
+
previewMode: systemShare.SharePreviewMode.DETAIL
|
|
45
|
+
}).then(() => {
|
|
46
|
+
hilog.info(DOMAIN, TAG, 'ShareController show success')
|
|
47
|
+
}).catch((error: BusinessError) => {
|
|
48
|
+
hilog.error(DOMAIN, TAG, `ShareController show error: ${error.code}, ${error.message}`)
|
|
49
|
+
promptAction.showToast({ message: '分享失败' })
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## MIME 类型判断
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
private getMimeType(medium: Medium): string {
|
|
60
|
+
if ((medium.type & TYPE_VIDEOS) !== 0) {
|
|
61
|
+
return 'video/*'
|
|
62
|
+
}
|
|
63
|
+
if ((medium.type & TYPE_IMAGES) !== 0) {
|
|
64
|
+
return 'image/*'
|
|
65
|
+
}
|
|
66
|
+
return '*/*'
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## SharePreviewMode 配置
|
|
73
|
+
|
|
74
|
+
| 模式 | 效果 |
|
|
75
|
+
|------|------|
|
|
76
|
+
| `DETAIL` | 显示详情预览(推荐) |
|
|
77
|
+
| `NONE` | 不显示预览 |
|
|
78
|
+
|
|
79
|
+
## SelectionMode 配置
|
|
80
|
+
|
|
81
|
+
`SelectionMode` 只有两个成员(`@kit.ShareKit`,编译校验过)。**没有 `MULTIPLE`**——写 `SelectionMode.MULTIPLE` 会编译失败:`Property 'MULTIPLE' does not exist on type 'typeof SelectionMode'`。
|
|
82
|
+
|
|
83
|
+
| 模式 | 值 | 效果 |
|
|
84
|
+
|------|----|------|
|
|
85
|
+
| `SINGLE` | 0 | 单选模式:传入多条记录时,用户多选一进行分享(缺省值) |
|
|
86
|
+
| `BATCH` | 1 | 批量模式:分享全部数据记录 |
|
|
87
|
+
|
|
88
|
+
> 一次分享多条记录、想让目标应用收到全部记录时,用 `selectionMode: systemShare.SelectionMode.BATCH`。
|
|
89
|
+
> ⚠️ BATCH 批量模式只支持文件类型记录(每条 `SharedRecord` 走 `uri` 的 File 类型);混入纯文本/链接等非文件记录会触发错误码 `1003702001`。
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## module.json5 配置
|
|
94
|
+
|
|
95
|
+
### 配置规则
|
|
96
|
+
|
|
97
|
+
| 分享内容类型 | 是否需要配置 | 说明 |
|
|
98
|
+
|------------|------------|------|
|
|
99
|
+
| 图片/视频/文件 | 通常不需要 | 系统已内置支持 |
|
|
100
|
+
| 纯文本 | 需要配置 | 需声明 `ohos.want.action.send` |
|
|
101
|
+
| 自定义数据 | 需要配置 | 需声明 `ohos.want.action.send` |
|
|
102
|
+
|
|
103
|
+
### 配置示例(纯文本/自定义数据)
|
|
104
|
+
|
|
105
|
+
```json5
|
|
106
|
+
{
|
|
107
|
+
"skills": [{
|
|
108
|
+
"entities": ["entity.system.home"],
|
|
109
|
+
"actions": [
|
|
110
|
+
"ohos.want.action.home",
|
|
111
|
+
"ohos.want.action.send" // 发送数据
|
|
112
|
+
]
|
|
113
|
+
}]
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
> ⚠️ **注意**:虽然图片/视频分享通常不需要额外声明,但某些设备可能需要。如遇分享失败,建议添加 `ohos.want.action.send` 配置。
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 常见错误
|
|
122
|
+
|
|
123
|
+
### 错误 1:不转换 UTD 直接传 MIME
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// 错误 — 部分设备不支持
|
|
127
|
+
const shareData = new systemShare.SharedData({
|
|
128
|
+
mimeType: 'image/*', // ❌
|
|
129
|
+
uri: uri,
|
|
130
|
+
title: title
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// 正确 — 使用 UTD(按后缀推断,不传 belongsTo)
|
|
134
|
+
const utdTypeId = utd.getUniformDataTypeByFilenameExtension('.' + ext)
|
|
135
|
+
const shareData = new systemShare.SharedData({
|
|
136
|
+
utd: utdTypeId,
|
|
137
|
+
uri: uri,
|
|
138
|
+
title: title
|
|
139
|
+
})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 错误 2:show() 后不 await
|
|
143
|
+
|
|
144
|
+
`controller.show()` 是异步的,但不会阻塞 UI。不需要 await,但错误需要通过 catch 处理。
|
|
145
|
+
|
|
146
|
+
### 错误 3:给每个文件的 `getUniformDataTypeByFilenameExtension` 硬塞第二参 IMAGE
|
|
147
|
+
|
|
148
|
+
签名是 `getUniformDataTypeByFilenameExtension(filenameExtension: string, belongsTo?: string): string`——**第二参 `belongsTo` 可选**(归属类型 ID,无默认值)。按后缀推断类型时**只传后缀、不传第二参**;否则给 `.pdf` 也塞 `utd.UniformDataType.IMAGE` 会把它误标成图片。
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// 错误 — 所有扩展名都硬塞 IMAGE,.pdf / .txt 被误标
|
|
152
|
+
const t = utd.getUniformDataTypeByFilenameExtension('.' + ext, utd.UniformDataType.IMAGE) // ❌
|
|
153
|
+
|
|
154
|
+
// 正确 A — 按后缀推断:只传一个后缀实参
|
|
155
|
+
const t = utd.getUniformDataTypeByFilenameExtension('.' + ext)
|
|
156
|
+
|
|
157
|
+
// 正确 B — 类型已知时直接用字面 UTD 枚举(更精准,无需查后缀)
|
|
158
|
+
const t = utd.UniformDataType.PNG // 或 JPEG / PDF / IMAGE / TEXT / PLAIN_TEXT ...
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> `SharedRecord` 用 `utd`(string)承载类型、`uri` 承载文件位置、文本/链接用 `content`;**没有 `mimeType` 字段**。`content` 与 `uri` 至少一个非空。
|
|
162
|
+
|
|
163
|
+
### 多条记录批量分享
|
|
164
|
+
|
|
165
|
+
一个 `SharedData` 至少含一条记录。多条时:用首条 `new systemShare.SharedData(firstRecord)`,其余用实例方法 `sharedData.addRecord(record)` 逐条追加——**构造器只收单条 `SharedRecord`,不能把数组塞进构造器,也不要为每个文件各建一个 `SharedData`/`ShareController`**。每条记录的 `utd` 按它自己的扩展名求得。
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
const shareData = new systemShare.SharedData(records[0])
|
|
169
|
+
for (let i = 1; i < records.length; i++) {
|
|
170
|
+
shareData.addRecord(records[i])
|
|
171
|
+
}
|
|
172
|
+
const controller = new systemShare.ShareController(shareData)
|
|
173
|
+
controller.show(context, {
|
|
174
|
+
selectionMode: systemShare.SelectionMode.BATCH, // 全部记录一起分享
|
|
175
|
+
previewMode: systemShare.SharePreviewMode.DETAIL
|
|
176
|
+
}).then(() => {}).catch((e: BusinessError) => {})
|
|
177
|
+
```
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# 系统设置跳转
|
|
2
|
+
|
|
3
|
+
> ⚠ 设备相关总标注:文内 com.huawei.settings/MainAbility 等设置页包名与 uri 为特定 ROM 实测值,随品牌/版本可变——使用前按目标设备验证,失败走通用 want 降级。
|
|
4
|
+
|
|
5
|
+
> ⚠ 本文样例中 `getContext(this)` 为已废弃写法:组件内一律改 `this.getUIContext().getHostContext()`;Ability 内用 `this.context`。
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
> 使用显式 Want + startAbility 跳转到系统设置页面,替代 Android 的 Settings.ACTION_APPLICATION_DETAILS_SETTINGS。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 基本导入
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { common, Want } from '@kit.AbilityKit'
|
|
16
|
+
import { hilog } from '@kit.PerformanceAnalysisKit'
|
|
17
|
+
import { BusinessError } from '@kit.BasicServicesKit'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 跳转到应用权限设置页
|
|
23
|
+
|
|
24
|
+
> ✅ **验证状态**:已在 PermissionHelper.ets:168-182 中验证
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
function openAppSettings(context: common.UIAbilityContext): void {
|
|
28
|
+
try {
|
|
29
|
+
const params: Record<string, string> = { 'page': 'permission' }
|
|
30
|
+
const want: Want = {
|
|
31
|
+
bundleName: 'com.huawei.settings', // 华为系统设置包名
|
|
32
|
+
abilityName: 'com.huawei.settings.MainAbility', // 主 Ability
|
|
33
|
+
action: 'action.settings.app.info', // 应用信息页
|
|
34
|
+
parameters: params // 传递参数
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
context.startAbility(want)
|
|
38
|
+
.then(() => {
|
|
39
|
+
hilog.info(DOMAIN, TAG, 'Opened app settings')
|
|
40
|
+
})
|
|
41
|
+
.catch((err: Error) => {
|
|
42
|
+
hilog.error(DOMAIN, TAG, `Failed to open settings: ${err.message}`)
|
|
43
|
+
})
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const error = err as BusinessError
|
|
46
|
+
hilog.error(DOMAIN, TAG, `openAppSettings error: ${error.message}`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 封装为工具方法
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// common/PermissionHelper.ets
|
|
57
|
+
export class PermissionHelper {
|
|
58
|
+
private context: common.UIAbilityContext
|
|
59
|
+
|
|
60
|
+
constructor(context: common.UIAbilityContext) {
|
|
61
|
+
this.context = context
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
openAppSettings(): void {
|
|
65
|
+
try {
|
|
66
|
+
const params: Record<string, string> = { 'page': 'permission' }
|
|
67
|
+
const want: Want = {
|
|
68
|
+
bundleName: 'com.huawei.settings',
|
|
69
|
+
abilityName: 'com.huawei.settings.MainAbility',
|
|
70
|
+
action: 'action.settings.app.info',
|
|
71
|
+
parameters: params
|
|
72
|
+
}
|
|
73
|
+
this.context.startAbility(want)
|
|
74
|
+
} catch (err) {
|
|
75
|
+
const error = err as BusinessError
|
|
76
|
+
hilog.error(DOMAIN, TAG, `openAppSettings error: ${error.message}`)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 常见系统设置页面
|
|
85
|
+
|
|
86
|
+
| Action | 用途 | 说明 |
|
|
87
|
+
|--------|------|------|
|
|
88
|
+
| `action.settings.app.info` | 应用信息页 | 权限管理、存储占用等 ✅ 已验证 |
|
|
89
|
+
|
|
90
|
+
> 其它设置页(WiFi/蓝牙/定位)的入口未核实,不写;需要时查官方文档再加,不猜 action 名。
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 显式 vs 隐式 Want
|
|
95
|
+
|
|
96
|
+
### 显式 Want(系统设置跳转使用)
|
|
97
|
+
```typescript
|
|
98
|
+
const want: Want = {
|
|
99
|
+
bundleName: 'com.huawei.settings', // 明确指定目标应用
|
|
100
|
+
abilityName: 'com.huawei.settings.MainAbility',
|
|
101
|
+
action: 'action.settings.app.info'
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
**特点**:
|
|
105
|
+
- 精确跳转到指定应用
|
|
106
|
+
- 适用于系统应用跳转
|
|
107
|
+
- 不需要在 module.json5 中声明 skills
|
|
108
|
+
|
|
109
|
+
### 隐式 Want(浏览器/分享使用)
|
|
110
|
+
```typescript
|
|
111
|
+
const want: Want = {
|
|
112
|
+
action: 'ohos.want.action.viewData', // 只指定 action
|
|
113
|
+
uri: 'https://example.com' // 让系统匹配
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
**特点**:
|
|
117
|
+
- 让系统选择合适的应用
|
|
118
|
+
- 适用于第三方应用跳转
|
|
119
|
+
- 可能需要在 module.json5 中声明 skills
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## 使用场景
|
|
124
|
+
|
|
125
|
+
### 何时跳转到系统设置?
|
|
126
|
+
|
|
127
|
+
1. ✅ 用户拒绝了关键权限,需要手动开启
|
|
128
|
+
2. ✅ 需要用户配置系统级设置(WiFi、蓝牙等)
|
|
129
|
+
3. ✅ 应用出现异常,引导用户检查权限
|
|
130
|
+
|
|
131
|
+
### 最佳实践:权限拒绝后引导
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { promptAction } from '@kit.ArkUI'
|
|
135
|
+
|
|
136
|
+
async function requestPermissionWithFallback(
|
|
137
|
+
context: common.UIAbilityContext
|
|
138
|
+
): Promise<void> {
|
|
139
|
+
const atManager = abilityAccessCtrl.createAtManager()
|
|
140
|
+
const permissions: Permissions[] = ['ohos.permission.CAMERA']
|
|
141
|
+
|
|
142
|
+
const result = await atManager.requestPermissionsFromUser(context, permissions)
|
|
143
|
+
|
|
144
|
+
// 检查是否有权限被拒绝
|
|
145
|
+
const denied = result.authResults.some(r => r !== 0)
|
|
146
|
+
|
|
147
|
+
if (denied) {
|
|
148
|
+
// 用户拒绝了权限,引导到设置页面
|
|
149
|
+
promptAction.showDialog({
|
|
150
|
+
title: '需要权限',
|
|
151
|
+
message: '请在设置中开启相关权限以使用完整功能',
|
|
152
|
+
buttons: [
|
|
153
|
+
{ text: '取消', color: '#999999' },
|
|
154
|
+
{ text: '去设置', color: '#007DFF' }
|
|
155
|
+
]
|
|
156
|
+
}).then((result: promptAction.ShowDialogSuccessResponse) => {
|
|
157
|
+
if (result.index === 1) {
|
|
158
|
+
openAppSettings(context) // 跳转到设置
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 完整示例:在页面中使用
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
// pages/Index.ets(V2,API 12+)
|
|
169
|
+
@ComponentV2
|
|
170
|
+
struct Index {
|
|
171
|
+
private permissionHelper: PermissionHelper | null = null
|
|
172
|
+
@Local permissionDenied: boolean = false
|
|
173
|
+
|
|
174
|
+
aboutToAppear(): void {
|
|
175
|
+
const context = getContext(this) as common.UIAbilityContext
|
|
176
|
+
this.permissionHelper = PermissionHelper.getInstance(context)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
build() {
|
|
180
|
+
Column() {
|
|
181
|
+
// 权限错误提示
|
|
182
|
+
if (this.permissionDenied) {
|
|
183
|
+
this.buildPermissionErrorUI()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@Builder
|
|
189
|
+
buildPermissionErrorUI() {
|
|
190
|
+
Column() {
|
|
191
|
+
Text('权限被拒绝')
|
|
192
|
+
.fontSize(20)
|
|
193
|
+
.fontWeight(FontWeight.Bold)
|
|
194
|
+
|
|
195
|
+
Text('请在设置中开启相关权限')
|
|
196
|
+
.fontSize(14)
|
|
197
|
+
.margin({ top: 16 })
|
|
198
|
+
|
|
199
|
+
Button($r('app.string.permission_open_settings'))
|
|
200
|
+
.width(200)
|
|
201
|
+
.margin({ top: 16 })
|
|
202
|
+
.onClick(() => {
|
|
203
|
+
this.permissionHelper?.openAppSettings()
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
.width('100%')
|
|
207
|
+
.height('100%')
|
|
208
|
+
.justifyContent(FlexAlign.Center)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## module.json5 配置
|
|
216
|
+
|
|
217
|
+
系统设置跳转通常**不需要额外配置**,因为是显式调用已知应用。
|
|
218
|
+
|
|
219
|
+
但如果需要响应外部查看请求,需要配置:
|
|
220
|
+
```json5
|
|
221
|
+
{
|
|
222
|
+
"skills": [{
|
|
223
|
+
"entities": ["entity.system.home"],
|
|
224
|
+
"actions": [
|
|
225
|
+
"ohos.want.action.home",
|
|
226
|
+
"ohos.want.action.view"
|
|
227
|
+
]
|
|
228
|
+
}]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 常见错误
|
|
235
|
+
|
|
236
|
+
### 错误 1:使用错误的 bundleName
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// 错误 — 非华为设备可能失败
|
|
240
|
+
const want: Want = {
|
|
241
|
+
bundleName: 'com.huawei.settings',
|
|
242
|
+
abilityName: 'com.huawei.settings.MainAbility'
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// 正确 — 根据设备厂商判断(如果需要跨设备)
|
|
246
|
+
import { deviceInfo } from '@kit.BasicServicesKit'
|
|
247
|
+
|
|
248
|
+
const bundleName = deviceInfo.brand === 'huawei'
|
|
249
|
+
? 'com.huawei.settings'
|
|
250
|
+
: 'com.android.settings' // 其他设备
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 错误 2:忘记错误处理
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// 错误 — startAbility 可能失败
|
|
257
|
+
context.startAbility(want)
|
|
258
|
+
|
|
259
|
+
// 正确 — 处理失败情况
|
|
260
|
+
context.startAbility(want)
|
|
261
|
+
.then(() => {
|
|
262
|
+
hilog.info(DOMAIN, TAG, 'Opened settings')
|
|
263
|
+
})
|
|
264
|
+
.catch((err: Error) => {
|
|
265
|
+
hilog.error(DOMAIN, TAG, `Failed to open settings: ${err.message}`)
|
|
266
|
+
promptAction.showToast({ message: '无法打开设置页面' })
|
|
267
|
+
})
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 错误 3:参数传递错误
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// 错误 — 参数类型错误
|
|
274
|
+
const want: Want = {
|
|
275
|
+
bundleName: 'com.huawei.settings',
|
|
276
|
+
parameters: { 'page': 123 } // ❌ 应该是 string
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// 正确 — 参数类型正确
|
|
280
|
+
const want: Want = {
|
|
281
|
+
bundleName: 'com.huawei.settings',
|
|
282
|
+
parameters: { 'page': 'permission' } // ✅ string 类型
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 与其他跳转方式的对比
|
|
289
|
+
|
|
290
|
+
| 跳转类型 | Want 类型 | 是否需要配置 | 适用场景 |
|
|
291
|
+
|---------|----------|------------|---------|
|
|
292
|
+
| 系统设置跳转 | 显式 Want | 不需要 | 跳转到已知系统应用 |
|
|
293
|
+
| 浏览器跳转 | 隐式 Want | 通常不需要 | 打开网页/邮件链接 |
|
|
294
|
+
| 分享面板 | 系统 API | 通常不需要 | 分享内容到其他应用 |
|
|
295
|
+
| 系统相机 | 系统 API | **需要** | 拍照/录像 |
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 调用链分析
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
[UI层] 用户点击"打开设置"按钮
|
|
303
|
+
↓
|
|
304
|
+
[事件层] Button.onClick 回调
|
|
305
|
+
↓
|
|
306
|
+
[工具层] PermissionHelper.openAppSettings()
|
|
307
|
+
↓
|
|
308
|
+
[Want构建] 创建显式 Want 对象
|
|
309
|
+
↓
|
|
310
|
+
[系统API] context.startAbility(want)
|
|
311
|
+
↓
|
|
312
|
+
[系统应用] 华为系统设置应用
|
|
313
|
+
↓
|
|
314
|
+
[页面显示] 应用信息/权限设置页
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## 参考链接
|
|
320
|
+
|
|
321
|
+
- [AbilityKit API 文档](https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-app-ability-want-V5)
|
|
322
|
+
- [权限管理最佳实践](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/permission-overview-V5)
|