android2harmony 0.1.3 → 0.1.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/README.md +1 -407
- package/agents/self-tester.md +376 -339
- package/dist/index.js +131 -586
- package/dist/index.js.map +4 -4
- package/package.json +36 -37
- package/skills/a2h-resource-convert/SKILL.md +902 -0
- package/skills/a2h-resource-convert/references/code-vector-icon-rules.md +335 -0
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/conversion-rules.md +15 -2
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/dependency-analysis-rules.md +24 -2
- package/skills/a2h-resource-convert/references/lottie-conversion-rules.md +219 -0
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/resource-mapping-rules.md +27 -2
- package/skills/{hmos-resources-convert → a2h-resource-convert}/references/xml-drawable-to-svg-rules.md +118 -1
- package/skills/a2h-resource-convert/scripts/a2h_resource_convert.js +2166 -0
- package/skills/a2h-resource-convert/scripts/code_vector_icons.js +607 -0
- package/skills/a2h-resource-convert/scripts/package.json +3 -0
- package/skills/a2h-resource-convert/scripts/svg_fidelity_check.js +632 -0
- package/skills/a2h-ui-transfer/SKILL.md +420 -0
- package/skills/a2h-ui-transfer/references/conversion-procedure.md +572 -0
- package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/_/350/214/203/345/274/217/351/200/211/346/213/251/350/257/264/346/230/216.md +1 -1
- package/skills/a2h-ui-transfer/references/mvvm-v2/_/350/243/205/351/245/260/345/231/250/351/200/237/346/237/245.md +133 -0
- package/skills/{hmos-batch-ui-align/scripts/android_parse_fast.ts → a2h-ui-transfer/scripts/android_parse_fast.js} +397 -283
- package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +1624 -0
- package/skills/a2h-ui-transfer/scripts/measure_pack.js +1005 -0
- package/skills/a2h-ui-transfer/scripts/package.json +3 -0
- package/skills/hmos-incremental-ui-align/README.md +251 -251
- package/skills/hmos-incremental-ui-align/SKILL.md +364 -364
- package/skills/hmos-incremental-ui-align/references/State_Model_Template.md +2 -2
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.js +790 -0
- package/skills/hmos-incremental-ui-align/scripts/extract_checklist.js +285 -0
- package/skills/hmos-incremental-ui-align/scripts/navigation-capure.md +76 -76
- package/skills/hmos-incremental-ui-align/scripts/page_capture.js +756 -0
- package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.js +155 -0
- package/skills/hmos-batch-ui-align/SKILL.md +0 -141
- package/skills/hmos-batch-ui-align/references/conversion-procedure.md +0 -217
- package/skills/hmos-incremental-ui-align/scripts/app_feature_verify.ts +0 -999
- package/skills/hmos-incremental-ui-align/scripts/extract_checklist.ts +0 -343
- package/skills/hmos-incremental-ui-align/scripts/page_capture.ts +0 -977
- package/skills/hmos-incremental-ui-align/scripts/page_capture_burst.ts +0 -188
- package/skills/hmos-resources-convert/SKILL.md +0 -654
- package/skills/hmos-resources-convert/template/AppScope/app.json5 +0 -10
- package/skills/hmos-resources-convert/template/AppScope/resources/base/element/string.json +0 -8
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/AppScope/resources/base/media/layered_image.json +0 -7
- package/skills/hmos-resources-convert/template/build-profile.json5 +0 -42
- package/skills/hmos-resources-convert/template/code-linter.json5 +0 -32
- package/skills/hmos-resources-convert/template/entry/build-profile.json5 +0 -33
- package/skills/hmos-resources-convert/template/entry/hvigorfile.ts +0 -6
- package/skills/hmos-resources-convert/template/entry/obfuscation-rules.txt +0 -23
- package/skills/hmos-resources-convert/template/entry/oh-package.json5 +0 -10
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entryability/EntryAbility.ets +0 -48
- package/skills/hmos-resources-convert/template/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets +0 -16
- package/skills/hmos-resources-convert/template/entry/src/main/ets/pages/Index.ets +0 -23
- package/skills/hmos-resources-convert/template/entry/src/main/module.json5 +0 -55
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/color.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/float.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/element/string.json +0 -16
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/background.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/foreground.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/layered_image.json +0 -7
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/media/startIcon.png +0 -0
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/backup_config.json +0 -3
- package/skills/hmos-resources-convert/template/entry/src/main/resources/base/profile/main_pages.json +0 -5
- package/skills/hmos-resources-convert/template/entry/src/main/resources/dark/element/color.json +0 -8
- package/skills/hmos-resources-convert/template/entry/src/mock/mock-config.json5 +0 -2
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/Ability.test.ets +0 -35
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/ets/test/List.test.ets +0 -5
- package/skills/hmos-resources-convert/template/entry/src/ohosTest/module.json5 +0 -16
- package/skills/hmos-resources-convert/template/entry/src/test/List.test.ets +0 -5
- package/skills/hmos-resources-convert/template/entry/src/test/LocalUnit.test.ets +0 -33
- package/skills/hmos-resources-convert/template/hvigor/hvigor-config.json5 +0 -23
- package/skills/hmos-resources-convert/template/hvigorfile.ts +0 -6
- package/skills/hmos-resources-convert/template/oh-package-lock.json5 +0 -28
- package/skills/hmos-resources-convert/template/oh-package.json5 +0 -10
- package/tools/autotest/deps/autotest-agent-0.1.1.tgz +0 -0
- package/tools/autotest/engine/batch-launcher.ts +0 -326
- package/tools/autotest/engine/report-tool.ts +0 -773
- package/tools/autotest/engine/self-test-runner.ts +0 -1024
- package/tools/autotest/engine/testcases-tool.ts +0 -246
- package/tools/autotest/resolve-metadata-tool.ts +0 -143
- package/tools/autotest/runner/logger.ts +0 -45
- package/tools/autotest/runner/process-utils.ts +0 -41
- package/tools/autotest/validate.ts +0 -115
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-atomic-component-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-interaction-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mappings/android-to-harmonyOS-ui-layout-mapping-reference.md +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Link/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Observed/350/243/205/351/245/260/345/231/250/345/222/214@ObjectLink/350/243/205/351/245/260/345/231/250/357/274/232/345/265/214/345/245/227/347/261/273/345/257/271/350/261/241/345/261/236/346/200/247/345/217/230/345/214/226.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Prop/350/243/205/351/245/260/345/231/250/357/274/232/347/210/266/345/255/220/345/215/225/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Provide/350/243/205/351/245/260/345/231/250/345/222/214@Consume/350/243/205/351/245/260/345/231/250/357/274/232/344/270/216/345/220/216/344/273/243/347/273/204/344/273/266/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@State/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Track/350/243/205/351/245/260/345/231/250/357/274/232class/345/257/271/350/261/241/345/261/236/346/200/247/347/272/247/346/233/264/346/226/260.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/@Watch/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/346/233/264/346/224/271/351/200/232/347/237/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/AppStorage/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200/347/232/204UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/LocalStorage/357/274/232/351/241/265/351/235/242/347/272/247UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm/PersistentStorage/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm//347/256/241/347/220/206/345/272/224/347/224/250/346/213/245/346/234/211/347/232/204/347/212/266/346/200/201/346/246/202/350/277/260.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Computed/350/243/205/351/245/260/345/231/250/357/274/232/350/256/241/347/256/227/345/261/236/346/200/247.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Event/350/243/205/351/245/260/345/231/250/357/274/232/350/247/204/350/214/203/347/273/204/344/273/266/350/276/223/345/207/272.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Local/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/206/205/351/203/250/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Monitor/350/243/205/351/245/260/345/231/250/357/274/232/347/212/266/346/200/201/345/217/230/351/207/217/344/277/256/346/224/271/345/274/202/346/255/245/347/233/221/345/220/254.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@ObservedV2/350/243/205/351/245/260/345/231/250/345/222/214@Trace/350/243/205/351/245/260/345/231/250/357/274/232/347/261/273/345/261/236/346/200/247/345/217/230/345/214/226/350/247/202/346/265/213.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Once/350/243/205/351/245/260/345/231/250/357/274/232/345/210/235/345/247/213/345/214/226/345/220/214/346/255/245/344/270/200/346/254/241.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Param/350/243/205/351/245/260/345/231/250/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Provider/350/243/205/351/245/260/345/231/250/345/222/214@Consumer/350/243/205/351/245/260/345/231/250/357/274/232/350/267/250/347/273/204/344/273/266/345/261/202/347/272/247/345/217/214/345/220/221/345/220/214/346/255/245.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/@Type/350/243/205/351/245/260/345/231/250/357/274/232/346/240/207/350/256/260/347/261/273/345/261/236/346/200/247/347/232/204/347/261/273/345/236/213.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/AppStorageV2/357/274/232/345/272/224/347/224/250/345/205/250/345/261/200UI/347/212/266/346/200/201/345/255/230/345/202/250.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2/PersistenceV2/357/274/232/346/214/201/344/271/205/345/214/226/345/255/230/345/202/250UI/347/212/266/346/200/201.md" +0 -0
- /package/skills/{hmos-batch-ui-align → a2h-ui-transfer}/references/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/220/221V2/350/277/201/347/247/273/344/270/216/346/267/267/347/224/250/346/214/207/345/257/274.md" +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
function sleep(seconds) {
|
|
7
|
+
const end = Date.now() + seconds * 1e3;
|
|
8
|
+
const sab = new SharedArrayBuffer(4);
|
|
9
|
+
const ia = new Int32Array(sab);
|
|
10
|
+
while (Date.now() < end) {
|
|
11
|
+
Atomics.wait(ia, 0, 0, Math.min(50, end - Date.now()));
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function adb(args, serial = null, timeout = 15) {
|
|
15
|
+
const full = [];
|
|
16
|
+
if (serial) full.push("-s", serial);
|
|
17
|
+
full.push(...args);
|
|
18
|
+
const res = spawnSync("adb", full, {
|
|
19
|
+
timeout: timeout * 1e3,
|
|
20
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
21
|
+
windowsHide: true
|
|
22
|
+
});
|
|
23
|
+
if (res.error && res.error.code === "ETIMEDOUT") {
|
|
24
|
+
console.log(` [WARN] adb timeout: ${args.join(" ")}`);
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
const out = res.stdout ?? Buffer.alloc(0);
|
|
28
|
+
return out.toString("utf-8").trim();
|
|
29
|
+
}
|
|
30
|
+
function adbShell(args, serial = null, timeout = 15) {
|
|
31
|
+
return adb(["shell", ...args], serial, timeout);
|
|
32
|
+
}
|
|
33
|
+
function adbScreenshot(outputDir, filename, serial = null) {
|
|
34
|
+
const localPath = path.join(outputDir, filename);
|
|
35
|
+
const args = [];
|
|
36
|
+
if (serial) args.push("-s", serial);
|
|
37
|
+
args.push("exec-out", "screencap", "-p");
|
|
38
|
+
const res = spawnSync("adb", args, {
|
|
39
|
+
timeout: 15 * 1e3,
|
|
40
|
+
maxBuffer: 128 * 1024 * 1024,
|
|
41
|
+
windowsHide: true
|
|
42
|
+
});
|
|
43
|
+
if (res.error && res.error.code === "ETIMEDOUT") {
|
|
44
|
+
console.log(" [WARN] screenshot timeout");
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const stdoutBuf = res.stdout ?? Buffer.alloc(0);
|
|
48
|
+
if (stdoutBuf.length > 0) {
|
|
49
|
+
fs.writeFileSync(localPath, stdoutBuf);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function adbDumpXml(outputDir, filename, serial = null) {
|
|
55
|
+
const remotePath = "/sdcard/ui_dump.xml";
|
|
56
|
+
adbShell(["rm", "-f", "/sdcard/ui_dump.xml", "&&", "uiautomator", "dump", "/sdcard/ui_dump.xml"], serial, 20);
|
|
57
|
+
for (let i = 0; i < 6; i++) {
|
|
58
|
+
const check = adbShell(["ls", remotePath], serial);
|
|
59
|
+
if (check.includes(remotePath)) break;
|
|
60
|
+
sleep(0.2);
|
|
61
|
+
}
|
|
62
|
+
const xmlContent = adbShell(["cat", remotePath], serial, 15);
|
|
63
|
+
if (xmlContent) {
|
|
64
|
+
fs.writeFileSync(path.join(outputDir, filename), xmlContent, "utf-8");
|
|
65
|
+
}
|
|
66
|
+
return xmlContent;
|
|
67
|
+
}
|
|
68
|
+
function parseArgs(argv) {
|
|
69
|
+
const args = { device: "adb", outputDir: "", duration: 10, serial: null };
|
|
70
|
+
for (let i = 0; i < argv.length; i++) {
|
|
71
|
+
const a = argv[i];
|
|
72
|
+
const next = () => {
|
|
73
|
+
if (i + 1 >= argv.length) {
|
|
74
|
+
console.error(`Missing value for ${a}`);
|
|
75
|
+
process.exit(2);
|
|
76
|
+
}
|
|
77
|
+
return argv[++i];
|
|
78
|
+
};
|
|
79
|
+
if (a === "--device") {
|
|
80
|
+
const v = next();
|
|
81
|
+
if (v !== "adb") {
|
|
82
|
+
console.error("--device must be adb (currently adb only)");
|
|
83
|
+
process.exit(2);
|
|
84
|
+
}
|
|
85
|
+
args.device = v;
|
|
86
|
+
} else if (a === "--output-dir" || a === "-o") {
|
|
87
|
+
args.outputDir = next();
|
|
88
|
+
} else if (a === "--duration" || a === "-d") {
|
|
89
|
+
args.duration = parseInt(next(), 10);
|
|
90
|
+
} else if (a === "--serial") {
|
|
91
|
+
args.serial = next();
|
|
92
|
+
} else if (a === "-h" || a === "--help") {
|
|
93
|
+
printHelp();
|
|
94
|
+
process.exit(0);
|
|
95
|
+
} else {
|
|
96
|
+
console.error(`Unknown argument: ${a}`);
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (!args.outputDir) {
|
|
101
|
+
console.error("error: --output-dir/-o is required");
|
|
102
|
+
process.exit(2);
|
|
103
|
+
}
|
|
104
|
+
return args;
|
|
105
|
+
}
|
|
106
|
+
function printHelp() {
|
|
107
|
+
console.log(
|
|
108
|
+
[
|
|
109
|
+
"Burst capture: take view tree + screenshot every 1s for N seconds",
|
|
110
|
+
"",
|
|
111
|
+
"Usage: node page_capture_burst.ts --device adb -o <dir> [--duration N] [--serial S]",
|
|
112
|
+
"",
|
|
113
|
+
" --device Device type (currently adb only)",
|
|
114
|
+
" --output-dir,-o Output directory for captured files (required)",
|
|
115
|
+
" --duration,-d Total capture duration in seconds (default: 10)",
|
|
116
|
+
" --serial ADB device serial (optional)"
|
|
117
|
+
].join("\n")
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
function main() {
|
|
121
|
+
const args = parseArgs(process.argv.slice(2));
|
|
122
|
+
fs.mkdirSync(args.outputDir, { recursive: true });
|
|
123
|
+
console.log(`[Burst Capture] duration=${args.duration}s, output=${args.outputDir}`);
|
|
124
|
+
console.log("[Burst Capture] Starting continuous capture...");
|
|
125
|
+
const startTime = Date.now();
|
|
126
|
+
let frame = 0;
|
|
127
|
+
while (true) {
|
|
128
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
129
|
+
if (elapsed >= args.duration) break;
|
|
130
|
+
frame += 1;
|
|
131
|
+
const frameStart = Date.now();
|
|
132
|
+
const frameStr = String(frame).padStart(3, "0");
|
|
133
|
+
process.stdout.write(` Frame ${frame} (t=${elapsed.toFixed(1)}s)... `);
|
|
134
|
+
const xmlOk = !!adbDumpXml(args.outputDir, `view_${frameStr}.xml`, args.serial);
|
|
135
|
+
const shotOk = adbScreenshot(args.outputDir, `screenshot_${frameStr}.png`, args.serial);
|
|
136
|
+
console.log(xmlOk && shotOk ? "OK" : "partial");
|
|
137
|
+
const frameElapsed = (Date.now() - frameStart) / 1e3;
|
|
138
|
+
const sleepTime = Math.max(0, 1 - frameElapsed);
|
|
139
|
+
if (sleepTime > 0 && (Date.now() - startTime) / 1e3 + sleepTime < args.duration) {
|
|
140
|
+
sleep(sleepTime);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
console.log(`
|
|
144
|
+
[Burst Capture] Done. ${frame} frames captured to ${args.outputDir}`);
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
147
|
+
const isEntry = process.argv[1] !== void 0 && import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
148
|
+
if (isEntry) {
|
|
149
|
+
process.exit(main());
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
adbDumpXml,
|
|
153
|
+
adbScreenshot,
|
|
154
|
+
parseArgs
|
|
155
|
+
};
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: hmos-batch-ui-align
|
|
3
|
-
description: Batch-convert multiple Android Activity UI snapshots to HarmonyOS ArkUI (ArkTS) pages. Use when the user wants to migrate Android UI pages to HarmonyOS in bulk, port multiple Activity screens to ArkTS, or run an Android-to-HarmonyOS UI conversion across a folder of page snapshots (page_NNNN_ActivityName). Triggers on phrases like "把安卓页面迁移到鸿蒙", "Android UI 转鸿蒙", "批量转 ArkTS", "hmos-batch-ui-align", or any request that supplies an Android project path + Harmony project path + a directory of page snapshots.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# hmos-batch-ui-align — Android → HarmonyOS UI Batch Conversion
|
|
7
|
-
|
|
8
|
-
Batch-convert a set of Android page snapshots (each snapshot is a `page_NNNN_ActivityName/` directory containing `meta.json` + `view.xml` + optional `screenshot.png`) into HarmonyOS ArkTS pages following MVVM architecture. State management follows the target project's paradigm — **V2 for new 0→1 conversions, V1 when the existing Harmony project already uses V1** (detected per project; see Step 5).
|
|
9
|
-
|
|
10
|
-
## Step 0 — Environment Variables Check (run first)
|
|
11
|
-
|
|
12
|
-
The final build (Step 6, via `hmos_fix_build_errors`) resolves the DevEco location from the **OS environment variable** — then asks the user if unset (read env vars with `echo "$VAR"` on macOS/Linux; `$env:VAR` in PowerShell on Windows):
|
|
13
|
-
|
|
14
|
-
| Variable | Needed for | Valid when |
|
|
15
|
-
|---|---|---|
|
|
16
|
-
| `DEVECO_SDK_HOME` | Step 6 unified build | resolves to a valid DevEco install (derive the install root as its parent dir) |
|
|
17
|
-
|
|
18
|
-
If the env var does not yield a valid DevEco install, **ask the user** for the DevEco Studio install path before proceeding (suggest setting `DEVECO_SDK_HOME` as a machine environment variable).
|
|
19
|
-
|
|
20
|
-
## Step 1 — Parse User Request
|
|
21
|
-
|
|
22
|
-
Extract 4 paths from the user's message (natural language, no fixed format):
|
|
23
|
-
|
|
24
|
-
| Variable | Meaning | Typical Phrases |
|
|
25
|
-
|---|---|---|
|
|
26
|
-
| `android_project_dir` | Android source project root | "Android project path", "安卓项目", "source project" |
|
|
27
|
-
| `harmony_project_dir` | HarmonyOS target project root | "target Harmony project", "鸿蒙项目", "output project" |
|
|
28
|
-
| `ui_info_root` | **Parent directory containing all `page_NNNN_*` subdirectories** | "page screenshots and view tree", "page snapshots", "page folder" |
|
|
29
|
-
| `pages` (optional) | User-explicitly listed page subset | When user lists `1. .../page_0001_X` ... take these |
|
|
30
|
-
| `apk_path` (optional) | Full path of the Android APK corresponding to `android_project_dir`. Passed through to `hmos-resources-convert` in Step 2 to skip the Gradle build and decompile this APK directly with apktool to extract resources | "APK path", "apk文件", "安卓apk" |
|
|
31
|
-
|
|
32
|
-
**`references_dir` and MVVM document directories always use skill-bundled relative paths** — **do not** accept user-provided overrides:
|
|
33
|
-
|
|
34
|
-
- mappings: `./references/mappings/`
|
|
35
|
-
- mvvm (V1): `./references/mvvm/`
|
|
36
|
-
- mvvm (V2): `./references/mvvm-v2/`
|
|
37
|
-
|
|
38
|
-
The per-page sub-agent first **detects whether the target Harmony project uses state-management V1 or V2** (project-level, decided once), then reads only the matching doc set. New 0→1 conversions default to V2; a project already on V1 stays on V1. See `./references/mvvm-v2/_范式选择说明.md` for the detection rule.
|
|
39
|
-
|
|
40
|
-
If any required path cannot be extracted from the user's message, **ask the user** — do not guess.
|
|
41
|
-
|
|
42
|
-
## Step 2 — Resource Conversion
|
|
43
|
-
|
|
44
|
-
Invoke the `hmos-resources-convert` skill with the following parameters (all snake_case, matching that skill's declared inputs):
|
|
45
|
-
1. `android_project_dir` = `android_project_dir`
|
|
46
|
-
2. `harmony_project_dir` = `harmony_project_dir`
|
|
47
|
-
3. `resource_mapping_path` = generate the resource mapping document under `harmony_project_dir` at `${harmony_project_dir}/resource_mapping.md`
|
|
48
|
-
4. `apk_path` = `apk_path` — **only if the user provided it**. When passed through, `hmos-resources-convert` skips the Gradle build and decodes this APK directly to extract resources. Omit this parameter entirely when `apk_path` was not provided.
|
|
49
|
-
|
|
50
|
-
This skill batch-converts Android resources (strings, colors, drawables, images, etc.) to HarmonyOS resource format and generates the mapping document.
|
|
51
|
-
|
|
52
|
-
## Step 3 — Page Exploration
|
|
53
|
-
|
|
54
|
-
1. Check if `ui_info_root` already contains `page_*` subdirectories. If yes, inform the user "N page snapshots already exist, skipping exploration" and skip this step.
|
|
55
|
-
2. Otherwise, determine the target App package name (`package`). If the user didn't provide it, extract the `package` attribute from `AndroidManifest.xml` in `android_project_dir`; if still unavailable, ask the user.
|
|
56
|
-
3. Execute with Bash (TypeScript version, run directly with Node ≥ 22.18 / 23.6 which natively strips types — no build step, no dependencies):
|
|
57
|
-
```
|
|
58
|
-
node ./scripts/android_parse_fast.ts --package <package_name> --output <ui_info_root_absolute_path>
|
|
59
|
-
```
|
|
60
|
-
This script connects to the Android emulator via ADB, BFS-traverses all reachable pages in the App, and for each page saves `screenshot.png`, `view.xml`, `meta.json` into `page_NNNN_ActivityName/` subdirectories. It also generates `index.json` and `report.html` under `ui_info_root`.
|
|
61
|
-
4. After execution, confirm that `index.json` has been generated under `ui_info_root`. If not, report an error and abort.
|
|
62
|
-
|
|
63
|
-
## Step 4 — Discover Pages
|
|
64
|
-
|
|
65
|
-
If the user did not explicitly list pages: use Glob `page_*_*` under `ui_info_root` to find all page subdirectories, sorted by name (`page_0001_*` first).
|
|
66
|
-
|
|
67
|
-
If the user explicitly listed pages (e.g. "1. .../page_0001_MainActivity ... 19. .../page_0019_MainActivity"): use the user's list.
|
|
68
|
-
|
|
69
|
-
### Resume from progress (checkpoint)
|
|
70
|
-
|
|
71
|
-
The batch persists progress to `${ui_info_root}/batch_progress.json` so an interrupted run can continue instead of restarting from scratch. Schema:
|
|
72
|
-
|
|
73
|
-
```json
|
|
74
|
-
{
|
|
75
|
-
"harmony_project_dir": "<abs path>",
|
|
76
|
-
"updated_at": "<ISO timestamp>",
|
|
77
|
-
"pages": {
|
|
78
|
-
"page_0001_MainActivity": { "status": "done", "output": "pages/MainPage.ets", "summary": "..." },
|
|
79
|
-
"page_0002_LoginActivity": { "status": "failed", "error": "..." },
|
|
80
|
-
"page_0003_SettingsActivity": { "status": "pending" }
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
- `status` is one of `pending` | `done` | `failed`.
|
|
86
|
-
- If `batch_progress.json` exists **and** its `harmony_project_dir` matches the current one, read it back and merge into the discovered page set: pages marked `done` are skipped in Step 5; `pending`/`failed` pages are (re)processed.
|
|
87
|
-
- If it does not exist, or `harmony_project_dir` differs (different target project), start fresh — initialize every discovered page to `pending` and write the file.
|
|
88
|
-
- Before skipping a `done` page, sanity-check that its recorded `output` file still exists under `harmony_project_dir`. If the output is missing, treat the page as `pending` (re-convert).
|
|
89
|
-
|
|
90
|
-
After discovery, create a task for each page via TaskCreate to track progress, initializing already-`done` pages as completed. Report to the user how many pages are being resumed vs. freshly processed (e.g. "Resuming: 7 done, 12 remaining").
|
|
91
|
-
|
|
92
|
-
## Step 5 — Per-Page Conversion
|
|
93
|
-
|
|
94
|
-
**Idempotency gate (check before converting each page):** consult `batch_progress.json`. If the page's `status` is `done` and its recorded `output` file still exists under `harmony_project_dir`, **skip it** (do not spawn a sub-agent) and go to the next page. Only pages with status `pending`/`failed` (or a `done` page whose output went missing) are converted.
|
|
95
|
-
|
|
96
|
-
**For each page to convert**, invoke the Agent tool (`subagent_type: general-purpose`) to perform conversion. The prompt must include:
|
|
97
|
-
|
|
98
|
-
1. The page's `ui_info` absolute path (`{ui_info_root}/page_NNNN_ActivityName`)
|
|
99
|
-
2. `android_project_dir`, `harmony_project_dir`
|
|
100
|
-
3. Absolute paths for mappings, mvvm (V1: `references/mvvm/`) and mvvm-v2 (V2: `references/mvvm-v2/`) directories (skill-bundled) — pass both so the sub-agent can pick the set matching the project's detected paradigm
|
|
101
|
-
4. **Copy the full contents of `references/conversion-procedure.md` as the sub-agent's work instructions** (use Read to read it and paste into the prompt)
|
|
102
|
-
|
|
103
|
-
Why use sub-agents: single-page conversion reads many mapping/MVVM documents + Android source code. Sub-agents isolate context to avoid overwhelming the main session.
|
|
104
|
-
|
|
105
|
-
After receiving a sub-agent's conversion report:
|
|
106
|
-
1. **Update `batch_progress.json` immediately** (before moving to the next page) — set the page's `status` to `done` (with `output` = generated `.ets` path and a one-line `summary`) or `failed` (with `error`), and refresh `updated_at`. Writing after every page is what makes the batch resumable: a crash or interruption loses at most the one in-flight page.
|
|
107
|
-
2. Append a brief summary to a cumulative report and mark the corresponding task as completed.
|
|
108
|
-
|
|
109
|
-
**Execute serially** — do not parallelize (multiple sub-agents modifying the same Harmony project simultaneously will conflict). Serial execution also keeps `batch_progress.json` writes free of races.
|
|
110
|
-
|
|
111
|
-
## Step 6 — Unified Build Fix
|
|
112
|
-
|
|
113
|
-
After all page conversions are complete, call the `hmos_fix_build_errors` skill **only once at the end** (passing `harmony_project_dir`) to make the entire project compile. Do not fix per-page (wasteful, and sub-agents are instructed to skip this step).
|
|
114
|
-
|
|
115
|
-
## Step 7 — Summary Report
|
|
116
|
-
|
|
117
|
-
Output an overview:
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
## Batch Conversion Overview
|
|
121
|
-
- Pages processed: N
|
|
122
|
-
- Successful: M Failed/partial: K
|
|
123
|
-
- Final build status: SUCCESS / PARTIAL
|
|
124
|
-
|
|
125
|
-
### Per-Page Results
|
|
126
|
-
| Page | Activity | Output File | Status |
|
|
127
|
-
|---|---|---|---|
|
|
128
|
-
| 0001 | MainActivity | pages/MainPage.ets | ✓ |
|
|
129
|
-
| ... | | | |
|
|
130
|
-
|
|
131
|
-
### Manual Follow-up TODOs
|
|
132
|
-
- (Aggregate mocked items / unimplemented navigation / business logic from sub-reports)
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Key Constraints
|
|
136
|
-
|
|
137
|
-
- **Never** let a per-page sub-agent call `hmos_fix_build_errors`; do it once in Step 6 only.
|
|
138
|
-
- If the user's `harmony_project_dir` lacks entry/resources subdirectories or the target project doesn't exist, stop and ask — do not generate code.
|
|
139
|
-
- If input paths are Windows paths (with backslashes), pass them as-is to sub-agents; internal Read/Glob support them.
|
|
140
|
-
- `batch_progress.json` is the single source of truth for resume. Write it after **every** page (Step 5), keyed by the target `harmony_project_dir`. Do not delete it mid-batch; it is safe to leave in place after completion (a re-run with all pages `done` becomes a no-op). To force a full re-conversion, delete `batch_progress.json` (or point at a fresh `harmony_project_dir`).
|
|
141
|
-
- The Step 6 build fix runs regardless of resume state — resuming skips per-page conversion, not the final unified build.
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
# Single-Page Android → HarmonyOS UI 转换流程
|
|
2
|
-
|
|
3
|
-
> 这是 `hmos-batch-ui-align` skill 在每个子 agent 中执行的单页转换流程。基于 `a2h-ui-conversion-new` agent 改写:去掉 Phase 6 build 修复(由父 skill 统一在最后做一次)。
|
|
4
|
-
|
|
5
|
-
你是一个 **UI Activity 转换器**,专门把单个 Android Activity 的某一页 UI 迁移到 HarmonyOS ArkUI(ArkTS)。
|
|
6
|
-
|
|
7
|
-
## 输入(由父 skill 传入 prompt)
|
|
8
|
-
|
|
9
|
-
1. `activity_name` — Android Activity 类名
|
|
10
|
-
2. `android_project_dir` — Android 源项目根
|
|
11
|
-
3. `ui_info` — **单个** `page_NNNN_ActivityName/` 目录绝对路径(含 `meta.json` + `view.xml` + 可能的 `view_scroll_n.xml` / `screenshot.png`)
|
|
12
|
-
4. `harmony_project_dir` — HarmonyOS 项目根
|
|
13
|
-
5. `mappings_dir` — 三份映射表所在目录(skill 自带)
|
|
14
|
-
6. `mvvm_dir` — V1 状态管理文档目录(skill 自带 `references/mvvm/`,13 份)
|
|
15
|
-
7. `mvvm_v2_dir` — V2 状态管理文档目录(skill 自带 `references/mvvm-v2/`,14 份)
|
|
16
|
-
|
|
17
|
-
## 输出
|
|
18
|
-
|
|
19
|
-
- ArkTS `.ets` 页面写到 `{harmony_project_dir}/entry/src/main/ets/pages/`
|
|
20
|
-
- dialog/fragment/adapter 等组件写到 `.../ets/components/`
|
|
21
|
-
- ViewModel 写到 `.../ets/viewmodel/`,Model 写到 `.../ets/model/`
|
|
22
|
-
- 文本格式的转换报告(不落盘,作为最终回复返回给父 skill)
|
|
23
|
-
|
|
24
|
-
## Phase 1 — 定位页面
|
|
25
|
-
|
|
26
|
-
`ui_info` 已是单个页面目录,无需再筛选。
|
|
27
|
-
|
|
28
|
-
## Phase 2 — 探测工程范式 + 加载 MVVM 参考
|
|
29
|
-
|
|
30
|
-
### 2.0 探测目标工程采用 V1 还是 V2(先做,决定后续读哪套文档)
|
|
31
|
-
|
|
32
|
-
状态管理 V1、V2 **不在同一工程内混用**。以**工程级**粒度判定一次,后续全程沿用:
|
|
33
|
-
|
|
34
|
-
1. 扫 `{harmony_project_dir}/entry/src/main/ets/` 下已有 `.ets` 的状态管理装饰器:
|
|
35
|
-
- 命中 `@Component` + `@State`/`@Prop`/`@Link`/`@Provide`/`@Consume`/`@Observed`/`@ObjectLink`/`@Watch` → **V1**
|
|
36
|
-
- 命中 `@ComponentV2` + `@Local`/`@Param`/`@Once`/`@Event`/`@ObservedV2`/`@Trace`/`@Monitor`/`@Provider`/`@Consumer` → **V2**
|
|
37
|
-
2. 判定:
|
|
38
|
-
- **空工程 / 0→1 全新转换 / 无任何状态装饰器 → 默认 V2**(新项目优先 V2)
|
|
39
|
-
- 只有 V1 → **V1**;只有 V2 → **V2**
|
|
40
|
-
- V1、V2 都有 → 以占多数者为准;新增代码贴合**目标页所在目录**的既有范式,并在转换报告中标注
|
|
41
|
-
3. 输出判定结果(`范式 = V1 | V2`),Phase 4 全程据此选装饰器。
|
|
42
|
-
|
|
43
|
-
> 详见 `{mvvm_v2_dir}/_范式选择说明.md`。
|
|
44
|
-
|
|
45
|
-
### 2.1 加载对应范式的 MVVM 文档
|
|
46
|
-
|
|
47
|
-
**若判定为 V1**,读 `{mvvm_dir}/` 下全部 13 份:
|
|
48
|
-
- `MVVM模式(V1).md`
|
|
49
|
-
- `@Track装饰器:class对象属性级更新.md`
|
|
50
|
-
- `@State装饰器:组件内状态.md`
|
|
51
|
-
- `@Prop装饰器:父子单向同步.md`
|
|
52
|
-
- `@Link装饰器:父子双向同步.md`
|
|
53
|
-
- `@Provide装饰器和@Consume装饰器:与后代组件双向同步.md`
|
|
54
|
-
- `@Observed装饰器和@ObjectLink装饰器:嵌套类对象属性变化.md`
|
|
55
|
-
- `@Watch装饰器:状态变量更改通知.md`
|
|
56
|
-
- `管理应用拥有的状态概述.md`
|
|
57
|
-
- `LocalStorage:页面级UI状态存储.md`
|
|
58
|
-
- `AppStorage:应用全局的UI状态存储.md`
|
|
59
|
-
- `PersistentStorage:持久化存储UI状态.md`
|
|
60
|
-
- `Environment:设备环境查询.md`
|
|
61
|
-
|
|
62
|
-
**若判定为 V2**,读 `{mvvm_v2_dir}/` 下全部 14 份:
|
|
63
|
-
- `MVVM模式(V2).md`
|
|
64
|
-
- `@Local装饰器:组件内部状态.md`(组件内状态,对应 V1 `@State`)
|
|
65
|
-
- `@Param装饰器:组件外部输入.md` / `@Once装饰器:初始化同步一次.md`(父子输入,对应 V1 `@Prop`)
|
|
66
|
-
- `@Event装饰器:规范组件输出.md`(子→父回调,配合 `!!` 实现双向,对应 V1 `@Link`)
|
|
67
|
-
- `@Provider装饰器和@Consumer装饰器:跨组件层级双向同步.md`(对应 V1 `@Provide`/`@Consume`)
|
|
68
|
-
- `@ObservedV2装饰器和@Trace装饰器:类属性变化观测.md`(对应 V1 `@Observed`/`@ObjectLink` + `@Track`)
|
|
69
|
-
- `@Monitor装饰器:状态变量修改异步监听.md`(对应 V1 `@Watch`)
|
|
70
|
-
- `@Computed装饰器:计算属性.md` / `@Type装饰器:标记类属性的类型.md`
|
|
71
|
-
- `AppStorageV2:应用全局UI状态存储.md` / `PersistenceV2:持久化存储UI状态.md`
|
|
72
|
-
- `!!语法:双向绑定.md`
|
|
73
|
-
- `状态管理V1向V2迁移与混用指导.md`(仅当遇到既有 V1 工程需局部扩展时参考)
|
|
74
|
-
|
|
75
|
-
## Phase 3 — 加载映射参考
|
|
76
|
-
|
|
77
|
-
读 `{mappings_dir}/` 下:
|
|
78
|
-
1. `android-to-harmonyOS-ui-layout-mapping-reference.md` — 布局容器与布局属性映射(LinearLayout→Column/Row、FrameLayout→Stack、RecyclerView→List、layout_weight→.layoutWeight()、padding→.padding() 等)
|
|
79
|
-
2. `android-to-harmonyOS-ui-atomic-component-mapping-reference.md` — 原子组件映射(TextView→Text、EditText→TextInput/TextArea、Button→Button),含属性级映射(文本/样式/对齐/省略 等)
|
|
80
|
-
3. `android-to-harmonyOS-ui-interaction-mapping-reference.md` — 交互映射(onClick→.onClick()、onLongClick→LongPressGesture、touch、gesture、animation)
|
|
81
|
-
|
|
82
|
-
> 这三份映射是**首要依据**,优先于内置知识。
|
|
83
|
-
|
|
84
|
-
## Phase 4 — 单页转换
|
|
85
|
-
|
|
86
|
-
### 4.1 解析 meta.json
|
|
87
|
-
|
|
88
|
-
提取:`page_id`、`label`、`clickable_elements`(含 class/resource-id/text/content-desc/bounds)、`click_path`、`came_from`/`trigger_element`。
|
|
89
|
-
|
|
90
|
-
### 4.2 解析视图树
|
|
91
|
-
|
|
92
|
-
读 `view.xml`,建立结构化理解:根布局、toolbar、content、bottom nav、各 widget 的 class/resource-id/text/content-desc/bounds/clickable/scrollable/checkable/checked/enabled、由 bounds 推断父子+兄弟关系、识别复用模式(list item / card)。
|
|
93
|
-
|
|
94
|
-
若存在 `view_scroll_n.xml`(n=1,2,...):表示 Android UI 太长需滚动捕获,按编号 1→n 依次读取,**以 `view.xml` 为根**合并所有 `view_scroll_n.xml` 得到合并结构。
|
|
95
|
-
|
|
96
|
-
合并结构仍可能不完整(某些组件未出现在任何快照中)。**必须**在 `android_project_dir` 中查找并阅读与 `meta.json`、合并结构最相关的静态 XML 布局文件 + Kotlin/Java 代码,以静态源码为主结构,把合并结构作为动态信息补充,并把用户自定义/三方组件展开成原子 Android 布局/组件,得到**最终结构理解**。输出该理解。
|
|
97
|
-
|
|
98
|
-
可选:调用 `android-ui-graph-query` skill 补充 UI 图谱信息(widget 详细属性、样式、theme)。
|
|
99
|
-
|
|
100
|
-
### 4.3 生成 ArkUI 代码
|
|
101
|
-
|
|
102
|
-
**与现有 Harmony 文件冲突处理**:转换前若目标 .ets 已存在,三种情形——
|
|
103
|
-
1. 动态刷新组件(toolbar 不同状态显示不同图标)
|
|
104
|
-
2. 增量实现
|
|
105
|
-
3. 现有代码或最终结构理解有错
|
|
106
|
-
|
|
107
|
-
无论哪种,必须通过对照静态 XML/Kotlin/Java + view.xml + view_scroll_n.xml 验证。**不要删除已有 Harmony 代码**。
|
|
108
|
-
|
|
109
|
-
dialog/fragment/adapter 等必须从主页面拆出去到 `{harmony_project_dir}/entry/src/main/ets/components/`。
|
|
110
|
-
|
|
111
|
-
**架构**:
|
|
112
|
-
- MVVM:page/component 用 viewmodel,viewmodel 用 model
|
|
113
|
-
- 转换前先扫 `{harmony_project_dir}` 找可复用组件/VM/Model;toolbar 等通用组件抽到 components 复用;相关 model/viewmodel 合并复用,不要每页新建一份
|
|
114
|
-
- **按 Phase 2.0 判定的范式选装饰器,全程保持同一套,不混用**:
|
|
115
|
-
- **V1**:`@Component` 组件;组件内状态 `@State`,父子 `@Prop`(单向)/`@Link`(双向),跨层级 `@Provide`/`@Consume`;ViewModel/Model 类用 `@Observed` + 属性 `@Track`;副作用监听 `@Watch`
|
|
116
|
-
- **V2**:`@ComponentV2` 组件;组件内状态 `@Local`,父子输入 `@Param`(+ 可选 `@Once`)、子→父 `@Event`(配合 `!!` 双向绑定),跨层级 `@Provider`/`@Consumer`;ViewModel/Model 类用 `@ObservedV2` + 属性 `@Trace`;监听 `@Monitor`,计算属性 `@Computed`
|
|
117
|
-
|
|
118
|
-
**资源**:每个 Android UI 引用的资源(图片/字符串/颜色/尺寸等)必须使用 `{harmony_project_dir}\entry\src\main\resources` 中**同名**的转换后资源。
|
|
119
|
-
|
|
120
|
-
**布局**:查 layout mapping ref。
|
|
121
|
-
|
|
122
|
-
**组件**:查 atomic component mapping ref。
|
|
123
|
-
|
|
124
|
-
**交互**:查 interaction mapping ref。
|
|
125
|
-
|
|
126
|
-
**始终先查映射文件**(项目专属、覆盖更全),覆盖内置知识。
|
|
127
|
-
|
|
128
|
-
**单位与样式**:
|
|
129
|
-
- `dp` → `vp`,`sp` → `fp`
|
|
130
|
-
- 由 bounds 算尺寸:`width = x2 - x1`,`height = y2 - y1`
|
|
131
|
-
- 假定 3x 密度:`1vp ≈ 3px`
|
|
132
|
-
- 资源用 `$r('app.color.xxx')` / `$r('app.string.xxx')` / `$r('app.media.xxx')`
|
|
133
|
-
|
|
134
|
-
**路由**:实现 UI 与组件之间的路由关系。
|
|
135
|
-
|
|
136
|
-
**交互元素**:
|
|
137
|
-
- 给每个 `clickable_element` 挂 `.onClick()`
|
|
138
|
-
- `click_path` 显示跳到另一 Activity → `router.pushUrl()`
|
|
139
|
-
- 状态切换(dialog/menu/toggle)→ 用 `@State` 管理
|
|
140
|
-
- **优先真实实现**:mock 之前先在 `{harmony_project_dir}` 搜目标页面/组件 .ets 是否存在;存在则接真实 `router.pushUrl()`;**仅当目标尚未实现**才 mock + `console.info('TODO: ...')`
|
|
141
|
-
|
|
142
|
-
### 4.4 截图复核(如有)
|
|
143
|
-
|
|
144
|
-
`screenshot.png` 仅覆盖屏内 UI,**不要**用它去校验屏外组件。
|
|
145
|
-
|
|
146
|
-
存在则读取:视觉校验布局理解,对照视图层级,调整 `view.xml` 未捕获的间距/对齐/样式。`screenshot_scroll_n.png` 同理对照 `view_scroll_n.xml`。
|
|
147
|
-
|
|
148
|
-
## Phase 5 — 一致性校验(关键,必跑 3 轮)
|
|
149
|
-
|
|
150
|
-
列出转换后 HarmonyOS UI 中所有 layout/component/resource reference,对每一个**逐项**走完以下检查(不可省略):
|
|
151
|
-
|
|
152
|
-
- **观测装饰器缺失**(按 Phase 2.0 判定的范式分支):
|
|
153
|
-
- V1:`@Observed` 类中所有在 UI 中使用的属性是否都加了 `@Track`?没有则补
|
|
154
|
-
- V2:`@ObservedV2` 类中所有在 UI 中使用的属性是否都加了 `@Trace`?没有则补(`@ObservedV2` 与 `@Trace` 必须配合,单独使用任一个都不生效)
|
|
155
|
-
- 范式一致性:本页新增/修改的组件与 VM/Model 是否与工程判定范式一致?严禁 V1/V2 装饰器混用(如 `@Component` 里用 `@Local`、`@ComponentV2` 里用 `@State`)
|
|
156
|
-
- **颜色冲突**:fill/background/foreground 颜色是否冲突导致不可见(白底白图)?有则修
|
|
157
|
-
- **颜色设置**:背景/填充/前景色是否与 Android UI 一致(漏设、错设)?有则修
|
|
158
|
-
- **资源引用错用**:资源引用是否与 Android UI 一致;不存在的资源要重新查找正确名
|
|
159
|
-
- **形状错用**:圆角/直角等形状是否与 Android UI 一致
|
|
160
|
-
- **尺寸问题**:图标/组件大小是否一致
|
|
161
|
-
- **位置问题**:toolbar 是否在顶部、图标位置是否过左/过右等
|
|
162
|
-
- **路由关系**:UI 与组件间路由是否正确实现
|
|
163
|
-
|
|
164
|
-
**必须跑 3 轮验证迭代,并输出验证报告**。
|
|
165
|
-
|
|
166
|
-
## Phase 6 — Build 修复
|
|
167
|
-
|
|
168
|
-
**跳过**。父 skill `hmos-batch-ui-align` 会在所有页面转换完后统一调用 `hmos_fix_build_errors` 修一次。
|
|
169
|
-
|
|
170
|
-
## 返回转换报告
|
|
171
|
-
|
|
172
|
-
```
|
|
173
|
-
## Conversion Report: {ActivityName} (page_NNNN)
|
|
174
|
-
|
|
175
|
-
### Page Processed
|
|
176
|
-
| Page ID | Label | Type | Output File |
|
|
177
|
-
|---------|-------|------|-------------|
|
|
178
|
-
| page_NNNN_XXX | ... | Base Page | XxxPage.ets |
|
|
179
|
-
|
|
180
|
-
### Component Mapping Decisions
|
|
181
|
-
- ...
|
|
182
|
-
|
|
183
|
-
### State Variables
|
|
184
|
-
- ...
|
|
185
|
-
|
|
186
|
-
### Resources Converted
|
|
187
|
-
- ...
|
|
188
|
-
|
|
189
|
-
### Implemented Interactions
|
|
190
|
-
- ...(接通真实跳转的事件)
|
|
191
|
-
|
|
192
|
-
### TODOs & Mocked Items
|
|
193
|
-
- ...(mock 的事件、未实现的目标页)
|
|
194
|
-
|
|
195
|
-
### Navigation
|
|
196
|
-
- ...(从 click_path 抽取的导航目标)
|
|
197
|
-
|
|
198
|
-
### Validation Report (3 iterations)
|
|
199
|
-
- 第 1 轮:发现/修复 N 项
|
|
200
|
-
- 第 2 轮:发现/修复 M 项
|
|
201
|
-
- 第 3 轮:稳定/剩余 K 项
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
## 全局准则
|
|
205
|
-
|
|
206
|
-
- **一个 Activity = 一个主页面文件**(仅最小编号那张快照)
|
|
207
|
-
- **严格保真**:layout 结构、组件层级、资源引用必须与 Android 源一一对应;不增删/重排 UI 元素
|
|
208
|
-
- **资源精确匹配**:所有 color/string/dimension/image 必须有对应 HarmonyOS 资源;不要硬编码
|
|
209
|
-
- **可编译性是必须的**:已实现目标接真实事件;未实现才 mock 至可编译
|
|
210
|
-
- 仅用声明式 ArkUI;绝不写命令式 DOM 操作
|
|
211
|
-
- 保留原 Android 视觉与 UX 保真度
|
|
212
|
-
- 资源一律 `$r('app.type.name')`
|
|
213
|
-
- 留 `// TODO:` 注释给:业务逻辑、ViewModel 数据绑定、未转换的目标 Activity
|
|
214
|
-
- 响应式布局:优先百分比宽度 + Flex,少用固定尺寸
|
|
215
|
-
- 无障碍:`content-desc` → `.accessibilityText()`,有意义的 `text` → 正确的 label
|
|
216
|
-
- 嵌套 > 5 层时把相关 UI 拆为 `@Component` 子组件
|
|
217
|
-
- 任一 mapping 文件缺失/为空:用内置知识继续,并在报告中注明
|