@harmonyos-arkts/d2h 0.1.11 → 0.1.13

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.
Files changed (126) hide show
  1. package/.claude-plugin/marketplace.json +15 -13
  2. package/.claude-plugin/plugin.json +13 -7
  3. package/AGENTS.md +5 -5
  4. package/README.md +212 -212
  5. package/agents/android-to-hmos-00-orchestrator.md +318 -318
  6. package/agents/d2h.md +157 -157
  7. package/dist/bin/install-opencode.js +30 -30
  8. package/docs/environment-check.md +86 -86
  9. package/install-opencode.sh +160 -160
  10. package/opencode/agents.json +16 -16
  11. package/opencode/vscode-d2h-package.json +7 -7
  12. package/opencode/vscode-d2h-plugin.mjs +29 -29
  13. package/package.json +54 -54
  14. package/schemas/android-source-manifest.schema.json +25 -25
  15. package/schemas/checkpoint-provenance.schema.json +67 -67
  16. package/schemas/final-acceptance.schema.json +29 -29
  17. package/schemas/managed-evidence-index.schema.json +15 -15
  18. package/schemas/managed-evidence.schema.json +114 -114
  19. package/schemas/migration-config.schema.json +51 -51
  20. package/schemas/migration-report-index.schema.json +39 -39
  21. package/schemas/migration-report-item.schema.json +51 -51
  22. package/schemas/migration-report-summary.schema.json +25 -25
  23. package/schemas/migration-status.schema.json +202 -202
  24. package/schemas/preflight.schema.json +60 -60
  25. package/schemas/source-order-audit.schema.json +16 -16
  26. package/schemas/source-provenance-event.schema.json +19 -19
  27. package/schemas/spec-app-shard.schema.json +45 -45
  28. package/schemas/spec-fact-corrections-shard.schema.json +13 -13
  29. package/schemas/spec-features-shard.schema.json +43 -43
  30. package/schemas/spec-index.schema.json +26 -26
  31. package/schemas/spec-interactions-shard.schema.json +40 -40
  32. package/schemas/spec-page.schema.json +98 -98
  33. package/schemas/spec-pages-index.schema.json +1 -1
  34. package/schemas/spec-unresolved-shard.schema.json +1 -1
  35. package/schemas/task-envelope.schema.json +109 -109
  36. package/schemas/task-plan.schema.json +341 -341
  37. package/scripts/check-environment.mjs +320 -320
  38. package/skills/android2hmos_resources_convert/SKILL.md +180 -180
  39. package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -230
  40. package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -175
  41. package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -513
  42. package/skills/arkts-app-identity/SKILL.md +238 -238
  43. package/skills/arkts-i18n/SKILL.md +496 -496
  44. package/skills/arkts-i18n/evals/evals.json +84 -84
  45. package/skills/arkts-i18n/references/code-examples.md +302 -302
  46. package/skills/arkts-i18n/references/common-pitfalls.md +391 -391
  47. package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -604
  48. package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -348
  49. package/skills/arkts-i18n/references/language-codes.md +104 -104
  50. package/skills/arkts-i18n/references/resource-file-structure.md +775 -775
  51. package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -242
  52. package/skills/arkts-i18n/references/v1-compat.md +244 -244
  53. package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -174
  54. package/skills/arkts-icon-sizing/SKILL.md +211 -211
  55. package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -131
  56. package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -88
  57. package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -179
  58. package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -119
  59. package/skills/arkts-mvvm-architecture/SKILL.md +613 -613
  60. package/skills/d2h-hypium-forge/SKILL.md +309 -309
  61. package/skills/d2h-hypium-forge/references/functional-spec-template.md +140 -140
  62. package/skills/d2h-hypium-forge/references/official-api.md +135 -135
  63. package/skills/d2h-hypium-forge/references/pitfalls.md +98 -98
  64. package/skills/d2h-hypium-forge/references/testability-template.md +71 -71
  65. package/skills/d2h-hypium-forge/scripts/_common_template.py +659 -659
  66. package/skills/d2h-hypium-forge/scripts/analyze_results.py +165 -165
  67. package/skills/d2h-hypium-forge/scripts/assert_audit.py +107 -107
  68. package/skills/d2h-hypium-forge/scripts/collect_runtime_pages.py +169 -169
  69. package/skills/d2h-hypium-forge/scripts/forge_report.py +325 -325
  70. package/skills/d2h-hypium-forge/scripts/inject_resource_ids.py +114 -114
  71. package/skills/d2h-hypium-forge/scripts/lint_asserts.py +80 -80
  72. package/skills/d2h-hypium-forge/scripts/lint_locate.py +60 -60
  73. package/skills/d2h-hypium-forge/scripts/merge_appgraph.py +249 -249
  74. package/skills/d2h-hypium-forge/scripts/precondition.py +140 -140
  75. package/skills/d2h-hypium-forge/scripts/preinject_ids.py +232 -232
  76. package/skills/d2h-hypium-forge/scripts/probe.py +197 -197
  77. package/skills/d2h-hypium-forge/scripts/run_direct.py +823 -823
  78. package/skills/d2h-hypium-forge/scripts/run_parallel.py +121 -121
  79. package/skills/d2h-hypium-forge/scripts/scan_features.py +255 -255
  80. package/skills/d2h-hypium-forge/scripts/upgrade_asserts.py +66 -66
  81. package/skills/d2h-hypium-forge/scripts/validate_scenarios.py +84 -84
  82. package/skills/d2h-page-develop/SKILL.md +112 -112
  83. package/skills/d2h-page-develop/agents/openai.yaml +7 -7
  84. package/skills/d2h-page-spec-generate/SKILL.md +206 -206
  85. package/skills/d2h-page-spec-generate/scripts/build_batch_report.py +227 -227
  86. package/skills/d2h-utg-ai-generate/SKILL.md +128 -128
  87. package/skills/d2h-utg-ai-generate/references/click-paths-schema.json +76 -76
  88. package/skills/d2h-utg-ai-generate/references/extraction-guide.md +34 -34
  89. package/skills/d2h-utg-ai-generate/references/facts-contract.md +96 -96
  90. package/skills/d2h-utg-ai-generate/references/page-schema.json +61 -61
  91. package/skills/d2h-utg-ai-generate/references/utg-schema.json +89 -89
  92. package/skills/d2h-utg-ai-generate/scripts/build_page_json.py +314 -314
  93. package/skills/d2h-utg-ai-generate/scripts/build_utg.py +123 -123
  94. package/skills/d2h-utg-ai-generate/scripts/capture_from_page_json.ts +280 -280
  95. package/skills/d2h-utg-ai-generate/scripts/utg_common.py +524 -524
  96. package/skills/d2h-utg-ai-generate/scripts/utg_html.py +489 -489
  97. package/skills/d2h-utg-ai-generate/scripts/validate_utg.py +69 -69
  98. package/skills/d2h-utg-ai-generate/scripts/vendor/D3-LICENSE +13 -13
  99. package/skills/d2h-utg-ai-generate/scripts/vendor/d3-7.9.0.min.js +2 -2
  100. package/skills/d2h-utg-ai-generate/tests/test_pipeline.py +369 -369
  101. package/skills/d2h-utg-static-generate/SKILL.md +73 -73
  102. package/skills/d2h-utg-static-generate/references/discoveries.md +38 -38
  103. package/skills/d2h-utg-static-generate/scripts/generate.py +499 -499
  104. package/skills/d2h-utg-static-generate/tests/test_static.py +335 -335
  105. package/skills/d2h-utg-static-generate/tests/verify-offline-html.py +60 -60
  106. package/skills/d2h-utg-static-generate/tests/verify-produced-fixture.py +131 -131
  107. package/skills/harmonyos-migration-playbook/SKILL.md +83 -83
  108. package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -7
  109. package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -24
  110. package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -53
  111. package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -45
  112. package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -23
  113. package/skills/harmonyos-migration-playbook/references/ui-automation.md +53 -53
  114. package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -38
  115. package/skills/maintaining-migration-report/SKILL.md +144 -144
  116. package/skills/maintaining-migration-report/references/final-acceptance.md +48 -48
  117. package/skills/maintaining-migration-report/references/source-provenance.md +66 -66
  118. package/skills/managing-d2h-toolchain/SKILL.md +24 -24
  119. package/skills/managing-d2h-toolchain/environment-requirements.json +16 -16
  120. package/skills/managing-d2h-toolchain/scripts/manage-toolchain.mjs +98 -98
  121. package/skills/managing-d2h-toolchain/toolchain.json +16 -16
  122. package/skills/native-library-substitution/SKILL.md +384 -384
  123. package/skills/native-library-substitution/references/native-library-substitution.json +56905 -56905
  124. package/skills/native-library-substitution/references/native-library-substitution.md +163 -163
  125. package/skills/preparing-migration-workspace/SKILL.md +131 -131
  126. package/skills/reviewing-migration-process/SKILL.md +62 -62
@@ -1,230 +1,230 @@
1
- # Android to HarmonyOS Image Resource Conversion Rules
2
-
3
- Complete rules for converting Android **image resources** — bitmap files, nine-patch images, launcher icons (mipmap), adaptive icons — into HarmonyOS `media/` resources. XML drawables → SVG are covered separately in `xml-drawable-to-svg-rules.md`; this document defines what routes to SVG, plus the qualifier directory algorithm and reference resolution needed by image conversion.
4
-
5
- ## Table of Contents
6
- 1. [Target Directory Structure](#target-directory-structure)
7
- 2. [Bitmap Images → media](#bitmap-images--media)
8
- 3. [Nine-Patch Images](#nine-patch-images)
9
- 4. [Mipmap → media](#mipmap--media)
10
- 5. [Adaptive Icon → Layered-Image JSON](#adaptive-icon--layered-image-json)
11
- 6. [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables)
12
- 7. [Qualifier Mapping](#qualifier-mapping)
13
- 8. [Reference Resolution for Images](#reference-resolution-for-images)
14
- 9. [Special Cases and Edge Cases](#special-cases-and-edge-cases)
15
-
16
- ---
17
-
18
- ## Target Directory Structure
19
-
20
- HarmonyOS image resources live under the `media/` resource group:
21
-
22
- ```
23
- entry/src/main/resources/
24
- ├── base/media/ # default images (no qualifier)
25
- ├── ldpi/media/ # density-qualified
26
- ├── dark/media/ # night mode
27
- ├── zh_CN/media/ # locale-qualified
28
- └── zh_CN-dark-ldpi/media/ # combined qualifiers
29
- ```
30
-
31
- HarmonyOS supported image formats: PNG, JPG, GIF, SVG, WEBP, BMP — all common Android bitmap formats copy directly.
32
-
33
- ---
34
-
35
- ## Bitmap Images → media
36
-
37
- Copy image files from Android `drawable*/` (and `mipmap*/`) to the qualifier-mapped `media/` directory:
38
-
39
- - `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp` → direct copy, filename unchanged
40
- - Resource names are preserved exactly as Android spells them — they are identifiers
41
- - Do NOT copy XML files in this step — classify them first (see [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables))
42
-
43
- ---
44
-
45
- ## Nine-Patch Images
46
-
47
- Android nine-patch files (`.9.png`) have no native HarmonyOS equivalent:
48
-
49
- 1. **Rename**: `.9.png` → `<name>_9.png`. HarmonyOS forbids `.` in resource filenames except the final extension separator, so `btn_bg.9.png` would be invalid — it becomes `btn_bg_9.png`.
50
- 2. **Semantics lost**: the 1-px stretch/padding border markers are rendered as ordinary pixels; nine-patch stretching behavior does not carry over. The developer must implement stretching at the ArkUI component level (`.borderRadius` + `.resizable` or 9-slice via `ResizableOptions` where supported).
51
- 3. Always log nine-patch conversions in the report with the rename and the lost-semantics note.
52
-
53
- ---
54
-
55
- ## Mipmap → media
56
-
57
- Launcher icons from `mipmap*/` directories go to the qualifier-mapped `media/` directory:
58
-
59
- - PNG/WEBP launcher icons (`ic_launcher.png`, `ic_launcher_round.png`, `ic_launcher_foreground.png`, `ic_launcher_background.png`, …) → direct copy
60
- - **Adaptive icon XML** (root `<adaptive-icon>`, commonly in `mipmap-anydpi-v26/`) → layered-image JSON (see below)
61
- - **VectorDrawable XML in mipmap** (root `<vector>`) → convert to SVG exactly like drawable vectors
62
-
63
- **Common pattern**: `mipmap-anydpi-v26/` is extremely common. Parse the qualifier as `anydpi` (→ `base`) + `v26` (→ strip) → target `base/media/`. Classify the files inside by their XML root element, never by the directory name.
64
-
65
- ---
66
-
67
- ## Adaptive Icon → Layered-Image JSON
68
-
69
- Convert `<adaptive-icon>` XML to `<original_name>_layered_image.json` in the target `media/` directory.
70
-
71
- **Conversion rules:**
72
-
73
- 1. Parse the XML and extract the `<background>` and `<foreground>` elements.
74
- 2. Only `background` and `foreground` are valid keys in the output JSON. Skip `monochrome` (and any other elements) **from the JSON output** — HarmonyOS layered-image has no equivalent. However, the resource *referenced* by `monochrome` is a real asset that must still be converted as a normal image.
75
- 3. Convert references:
76
- - `@drawable/xxx` → `$media:xxx`
77
- - `@mipmap/xxx` → `$media:xxx`
78
- - `@color/xxx` → **generate a solid-color PNG** (below) — layered-image only accepts `$media:` references, never `$color:`
79
- 4. **Color background handling (critical)**:
80
- a. Resolve `@color/xxx` to its hex value from `values*/colors.xml`, following reference chains to a concrete value.
81
- b. Generate a solid-color PNG filled entirely with that hex (any small size, e.g. 1×1 or 108×108 px).
82
- c. Save as `<color_name>_bg.png` in `base/media/`.
83
- d. Reference it as `$media:<color_name>_bg` in the JSON.
84
- e. If the color cannot be resolved (library resource, source `res/` fallback), log it as an unresolved dependency in the report.
85
- 5. A layer may carry artwork as a **child element** instead of an `android:drawable` attribute (`<foreground><vector …/></foreground>`). Convert that inline drawable to its own media file named `<icon_name>_<layer>.svg` and reference it as `$media:<icon_name>_<layer>`. Never emit the JSON with the layer simply absent. A `<bitmap android:src="@drawable/x"/>` wrapper is just a reference — point at `$media:x`.
86
-
87
- **Example:**
88
-
89
- ```xml
90
- <!-- mipmap-anydpi-v26/ic_launcher.xml -->
91
- <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
92
- <background android:drawable="@color/md_orange_700" />
93
- <foreground android:drawable="@mipmap/ic_launcher_foreground" />
94
- <monochrome android:drawable="@drawable/ic_launcher_monochrome" />
95
- </adaptive-icon>
96
- ```
97
-
98
- Suppose `@color/md_orange_700` resolves to `#F57C00`. Generate `base/media/md_orange_700_bg.png` (solid `#F57C00`), then write `base/media/ic_launcher_layered_image.json`:
99
-
100
- ```json
101
- {
102
- "layered-image": {
103
- "background": "$media:md_orange_700_bg",
104
- "foreground": "$media:ic_launcher_foreground"
105
- }
106
- }
107
- ```
108
-
109
- `@drawable/ic_launcher_monochrome` is additionally converted as a normal drawable (SVG or bitmap per its type) even though it never appears in the JSON.
110
-
111
- ---
112
-
113
- ## Identifying XML vs Image Drawables
114
-
115
- In `drawable*/` and `mipmap*/` directories, classify every `.xml` file by its **root element** (never by filename or directory):
116
-
117
- | Root element | Type | Route |
118
- |---|---|---|
119
- | `<vector>` | VectorDrawable | → SVG (`xml-drawable-to-svg-rules.md`) |
120
- | `<shape>` | ShapeDrawable | → SVG |
121
- | `<layer-list>` | LayerListDrawable | → SVG |
122
- | `<selector>` | StateListDrawable | Extract default state → SVG; log dropped state variants |
123
- | `<adaptive-icon>` | Adaptive icon | → layered-image JSON (above) |
124
- | `<ripple>`, `<animated-vector>`, `<animated-selector>`, `<transition>`, `<level-list>`, `<scale>`, `<rotate>`, `<inset>` | Runtime behavior | Unmappable — log. Exception: `<inset>`/`<rotate>` with fixed values may be applied statically and the inner drawable converted; log either way |
125
-
126
- When several drawable directories provide the same resource name (`drawable/x.xml` and `drawable-anydpi/x.xml` both landing in `base/media/`), the source with **fewer qualifiers** wins; record the loser in the report as a collision — never silently drop it.
127
-
128
- ---
129
-
130
- ## Qualifier Mapping
131
-
132
- Android uses qualifier suffixes on resource directories; HarmonyOS uses a different qualifier naming scheme. Images are always placed under the `media/` group of the mapped qualifier directory.
133
-
134
- ### Density Qualifiers
135
-
136
- The DPI range labels differ between platforms:
137
-
138
- | Android Qualifier | Approx DPI | HarmonyOS Qualifier | HarmonyOS DPI Range |
139
- |---|---|---|---|
140
- | `ldpi` | 120 | `sdpi` | (0, 120] |
141
- | `mdpi` | 160 | `mdpi` | (120, 160] |
142
- | `tvdpi` | 213 | `ldpi` | (160, 240] |
143
- | `hdpi` | 240 | `ldpi` | (160, 240] |
144
- | `xhdpi` | 320 | `xldpi` | (240, 320] |
145
- | `xxhdpi` | 480 | `xxldpi` | (320, 480] |
146
- | `xxxhdpi` | 640 | `xxxldpi` | (480, 640] |
147
- | `nodpi` | N/A | `base` | No density qualifier |
148
- | `anydpi` | N/A | `base` | No density qualifier |
149
-
150
- Note the deliberate mismatch: Android `hdpi` (~240) maps to HarmonyOS **`ldpi`** because HarmonyOS labels the (160, 240] range `ldpi`.
151
-
152
- ### Language/Region Qualifiers
153
-
154
- | Android | HarmonyOS |
155
- |---|---|
156
- | `drawable-ar` | `ar/media/` |
157
- | `drawable-zh` | `zh/media/` |
158
- | `drawable-zh-rCN` | `zh_CN/media/` |
159
- | `drawable-zh-rTW` | `zh_TW/media/` |
160
- | `drawable-en-rUS` | `en_US/media/` |
161
- | `drawable-fr-rFR` | `fr_FR/media/` |
162
-
163
- Parsing: language = 2–3 lowercase letters (ISO 639); region = `r` + 2 uppercase letters, strip the `r`; script = `b+sr+Latn` → `sr_Latn`. Language and region join with `_`.
164
-
165
- ### Orientation Qualifiers
166
-
167
- | Android | HarmonyOS |
168
- |---|---|
169
- | `land` | `horizontal` |
170
- | `port` | `vertical` |
171
-
172
- ### Night Mode Qualifiers
173
-
174
- | Android | HarmonyOS |
175
- |---|---|
176
- | `night` | `dark` |
177
- | `notnight` | `light` |
178
-
179
- ### Combining Multiple Qualifiers
180
-
181
- **Parsing algorithm for any Android drawable/mipmap directory name:**
182
-
183
- 1. Split the directory name by `-`; the first segment is the resource type
184
- 2. Parse remaining qualifiers left-to-right: language/region, density, orientation, night mode, API level (`v<N>`), screen-size constraints
185
- 3. Map each recognized qualifier to its HarmonyOS equivalent
186
- 4. **Strip silently** (do not reject): API level `v<N>`, layout direction (`ldrtl`/`ldltr`), screen shape (`round`/`notround`), HDR (`highdr`/`lowdr`), aspect (`long`/`notlong`)
187
- 5. **Skip the entire directory** (mark all resources unmapped) when screen-size qualifiers remain: `sw<N>dp`, `w<N>dp`, `h<N>dp`, `small`, `normal`, `large`, `xlarge` — HarmonyOS has no equivalent; do NOT convert them to `base/`
188
- 6. If no qualifiers remain after stripping, use `base/`
189
- 7. Reassemble in HarmonyOS order: MCC_MNC-language_script_country/region-orientation-device-colormode-density
190
-
191
- **Examples:**
192
-
193
- | Android Directory | Qualifiers | HarmonyOS Target |
194
- |---|---|---|
195
- | `drawable-hdpi` | hdpi→ldpi | `ldpi/media/` |
196
- | `mipmap-anydpi-v26` | anydpi→base, v26 stripped | `base/media/` |
197
- | `drawable-zh-rCN-night-hdpi` | zh_CN, dark, ldpi | `zh_CN-dark-ldpi/media/` |
198
- | `drawable-night-v21` | dark, v21 stripped | `dark/media/` |
199
- | `drawable-hdpi-v4` | ldpi, v4 stripped | `ldpi/media/` |
200
- | `drawable-sw600dp` | **skip — unmapped** | N/A |
201
-
202
- Never reject a directory just because one qualifier is unsupported — strip it and continue.
203
-
204
- ---
205
-
206
- ## Reference Resolution for Images
207
-
208
- Image conversion encounters value references in three places. Build a lookup table from `values*/colors.xml` and `values*/dimens.xml` before converting drawables.
209
-
210
- **In converted SVG files:**
211
- - `@color/name` used as `fillColor`/`strokeColor` → resolve to the hex value during SVG generation (follow chains; fallback `#000000` + log)
212
- - `@dimen/name` used as a dimension → resolve to the numeric value (fallback: sensible default + log)
213
- - `@android:color/*` system colors → map known ones (`white`→`#FFFFFF`, `black`→`#000000`, `transparent`→`#00000000`); unknown → `#000000` + log
214
-
215
- **In layered-image JSON files:**
216
- - `@drawable/xxx` / `@mipmap/xxx` → keep as `$media:xxx` (file references — the files themselves are converted)
217
- - `@color/xxx` → resolve to hex and generate the solid-color PNG (see adaptive icon rules)
218
-
219
- **Reference chain depth**: follow at most 5 levels (`A → B → C → concrete value`). Unresolved references are expected — library resources are absent from source `res/`; record them in the report with likely library provenance (e.g. `@drawable/abc_*` → AndroidX AppCompat, `@drawable/mtrl_*`/`design_*` → Material Components) so the developer can supply them manually.
220
-
221
- ---
222
-
223
- ## Special Cases and Edge Cases
224
-
225
- - **Same name in multiple density directories** (`drawable-hdpi/x.png`, `drawable-xxhdpi/x.png`) → each copies to its own qualifier directory (`ldpi/media/x.png`, `xxldpi/media/x.png`) — no conflict.
226
- - **Same name from qualified and unqualified dirs** (`drawable/x.png` and `drawable-anydpi/x.xml` both → `base/media/`) → the source with fewer qualifiers wins; the loser is recorded as a collision.
227
- - **Existing files in the HarmonyOS project**: merge, do not overwrite — an existing media file with the same name keeps its position; the attempted copy is logged as a collision for the developer to review.
228
- - **Empty/corrupt image files**: skip and log; do not copy zero-byte files into `media/`.
229
- - **Filenames illegal in HarmonyOS** (`.` inside the name, leading digits are fine but uppercase is kept as-is): only the `.9.png` rename is automatic; any other illegal name is copied with a logged `unmappable` entry explaining the manual rename needed.
230
- - **Large image sets**: first inventory, then group by (classification × qualifier), then convert group by group, then verify — never convert while still scanning.
1
+ # Android to HarmonyOS Image Resource Conversion Rules
2
+
3
+ Complete rules for converting Android **image resources** — bitmap files, nine-patch images, launcher icons (mipmap), adaptive icons — into HarmonyOS `media/` resources. XML drawables → SVG are covered separately in `xml-drawable-to-svg-rules.md`; this document defines what routes to SVG, plus the qualifier directory algorithm and reference resolution needed by image conversion.
4
+
5
+ ## Table of Contents
6
+ 1. [Target Directory Structure](#target-directory-structure)
7
+ 2. [Bitmap Images → media](#bitmap-images--media)
8
+ 3. [Nine-Patch Images](#nine-patch-images)
9
+ 4. [Mipmap → media](#mipmap--media)
10
+ 5. [Adaptive Icon → Layered-Image JSON](#adaptive-icon--layered-image-json)
11
+ 6. [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables)
12
+ 7. [Qualifier Mapping](#qualifier-mapping)
13
+ 8. [Reference Resolution for Images](#reference-resolution-for-images)
14
+ 9. [Special Cases and Edge Cases](#special-cases-and-edge-cases)
15
+
16
+ ---
17
+
18
+ ## Target Directory Structure
19
+
20
+ HarmonyOS image resources live under the `media/` resource group:
21
+
22
+ ```
23
+ entry/src/main/resources/
24
+ ├── base/media/ # default images (no qualifier)
25
+ ├── ldpi/media/ # density-qualified
26
+ ├── dark/media/ # night mode
27
+ ├── zh_CN/media/ # locale-qualified
28
+ └── zh_CN-dark-ldpi/media/ # combined qualifiers
29
+ ```
30
+
31
+ HarmonyOS supported image formats: PNG, JPG, GIF, SVG, WEBP, BMP — all common Android bitmap formats copy directly.
32
+
33
+ ---
34
+
35
+ ## Bitmap Images → media
36
+
37
+ Copy image files from Android `drawable*/` (and `mipmap*/`) to the qualifier-mapped `media/` directory:
38
+
39
+ - `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.bmp` → direct copy, filename unchanged
40
+ - Resource names are preserved exactly as Android spells them — they are identifiers
41
+ - Do NOT copy XML files in this step — classify them first (see [Identifying XML vs Image Drawables](#identifying-xml-vs-image-drawables))
42
+
43
+ ---
44
+
45
+ ## Nine-Patch Images
46
+
47
+ Android nine-patch files (`.9.png`) have no native HarmonyOS equivalent:
48
+
49
+ 1. **Rename**: `.9.png` → `<name>_9.png`. HarmonyOS forbids `.` in resource filenames except the final extension separator, so `btn_bg.9.png` would be invalid — it becomes `btn_bg_9.png`.
50
+ 2. **Semantics lost**: the 1-px stretch/padding border markers are rendered as ordinary pixels; nine-patch stretching behavior does not carry over. The developer must implement stretching at the ArkUI component level (`.borderRadius` + `.resizable` or 9-slice via `ResizableOptions` where supported).
51
+ 3. Always log nine-patch conversions in the report with the rename and the lost-semantics note.
52
+
53
+ ---
54
+
55
+ ## Mipmap → media
56
+
57
+ Launcher icons from `mipmap*/` directories go to the qualifier-mapped `media/` directory:
58
+
59
+ - PNG/WEBP launcher icons (`ic_launcher.png`, `ic_launcher_round.png`, `ic_launcher_foreground.png`, `ic_launcher_background.png`, …) → direct copy
60
+ - **Adaptive icon XML** (root `<adaptive-icon>`, commonly in `mipmap-anydpi-v26/`) → layered-image JSON (see below)
61
+ - **VectorDrawable XML in mipmap** (root `<vector>`) → convert to SVG exactly like drawable vectors
62
+
63
+ **Common pattern**: `mipmap-anydpi-v26/` is extremely common. Parse the qualifier as `anydpi` (→ `base`) + `v26` (→ strip) → target `base/media/`. Classify the files inside by their XML root element, never by the directory name.
64
+
65
+ ---
66
+
67
+ ## Adaptive Icon → Layered-Image JSON
68
+
69
+ Convert `<adaptive-icon>` XML to `<original_name>_layered_image.json` in the target `media/` directory.
70
+
71
+ **Conversion rules:**
72
+
73
+ 1. Parse the XML and extract the `<background>` and `<foreground>` elements.
74
+ 2. Only `background` and `foreground` are valid keys in the output JSON. Skip `monochrome` (and any other elements) **from the JSON output** — HarmonyOS layered-image has no equivalent. However, the resource *referenced* by `monochrome` is a real asset that must still be converted as a normal image.
75
+ 3. Convert references:
76
+ - `@drawable/xxx` → `$media:xxx`
77
+ - `@mipmap/xxx` → `$media:xxx`
78
+ - `@color/xxx` → **generate a solid-color PNG** (below) — layered-image only accepts `$media:` references, never `$color:`
79
+ 4. **Color background handling (critical)**:
80
+ a. Resolve `@color/xxx` to its hex value from `values*/colors.xml`, following reference chains to a concrete value.
81
+ b. Generate a solid-color PNG filled entirely with that hex (any small size, e.g. 1×1 or 108×108 px).
82
+ c. Save as `<color_name>_bg.png` in `base/media/`.
83
+ d. Reference it as `$media:<color_name>_bg` in the JSON.
84
+ e. If the color cannot be resolved (library resource, source `res/` fallback), log it as an unresolved dependency in the report.
85
+ 5. A layer may carry artwork as a **child element** instead of an `android:drawable` attribute (`<foreground><vector …/></foreground>`). Convert that inline drawable to its own media file named `<icon_name>_<layer>.svg` and reference it as `$media:<icon_name>_<layer>`. Never emit the JSON with the layer simply absent. A `<bitmap android:src="@drawable/x"/>` wrapper is just a reference — point at `$media:x`.
86
+
87
+ **Example:**
88
+
89
+ ```xml
90
+ <!-- mipmap-anydpi-v26/ic_launcher.xml -->
91
+ <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
92
+ <background android:drawable="@color/md_orange_700" />
93
+ <foreground android:drawable="@mipmap/ic_launcher_foreground" />
94
+ <monochrome android:drawable="@drawable/ic_launcher_monochrome" />
95
+ </adaptive-icon>
96
+ ```
97
+
98
+ Suppose `@color/md_orange_700` resolves to `#F57C00`. Generate `base/media/md_orange_700_bg.png` (solid `#F57C00`), then write `base/media/ic_launcher_layered_image.json`:
99
+
100
+ ```json
101
+ {
102
+ "layered-image": {
103
+ "background": "$media:md_orange_700_bg",
104
+ "foreground": "$media:ic_launcher_foreground"
105
+ }
106
+ }
107
+ ```
108
+
109
+ `@drawable/ic_launcher_monochrome` is additionally converted as a normal drawable (SVG or bitmap per its type) even though it never appears in the JSON.
110
+
111
+ ---
112
+
113
+ ## Identifying XML vs Image Drawables
114
+
115
+ In `drawable*/` and `mipmap*/` directories, classify every `.xml` file by its **root element** (never by filename or directory):
116
+
117
+ | Root element | Type | Route |
118
+ |---|---|---|
119
+ | `<vector>` | VectorDrawable | → SVG (`xml-drawable-to-svg-rules.md`) |
120
+ | `<shape>` | ShapeDrawable | → SVG |
121
+ | `<layer-list>` | LayerListDrawable | → SVG |
122
+ | `<selector>` | StateListDrawable | Extract default state → SVG; log dropped state variants |
123
+ | `<adaptive-icon>` | Adaptive icon | → layered-image JSON (above) |
124
+ | `<ripple>`, `<animated-vector>`, `<animated-selector>`, `<transition>`, `<level-list>`, `<scale>`, `<rotate>`, `<inset>` | Runtime behavior | Unmappable — log. Exception: `<inset>`/`<rotate>` with fixed values may be applied statically and the inner drawable converted; log either way |
125
+
126
+ When several drawable directories provide the same resource name (`drawable/x.xml` and `drawable-anydpi/x.xml` both landing in `base/media/`), the source with **fewer qualifiers** wins; record the loser in the report as a collision — never silently drop it.
127
+
128
+ ---
129
+
130
+ ## Qualifier Mapping
131
+
132
+ Android uses qualifier suffixes on resource directories; HarmonyOS uses a different qualifier naming scheme. Images are always placed under the `media/` group of the mapped qualifier directory.
133
+
134
+ ### Density Qualifiers
135
+
136
+ The DPI range labels differ between platforms:
137
+
138
+ | Android Qualifier | Approx DPI | HarmonyOS Qualifier | HarmonyOS DPI Range |
139
+ |---|---|---|---|
140
+ | `ldpi` | 120 | `sdpi` | (0, 120] |
141
+ | `mdpi` | 160 | `mdpi` | (120, 160] |
142
+ | `tvdpi` | 213 | `ldpi` | (160, 240] |
143
+ | `hdpi` | 240 | `ldpi` | (160, 240] |
144
+ | `xhdpi` | 320 | `xldpi` | (240, 320] |
145
+ | `xxhdpi` | 480 | `xxldpi` | (320, 480] |
146
+ | `xxxhdpi` | 640 | `xxxldpi` | (480, 640] |
147
+ | `nodpi` | N/A | `base` | No density qualifier |
148
+ | `anydpi` | N/A | `base` | No density qualifier |
149
+
150
+ Note the deliberate mismatch: Android `hdpi` (~240) maps to HarmonyOS **`ldpi`** because HarmonyOS labels the (160, 240] range `ldpi`.
151
+
152
+ ### Language/Region Qualifiers
153
+
154
+ | Android | HarmonyOS |
155
+ |---|---|
156
+ | `drawable-ar` | `ar/media/` |
157
+ | `drawable-zh` | `zh/media/` |
158
+ | `drawable-zh-rCN` | `zh_CN/media/` |
159
+ | `drawable-zh-rTW` | `zh_TW/media/` |
160
+ | `drawable-en-rUS` | `en_US/media/` |
161
+ | `drawable-fr-rFR` | `fr_FR/media/` |
162
+
163
+ Parsing: language = 2–3 lowercase letters (ISO 639); region = `r` + 2 uppercase letters, strip the `r`; script = `b+sr+Latn` → `sr_Latn`. Language and region join with `_`.
164
+
165
+ ### Orientation Qualifiers
166
+
167
+ | Android | HarmonyOS |
168
+ |---|---|
169
+ | `land` | `horizontal` |
170
+ | `port` | `vertical` |
171
+
172
+ ### Night Mode Qualifiers
173
+
174
+ | Android | HarmonyOS |
175
+ |---|---|
176
+ | `night` | `dark` |
177
+ | `notnight` | `light` |
178
+
179
+ ### Combining Multiple Qualifiers
180
+
181
+ **Parsing algorithm for any Android drawable/mipmap directory name:**
182
+
183
+ 1. Split the directory name by `-`; the first segment is the resource type
184
+ 2. Parse remaining qualifiers left-to-right: language/region, density, orientation, night mode, API level (`v<N>`), screen-size constraints
185
+ 3. Map each recognized qualifier to its HarmonyOS equivalent
186
+ 4. **Strip silently** (do not reject): API level `v<N>`, layout direction (`ldrtl`/`ldltr`), screen shape (`round`/`notround`), HDR (`highdr`/`lowdr`), aspect (`long`/`notlong`)
187
+ 5. **Skip the entire directory** (mark all resources unmapped) when screen-size qualifiers remain: `sw<N>dp`, `w<N>dp`, `h<N>dp`, `small`, `normal`, `large`, `xlarge` — HarmonyOS has no equivalent; do NOT convert them to `base/`
188
+ 6. If no qualifiers remain after stripping, use `base/`
189
+ 7. Reassemble in HarmonyOS order: MCC_MNC-language_script_country/region-orientation-device-colormode-density
190
+
191
+ **Examples:**
192
+
193
+ | Android Directory | Qualifiers | HarmonyOS Target |
194
+ |---|---|---|
195
+ | `drawable-hdpi` | hdpi→ldpi | `ldpi/media/` |
196
+ | `mipmap-anydpi-v26` | anydpi→base, v26 stripped | `base/media/` |
197
+ | `drawable-zh-rCN-night-hdpi` | zh_CN, dark, ldpi | `zh_CN-dark-ldpi/media/` |
198
+ | `drawable-night-v21` | dark, v21 stripped | `dark/media/` |
199
+ | `drawable-hdpi-v4` | ldpi, v4 stripped | `ldpi/media/` |
200
+ | `drawable-sw600dp` | **skip — unmapped** | N/A |
201
+
202
+ Never reject a directory just because one qualifier is unsupported — strip it and continue.
203
+
204
+ ---
205
+
206
+ ## Reference Resolution for Images
207
+
208
+ Image conversion encounters value references in three places. Build a lookup table from `values*/colors.xml` and `values*/dimens.xml` before converting drawables.
209
+
210
+ **In converted SVG files:**
211
+ - `@color/name` used as `fillColor`/`strokeColor` → resolve to the hex value during SVG generation (follow chains; fallback `#000000` + log)
212
+ - `@dimen/name` used as a dimension → resolve to the numeric value (fallback: sensible default + log)
213
+ - `@android:color/*` system colors → map known ones (`white`→`#FFFFFF`, `black`→`#000000`, `transparent`→`#00000000`); unknown → `#000000` + log
214
+
215
+ **In layered-image JSON files:**
216
+ - `@drawable/xxx` / `@mipmap/xxx` → keep as `$media:xxx` (file references — the files themselves are converted)
217
+ - `@color/xxx` → resolve to hex and generate the solid-color PNG (see adaptive icon rules)
218
+
219
+ **Reference chain depth**: follow at most 5 levels (`A → B → C → concrete value`). Unresolved references are expected — library resources are absent from source `res/`; record them in the report with likely library provenance (e.g. `@drawable/abc_*` → AndroidX AppCompat, `@drawable/mtrl_*`/`design_*` → Material Components) so the developer can supply them manually.
220
+
221
+ ---
222
+
223
+ ## Special Cases and Edge Cases
224
+
225
+ - **Same name in multiple density directories** (`drawable-hdpi/x.png`, `drawable-xxhdpi/x.png`) → each copies to its own qualifier directory (`ldpi/media/x.png`, `xxldpi/media/x.png`) — no conflict.
226
+ - **Same name from qualified and unqualified dirs** (`drawable/x.png` and `drawable-anydpi/x.xml` both → `base/media/`) → the source with fewer qualifiers wins; the loser is recorded as a collision.
227
+ - **Existing files in the HarmonyOS project**: merge, do not overwrite — an existing media file with the same name keeps its position; the attempted copy is logged as a collision for the developer to review.
228
+ - **Empty/corrupt image files**: skip and log; do not copy zero-byte files into `media/`.
229
+ - **Filenames illegal in HarmonyOS** (`.` inside the name, leading digits are fine but uppercase is kept as-is): only the `.9.png` rename is automatic; any other illegal name is copied with a logged `unmappable` entry explaining the manual rename needed.
230
+ - **Large image sets**: first inventory, then group by (classification × qualifier), then convert group by group, then verify — never convert while still scanning.