@harmonyos-arkts/d2h 0.0.0-stage → 0.1.0
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/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +7 -0
- package/README.md +168 -2
- package/agents/android-to-hmos-00-orchestrator.md +312 -0
- package/agents/d2h.md +168 -0
- package/bin/install-opencode.mjs +51 -0
- package/install-opencode.sh +160 -0
- package/opencode/agents.json +16 -0
- package/package.json +16 -4
- package/schemas/android-source-manifest.schema.json +25 -0
- package/schemas/checkpoint-provenance.schema.json +67 -0
- package/schemas/final-acceptance.schema.json +29 -0
- package/schemas/managed-evidence-index.schema.json +15 -0
- package/schemas/managed-evidence.schema.json +114 -0
- package/schemas/migration-config.schema.json +51 -0
- package/schemas/migration-report-index.schema.json +39 -0
- package/schemas/migration-report-item.schema.json +51 -0
- package/schemas/migration-report-summary.schema.json +25 -0
- package/schemas/migration-status.schema.json +202 -0
- package/schemas/preflight.schema.json +60 -0
- package/schemas/source-order-audit.schema.json +16 -0
- package/schemas/source-provenance-event.schema.json +19 -0
- package/schemas/spec-app-shard.schema.json +45 -0
- package/schemas/spec-fact-corrections-shard.schema.json +13 -0
- package/schemas/spec-features-shard.schema.json +43 -0
- package/schemas/spec-index.schema.json +26 -0
- package/schemas/spec-interactions-shard.schema.json +40 -0
- package/schemas/spec-page.schema.json +98 -0
- package/schemas/spec-pages-index.schema.json +1 -0
- package/schemas/spec-unresolved-shard.schema.json +1 -0
- package/schemas/task-envelope.schema.json +55 -0
- package/schemas/task-plan.schema.json +123 -0
- package/skills/android2hmos_resources_convert/SKILL.md +162 -0
- package/skills/android2hmos_resources_convert/references/image-conversion-rules.md +230 -0
- package/skills/android2hmos_resources_convert/references/svg-fix-patterns.md +175 -0
- package/skills/android2hmos_resources_convert/references/xml-drawable-to-svg-rules.md +513 -0
- package/skills/appgraph-rule-audit/SKILL.md +58 -0
- package/skills/appgraph-rule-audit/references/audit-contract.md +47 -0
- package/skills/appgraph-rule-audit/references/recommendation-schema.md +41 -0
- package/skills/appgraph-rule-audit/schemas/rule-opportunities.schema.json +125 -0
- package/skills/arkts-app-identity/SKILL.md +238 -0
- package/skills/arkts-i18n/SKILL.md +496 -0
- package/skills/arkts-i18n/evals/evals.json +84 -0
- package/skills/arkts-i18n/references/code-examples.md +302 -0
- package/skills/arkts-i18n/references/common-pitfalls.md +391 -0
- package/skills/arkts-i18n/references/dynamic-language-switch.md +604 -0
- package/skills/arkts-i18n/references/hardcoded-string-scanner.md +348 -0
- package/skills/arkts-i18n/references/language-codes.md +104 -0
- package/skills/arkts-i18n/references/resource-file-structure.md +775 -0
- package/skills/arkts-i18n/references/static-vs-dynamic.md +242 -0
- package/skills/arkts-i18n/references/v1-compat.md +244 -0
- package/skills/arkts-i18n/scripts/audit_i18n_completeness.sh +174 -0
- package/skills/arkts-icon-sizing/SKILL.md +211 -0
- package/skills/arkts-icon-sizing/scripts/icon_audit.py +131 -0
- package/skills/arkts-icon-sizing/scripts/icon_autofix.py +88 -0
- package/skills/arkts-icon-sizing/scripts/icon_dims.py +179 -0
- package/skills/arkts-icon-sizing/scripts/icon_fix.py +119 -0
- package/skills/arkts-mvvm-architecture/SKILL.md +613 -0
- package/skills/harmonyos-migration-playbook/SKILL.md +56 -0
- package/skills/harmonyos-migration-playbook/agents/openai.yaml +7 -0
- package/skills/harmonyos-migration-playbook/references/arkts-compile.md +24 -0
- package/skills/harmonyos-migration-playbook/references/harmony-runtime.md +53 -0
- package/skills/harmonyos-migration-playbook/references/lesson-lifecycle.md +45 -0
- package/skills/harmonyos-migration-playbook/references/protocol-e2e.md +23 -0
- package/skills/harmonyos-migration-playbook/references/ui-automation.md +52 -0
- package/skills/harmonyos-migration-playbook/references/windows-environment.md +38 -0
- package/skills/maintaining-migration-report/SKILL.md +155 -0
- package/skills/native-library-substitution/SKILL.md +385 -0
- package/skills/native-library-substitution/references/native-library-substitution.json +56906 -0
- package/skills/native-library-substitution/references/native-library-substitution.md +163 -0
- package/skills/preparing-migration-workspace/SKILL.md +124 -0
- package/skills/preparing-migration-workspace/toolchain.json +43 -0
- package/skills/reviewing-migration-process/SKILL.md +62 -0
- package/src/install-opencode.mjs +108 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
# Android XML Drawable to SVG Conversion Rules
|
|
2
|
+
|
|
3
|
+
This document provides complete rules for converting Android XML drawable resources to SVG format for HarmonyOS. HarmonyOS natively supports SVG in `media/` directories, making SVG the ideal target format for XML drawables that would otherwise be lost in migration.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
1. [General Principles](#general-principles)
|
|
7
|
+
2. [VectorDrawable Conversion](#vectordrawable-conversion)
|
|
8
|
+
3. [ShapeDrawable Conversion](#shapedrawable-conversion)
|
|
9
|
+
4. [LayerList Conversion](#layerlist-conversion)
|
|
10
|
+
5. [Selector / StateList Conversion](#selector--statelist-conversion)
|
|
11
|
+
6. [Gradient Conversion](#gradient-conversion)
|
|
12
|
+
7. [Common Attribute Mapping](#common-attribute-mapping)
|
|
13
|
+
8. [Unconvertible Types](#unconvertible-types)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## General Principles
|
|
18
|
+
|
|
19
|
+
- Output files use the original Android filename with `.svg` extension (e.g., `ic_arrow.xml` → `ic_arrow.svg`)
|
|
20
|
+
- All SVG output should use the SVG 1.1 namespace: `xmlns="http://www.w3.org/2000/svg"`
|
|
21
|
+
- Android uses `dp` units for dimensions; convert to unitless numbers in SVG (1dp = 1 SVG user unit)
|
|
22
|
+
- Android colors use `#AARRGGBB` or `#RRGGBB`; SVG uses `#RRGGBB` with a separate `opacity` or `fill-opacity` attribute for alpha
|
|
23
|
+
- When a drawable XML references another resource (`@drawable/other`, `@color/primary`), resolve the reference if possible. If not resolvable, use a reasonable default and log it in the report.
|
|
24
|
+
|
|
25
|
+
### Color Conversion (Android → SVG)
|
|
26
|
+
|
|
27
|
+
| Android Format | SVG Fill/Stroke | SVG Opacity |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `#RRGGBB` | `#RRGGBB` | (none, fully opaque) |
|
|
30
|
+
| `#AARRGGBB` | `#RRGGBB` | `opacity="AA/255"` as decimal (e.g., `#80FF0000` → `fill="#FF0000" fill-opacity="0.502"`) |
|
|
31
|
+
| `#RGB` | Expand to `#RRGGBB` | (none) |
|
|
32
|
+
| `#ARGB` | Expand to `#RRGGBB` | Extract alpha as decimal |
|
|
33
|
+
|
|
34
|
+
### Dimension Conversion
|
|
35
|
+
|
|
36
|
+
- `24dp` → `24` (strip `dp` suffix, use as unitless SVG user units)
|
|
37
|
+
- `@dimen/ref` → resolve from dimens.xml if available, otherwise use a default and log
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## VectorDrawable Conversion
|
|
42
|
+
|
|
43
|
+
Android VectorDrawable (`<vector>`) is structurally very similar to SVG. This is the most straightforward conversion.
|
|
44
|
+
|
|
45
|
+
### Android VectorDrawable Example
|
|
46
|
+
|
|
47
|
+
```xml
|
|
48
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
49
|
+
android:width="24dp"
|
|
50
|
+
android:height="24dp"
|
|
51
|
+
android:viewportWidth="24"
|
|
52
|
+
android:viewportHeight="24"
|
|
53
|
+
android:tint="#FF000000"
|
|
54
|
+
android:alpha="0.8">
|
|
55
|
+
<group
|
|
56
|
+
android:name="arrow_group"
|
|
57
|
+
android:rotation="90"
|
|
58
|
+
android:pivotX="12"
|
|
59
|
+
android:pivotY="12"
|
|
60
|
+
android:scaleX="1.5"
|
|
61
|
+
android:scaleY="1.5"
|
|
62
|
+
android:translateX="2"
|
|
63
|
+
android:translateY="0">
|
|
64
|
+
<path
|
|
65
|
+
android:name="arrow"
|
|
66
|
+
android:pathData="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"
|
|
67
|
+
android:fillColor="#FF4081"
|
|
68
|
+
android:fillAlpha="0.8"
|
|
69
|
+
android:strokeColor="#000000"
|
|
70
|
+
android:strokeWidth="1"
|
|
71
|
+
android:strokeAlpha="1"
|
|
72
|
+
android:strokeLineCap="round"
|
|
73
|
+
android:strokeLineJoin="round"
|
|
74
|
+
android:fillType="evenOdd"
|
|
75
|
+
android:trimPathStart="0"
|
|
76
|
+
android:trimPathEnd="1" />
|
|
77
|
+
<clip-path
|
|
78
|
+
android:name="clip"
|
|
79
|
+
android:pathData="M0,0h24v24H0z" />
|
|
80
|
+
</group>
|
|
81
|
+
</vector>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Converted SVG Output
|
|
85
|
+
|
|
86
|
+
```svg
|
|
87
|
+
<svg xmlns="http://www.w3.org/2000/svg"
|
|
88
|
+
width="24" height="24"
|
|
89
|
+
viewBox="0 0 24 24"
|
|
90
|
+
opacity="0.8">
|
|
91
|
+
<g transform="translate(2,0) scale(1.5,1.5) rotate(90,12,12)">
|
|
92
|
+
<defs>
|
|
93
|
+
<clipPath id="clip">
|
|
94
|
+
<path d="M0,0h24v24H0z"/>
|
|
95
|
+
</clipPath>
|
|
96
|
+
</defs>
|
|
97
|
+
<path d="M12,2L4.5,20.29l0.71,0.71L12,18l6.79,3 0.71,-0.71z"
|
|
98
|
+
fill="#FF4081" fill-opacity="0.8"
|
|
99
|
+
stroke="#000000" stroke-width="1"
|
|
100
|
+
stroke-linecap="round" stroke-linejoin="round"
|
|
101
|
+
fill-rule="evenodd"
|
|
102
|
+
clip-path="url(#clip)"/>
|
|
103
|
+
</g>
|
|
104
|
+
</svg>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Element Mapping: `<vector>` → `<svg>`
|
|
108
|
+
|
|
109
|
+
| Android Attribute | SVG Equivalent |
|
|
110
|
+
|---|---|
|
|
111
|
+
| `android:width` | `width` (strip `dp`) |
|
|
112
|
+
| `android:height` | `height` (strip `dp`) |
|
|
113
|
+
| `android:viewportWidth` | Part of `viewBox="0 0 W H"` |
|
|
114
|
+
| `android:viewportHeight` | Part of `viewBox="0 0 W H"` |
|
|
115
|
+
| `android:alpha` | `opacity` on root `<svg>` |
|
|
116
|
+
| `android:tint` | Apply as a filter or ignore (SVG has no direct tint; log in report) |
|
|
117
|
+
|
|
118
|
+
### Element Mapping: `<path>` → `<path>`
|
|
119
|
+
|
|
120
|
+
| Android Attribute | SVG Attribute |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `android:pathData` | `d` (path data syntax is identical) |
|
|
123
|
+
| `android:fillColor` | `fill` (convert color format) |
|
|
124
|
+
| `android:fillAlpha` | `fill-opacity` |
|
|
125
|
+
| `android:strokeColor` | `stroke` (convert color format) |
|
|
126
|
+
| `android:strokeWidth` | `stroke-width` |
|
|
127
|
+
| `android:strokeAlpha` | `stroke-opacity` |
|
|
128
|
+
| `android:strokeLineCap` | `stroke-linecap` (`butt`, `round`, `square`) |
|
|
129
|
+
| `android:strokeLineJoin` | `stroke-linejoin` (`miter`, `round`, `bevel`) |
|
|
130
|
+
| `android:strokeMiterLimit` | `stroke-miterlimit` |
|
|
131
|
+
| `android:fillType` | `fill-rule` (`nonZero` → `nonzero`, `evenOdd` → `evenodd`) |
|
|
132
|
+
| `android:trimPathStart` | No direct SVG equivalent; if != 0, use `stroke-dasharray` + `stroke-dashoffset` to approximate, or log |
|
|
133
|
+
| `android:trimPathEnd` | Same as above |
|
|
134
|
+
| `android:trimPathOffset` | Same as above |
|
|
135
|
+
|
|
136
|
+
### Element Mapping: `<group>` → `<g>`
|
|
137
|
+
|
|
138
|
+
Build the SVG `transform` attribute from the group's transformation properties. Apply in this order:
|
|
139
|
+
1. `translate(translateX, translateY)`
|
|
140
|
+
2. `scale(scaleX, scaleY)`
|
|
141
|
+
3. `rotate(rotation, pivotX, pivotY)`
|
|
142
|
+
|
|
143
|
+
Combined: `transform="translate(tX,tY) scale(sX,sY) rotate(r,pX,pY)"`
|
|
144
|
+
|
|
145
|
+
Only include transform components that differ from defaults (translate 0,0; scale 1,1; rotation 0).
|
|
146
|
+
|
|
147
|
+
### Element Mapping: `<clip-path>` → `<clipPath>` + `<path>`
|
|
148
|
+
|
|
149
|
+
```xml
|
|
150
|
+
<!-- Android -->
|
|
151
|
+
<clip-path android:pathData="M0,0h24v24H0z"/>
|
|
152
|
+
|
|
153
|
+
<!-- SVG -->
|
|
154
|
+
<defs>
|
|
155
|
+
<clipPath id="generated_clip_id">
|
|
156
|
+
<path d="M0,0h24v24H0z"/>
|
|
157
|
+
</clipPath>
|
|
158
|
+
</defs>
|
|
159
|
+
<!-- Apply via clip-path="url(#generated_clip_id)" on sibling paths -->
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Generate unique IDs for clip paths (e.g., `clip_1`, `clip_2`, or derive from `android:name`).
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## ShapeDrawable Conversion
|
|
167
|
+
|
|
168
|
+
Android `<shape>` drawables define geometric shapes with fill, stroke, gradients, corners, and sizing.
|
|
169
|
+
|
|
170
|
+
### Shape Types
|
|
171
|
+
|
|
172
|
+
#### Rectangle (default)
|
|
173
|
+
|
|
174
|
+
```xml
|
|
175
|
+
<!-- Android -->
|
|
176
|
+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
|
177
|
+
android:shape="rectangle">
|
|
178
|
+
<solid android:color="#FF4081"/>
|
|
179
|
+
<stroke android:width="2dp" android:color="#000000"
|
|
180
|
+
android:dashWidth="4dp" android:dashGap="2dp"/>
|
|
181
|
+
<corners android:radius="8dp"/>
|
|
182
|
+
<size android:width="100dp" android:height="50dp"/>
|
|
183
|
+
<padding android:left="10dp" android:top="5dp"
|
|
184
|
+
android:right="10dp" android:bottom="5dp"/>
|
|
185
|
+
</shape>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```svg
|
|
189
|
+
<!-- SVG -->
|
|
190
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="50" viewBox="0 0 100 50">
|
|
191
|
+
<rect x="0" y="0" width="100" height="50" rx="8" ry="8"
|
|
192
|
+
fill="#FF4081"
|
|
193
|
+
stroke="#000000" stroke-width="2"
|
|
194
|
+
stroke-dasharray="4,2"/>
|
|
195
|
+
</svg>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Corner radius variants:**
|
|
199
|
+
- `android:radius` → `rx` and `ry` (same value)
|
|
200
|
+
- `android:topLeftRadius`, `android:topRightRadius`, `android:bottomRightRadius`, `android:bottomLeftRadius` → When different per corner, use a `<path>` with rounded corner arcs instead of `<rect>`
|
|
201
|
+
|
|
202
|
+
**Per-corner radius SVG path formula:**
|
|
203
|
+
```
|
|
204
|
+
M {tlr},0
|
|
205
|
+
H {w-trr}
|
|
206
|
+
Q {w},0 {w},{trr}
|
|
207
|
+
V {h-brr}
|
|
208
|
+
Q {w},{h} {w-brr},{h}
|
|
209
|
+
H {blr}
|
|
210
|
+
Q 0,{h} 0,{h-blr}
|
|
211
|
+
V {tlr}
|
|
212
|
+
Q 0,0 {tlr},0
|
|
213
|
+
Z
|
|
214
|
+
```
|
|
215
|
+
Where `tlr`=topLeftRadius, `trr`=topRightRadius, `brr`=bottomRightRadius, `blr`=bottomLeftRadius, `w`=width, `h`=height.
|
|
216
|
+
|
|
217
|
+
#### Oval
|
|
218
|
+
|
|
219
|
+
```xml
|
|
220
|
+
<!-- Android -->
|
|
221
|
+
<shape android:shape="oval">
|
|
222
|
+
<solid android:color="#2196F3"/>
|
|
223
|
+
<size android:width="60dp" android:height="60dp"/>
|
|
224
|
+
</shape>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```svg
|
|
228
|
+
<!-- SVG -->
|
|
229
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="60" height="60" viewBox="0 0 60 60">
|
|
230
|
+
<ellipse cx="30" cy="30" rx="30" ry="30" fill="#2196F3"/>
|
|
231
|
+
</svg>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- If width == height, use `<circle>` instead: `<circle cx="30" cy="30" r="30"/>`
|
|
235
|
+
|
|
236
|
+
#### Line
|
|
237
|
+
|
|
238
|
+
```xml
|
|
239
|
+
<!-- Android -->
|
|
240
|
+
<shape android:shape="line">
|
|
241
|
+
<stroke android:width="2dp" android:color="#000000"/>
|
|
242
|
+
<size android:height="2dp" android:width="100dp"/>
|
|
243
|
+
</shape>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
```svg
|
|
247
|
+
<!-- SVG -->
|
|
248
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="2" viewBox="0 0 100 2">
|
|
249
|
+
<line x1="0" y1="1" x2="100" y2="1" stroke="#000000" stroke-width="2"/>
|
|
250
|
+
</svg>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Ring
|
|
254
|
+
|
|
255
|
+
```xml
|
|
256
|
+
<!-- Android -->
|
|
257
|
+
<shape android:shape="ring"
|
|
258
|
+
android:innerRadius="20dp"
|
|
259
|
+
android:thickness="5dp">
|
|
260
|
+
<solid android:color="#4CAF50"/>
|
|
261
|
+
<size android:width="50dp" android:height="50dp"/>
|
|
262
|
+
</shape>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
```svg
|
|
266
|
+
<!-- SVG -->
|
|
267
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 50 50">
|
|
268
|
+
<circle cx="25" cy="25" r="22.5"
|
|
269
|
+
fill="none" stroke="#4CAF50" stroke-width="5"/>
|
|
270
|
+
</svg>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- Ring center radius = `innerRadius + thickness/2`
|
|
274
|
+
- SVG stroke-width = `thickness`
|
|
275
|
+
- If `android:innerRadiusRatio` is used instead of `android:innerRadius`: `innerRadius = width / (2 * innerRadiusRatio)`
|
|
276
|
+
- If `android:thicknessRatio` is used: `thickness = width / (2 * thicknessRatio)`
|
|
277
|
+
|
|
278
|
+
### Shape Size Handling
|
|
279
|
+
|
|
280
|
+
If `<size>` is not specified, use a sensible default (e.g., `24x24`) and note in the report that the original had no explicit size. The SVG `viewBox` should match the dimensions.
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Gradient Conversion
|
|
285
|
+
|
|
286
|
+
Android shapes and vector drawables can use gradients for fill or stroke colors.
|
|
287
|
+
|
|
288
|
+
### Linear Gradient
|
|
289
|
+
|
|
290
|
+
```xml
|
|
291
|
+
<!-- Android (in shape) -->
|
|
292
|
+
<gradient
|
|
293
|
+
android:type="linear"
|
|
294
|
+
android:startColor="#FF0000"
|
|
295
|
+
android:centerColor="#00FF00"
|
|
296
|
+
android:endColor="#0000FF"
|
|
297
|
+
android:angle="45"/>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
```svg
|
|
301
|
+
<!-- SVG -->
|
|
302
|
+
<defs>
|
|
303
|
+
<linearGradient id="grad_1" x1="0%" y1="100%" x2="100%" y2="0%">
|
|
304
|
+
<stop offset="0%" stop-color="#FF0000"/>
|
|
305
|
+
<stop offset="50%" stop-color="#00FF00"/>
|
|
306
|
+
<stop offset="100%" stop-color="#0000FF"/>
|
|
307
|
+
</linearGradient>
|
|
308
|
+
</defs>
|
|
309
|
+
<rect ... fill="url(#grad_1)"/>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Angle to coordinates mapping:**
|
|
313
|
+
|
|
314
|
+
| Android `angle` | SVG `x1,y1 → x2,y2` |
|
|
315
|
+
|---|---|
|
|
316
|
+
| 0 (left→right) | `0%,0% → 100%,0%` |
|
|
317
|
+
| 45 (bottom-left→top-right) | `0%,100% → 100%,0%` |
|
|
318
|
+
| 90 (bottom→top) | `0%,100% → 0%,0%` |
|
|
319
|
+
| 135 (bottom-right→top-left) | `100%,100% → 0%,0%` |
|
|
320
|
+
| 180 (right→left) | `100%,0% → 0%,0%` |
|
|
321
|
+
| 225 (top-right→bottom-left) | `100%,0% → 0%,100%` |
|
|
322
|
+
| 270 (top→bottom) | `0%,0% → 0%,100%` |
|
|
323
|
+
| 315 (top-left→bottom-right) | `0%,0% → 100%,100%` |
|
|
324
|
+
|
|
325
|
+
Android `angle` must be a multiple of 45. For other values, calculate:
|
|
326
|
+
- `x1 = 50 + 50*cos(angle+180)`, `y1 = 50 - 50*sin(angle+180)`
|
|
327
|
+
- `x2 = 50 + 50*cos(angle)`, `y2 = 50 - 50*sin(angle)`
|
|
328
|
+
|
|
329
|
+
### Radial Gradient
|
|
330
|
+
|
|
331
|
+
```xml
|
|
332
|
+
<!-- Android -->
|
|
333
|
+
<gradient
|
|
334
|
+
android:type="radial"
|
|
335
|
+
android:centerX="0.5"
|
|
336
|
+
android:centerY="0.5"
|
|
337
|
+
android:gradientRadius="50%p"
|
|
338
|
+
android:startColor="#FFFFFF"
|
|
339
|
+
android:endColor="#000000"/>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
```svg
|
|
343
|
+
<!-- SVG -->
|
|
344
|
+
<defs>
|
|
345
|
+
<radialGradient id="grad_2" cx="50%" cy="50%" r="50%">
|
|
346
|
+
<stop offset="0%" stop-color="#FFFFFF"/>
|
|
347
|
+
<stop offset="100%" stop-color="#000000"/>
|
|
348
|
+
</radialGradient>
|
|
349
|
+
</defs>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
- `centerX`/`centerY` are fractions (0.0–1.0) → convert to percentages
|
|
353
|
+
- `gradientRadius` with `%p` suffix → percentage of parent dimension
|
|
354
|
+
|
|
355
|
+
### Sweep Gradient
|
|
356
|
+
|
|
357
|
+
```xml
|
|
358
|
+
<!-- Android -->
|
|
359
|
+
<gradient
|
|
360
|
+
android:type="sweep"
|
|
361
|
+
android:centerX="0.5"
|
|
362
|
+
android:centerY="0.5"
|
|
363
|
+
android:startColor="#FF0000"
|
|
364
|
+
android:endColor="#0000FF"/>
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
SVG doesn't have a native sweep/conic gradient in SVG 1.1. Options:
|
|
368
|
+
1. Use CSS `conic-gradient` if targeting modern renderers (but not SVG 1.1 compliant)
|
|
369
|
+
2. Approximate with a series of `<path>` segments in a radial pattern
|
|
370
|
+
3. Log as partially converted and note that sweep gradient was approximated
|
|
371
|
+
|
|
372
|
+
Recommended: convert to a `<linearGradient>` as an approximation and log in the report that the original was a sweep gradient.
|
|
373
|
+
|
|
374
|
+
### Gradient in VectorDrawable Paths
|
|
375
|
+
|
|
376
|
+
Android API 24+ supports `<aapt:attr>` inline gradient in vector drawables:
|
|
377
|
+
|
|
378
|
+
```xml
|
|
379
|
+
<path android:pathData="...">
|
|
380
|
+
<aapt:attr name="android:fillColor">
|
|
381
|
+
<gradient android:type="linear" .../>
|
|
382
|
+
</aapt:attr>
|
|
383
|
+
</path>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Convert the gradient to an SVG `<linearGradient>` in `<defs>` and reference via `fill="url(#grad_id)"`.
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## LayerList Conversion
|
|
391
|
+
|
|
392
|
+
`<layer-list>` stacks multiple drawable items. Each `<item>` can contain a `<shape>`, a `<bitmap>`, or reference another drawable.
|
|
393
|
+
|
|
394
|
+
```xml
|
|
395
|
+
<!-- Android -->
|
|
396
|
+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
397
|
+
<item>
|
|
398
|
+
<shape android:shape="rectangle">
|
|
399
|
+
<solid android:color="#CCCCCC"/>
|
|
400
|
+
<corners android:radius="4dp"/>
|
|
401
|
+
</shape>
|
|
402
|
+
</item>
|
|
403
|
+
<item android:left="2dp" android:top="2dp"
|
|
404
|
+
android:right="2dp" android:bottom="2dp">
|
|
405
|
+
<shape android:shape="rectangle">
|
|
406
|
+
<solid android:color="#FFFFFF"/>
|
|
407
|
+
<corners android:radius="2dp"/>
|
|
408
|
+
</shape>
|
|
409
|
+
</item>
|
|
410
|
+
</layer-list>
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
```svg
|
|
414
|
+
<!-- SVG -->
|
|
415
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
|
|
416
|
+
<!-- Layer 0: background -->
|
|
417
|
+
<rect x="0" y="0" width="100" height="100" rx="4" ry="4" fill="#CCCCCC"/>
|
|
418
|
+
<!-- Layer 1: foreground with insets -->
|
|
419
|
+
<rect x="2" y="2" width="96" height="96" rx="2" ry="2" fill="#FFFFFF"/>
|
|
420
|
+
</svg>
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Rules
|
|
424
|
+
|
|
425
|
+
- Items are rendered in document order (first item = bottom layer)
|
|
426
|
+
- `android:left/top/right/bottom` on `<item>` → inset the element's position and reduce its size accordingly
|
|
427
|
+
- `android:width/height` on `<item>` → explicit size override
|
|
428
|
+
- `android:gravity` on `<item>` → position within the layer bounds
|
|
429
|
+
- If an `<item>` references another drawable via `android:drawable="@drawable/other"`, try to resolve and inline it; if not resolvable, log in report
|
|
430
|
+
- For layer-lists without explicit `<size>`, use a default canvas of `100x100` and note in report
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Selector / StateList Conversion
|
|
435
|
+
|
|
436
|
+
`<selector>` defines different drawables for different UI states (pressed, focused, disabled, etc.). SVG is static, so we extract the most useful representation.
|
|
437
|
+
|
|
438
|
+
```xml
|
|
439
|
+
<!-- Android -->
|
|
440
|
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
441
|
+
<item android:state_pressed="true">
|
|
442
|
+
<shape android:shape="rectangle">
|
|
443
|
+
<solid android:color="#E0E0E0"/>
|
|
444
|
+
<corners android:radius="4dp"/>
|
|
445
|
+
</shape>
|
|
446
|
+
</item>
|
|
447
|
+
<item android:state_focused="true">
|
|
448
|
+
<shape android:shape="rectangle">
|
|
449
|
+
<solid android:color="#F0F0F0"/>
|
|
450
|
+
<corners android:radius="4dp"/>
|
|
451
|
+
</shape>
|
|
452
|
+
</item>
|
|
453
|
+
<item>
|
|
454
|
+
<shape android:shape="rectangle">
|
|
455
|
+
<solid android:color="#FFFFFF"/>
|
|
456
|
+
<corners android:radius="4dp"/>
|
|
457
|
+
</shape>
|
|
458
|
+
</item>
|
|
459
|
+
</selector>
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Conversion Strategy
|
|
463
|
+
|
|
464
|
+
1. Find the **default item** — the `<item>` with no `android:state_*` attributes (or the last item, which serves as the default in Android)
|
|
465
|
+
2. Convert that single item to SVG using the appropriate rules (shape → SVG, vector → SVG, etc.)
|
|
466
|
+
3. In the conversion report, list all the state variants that were dropped:
|
|
467
|
+
- `state_pressed`, `state_focused`, `state_selected`, `state_checked`, `state_enabled`, `state_activated`, `state_hovered`
|
|
468
|
+
4. Note that HarmonyOS state handling should be done in ArkUI component code, not in resource files
|
|
469
|
+
|
|
470
|
+
---
|
|
471
|
+
|
|
472
|
+
## Common Attribute Mapping
|
|
473
|
+
|
|
474
|
+
### Android `android:` namespace to SVG
|
|
475
|
+
|
|
476
|
+
| Android Attribute | SVG Equivalent | Notes |
|
|
477
|
+
|---|---|---|
|
|
478
|
+
| `android:fillColor` | `fill` | Convert color format |
|
|
479
|
+
| `android:strokeColor` | `stroke` | Convert color format |
|
|
480
|
+
| `android:strokeWidth` | `stroke-width` | Strip `dp` |
|
|
481
|
+
| `android:alpha` | `opacity` | 0.0-1.0 range |
|
|
482
|
+
| `android:fillAlpha` | `fill-opacity` | 0.0-1.0 range |
|
|
483
|
+
| `android:strokeAlpha` | `stroke-opacity` | 0.0-1.0 range |
|
|
484
|
+
| `android:rotation` | `transform="rotate(deg)"` | In `<group>` |
|
|
485
|
+
| `android:scaleX/Y` | `transform="scale(x,y)"` | In `<group>` |
|
|
486
|
+
| `android:translateX/Y` | `transform="translate(x,y)"` | In `<group>` |
|
|
487
|
+
|
|
488
|
+
### Default Values
|
|
489
|
+
|
|
490
|
+
If an Android drawable doesn't specify certain attributes, use these defaults:
|
|
491
|
+
- `fillColor`: `#000000` (black) for paths in `<vector>`, transparent for shapes without `<solid>`
|
|
492
|
+
- `strokeWidth`: `0` (no stroke)
|
|
493
|
+
- `alpha`: `1.0`
|
|
494
|
+
- `fillType`: `nonZero`
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Unconvertible Types
|
|
499
|
+
|
|
500
|
+
These XML drawable types cannot be meaningfully converted to SVG and should be logged as unmappable in the conversion report:
|
|
501
|
+
|
|
502
|
+
| Type | Root Element | Reason |
|
|
503
|
+
|---|---|---|
|
|
504
|
+
| Ripple | `<ripple>` | Runtime touch feedback animation |
|
|
505
|
+
| Animated Vector | `<animated-vector>` | Requires animation timing/interpolation |
|
|
506
|
+
| Animated Selector | `<animated-selector>` | State transitions with animations |
|
|
507
|
+
| Transition Drawable | `<transition>` | Cross-fade between two drawables |
|
|
508
|
+
| Inset Drawable | `<inset>` | Simple wrapper; if inner drawable is convertible, convert that instead |
|
|
509
|
+
| Scale Drawable | `<scale>` | Runtime scaling behavior |
|
|
510
|
+
| Rotate Drawable | `<rotate>` | Runtime rotation; could be converted as static rotation if angle is fixed |
|
|
511
|
+
| Level List | `<level-list>` | Runtime level-based selection |
|
|
512
|
+
|
|
513
|
+
For `<inset>` and `<rotate>` with fixed values, attempt conversion by applying the inset/rotation statically and convert the inner drawable. Log in the report either way.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: appgraph-rule-audit
|
|
3
|
+
description: Review fresh AppGraph and SPEC results after Android analysis, identify evidence-backed opportunities to improve page, ownership, and navigation rules, and write a local rule-opportunity report. Use after `droid2hmos analyze android` and before initializing the migration report; do not use it to edit analyzer output or implement rules.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Audit AppGraph rule opportunities
|
|
7
|
+
|
|
8
|
+
Review the latest deterministic Android analysis and produce maintainable rule-improvement candidates. This is a read-only diagnostic step. Do not edit AppGraph, SPEC facts, Android source, rule packs, or analyzers.
|
|
9
|
+
|
|
10
|
+
All `.migration/...` paths below live in the sibling migration workspace `<android-project>-migration/.migration/`. Fall back to the legacy `<android-project>/.migration/` only when the sibling workspace does not exist.
|
|
11
|
+
|
|
12
|
+
## Preconditions
|
|
13
|
+
|
|
14
|
+
1. Run `droid2hmos analyze android <android-project>` successfully before this Skill.
|
|
15
|
+
2. Locate `.migration/spec/index.json`, `.migration/spec/facts/`, and `.migration/appgraph/app-graph.json`.
|
|
16
|
+
3. Check that the SPEC `generation.sourceRevision`, when present, matches the Android Git HEAD. Treat a mismatch or missing required artifact as stale analysis: write no recommendations and ask for a new analysis run.
|
|
17
|
+
4. Keep all source and generated data local. Network access is unnecessary.
|
|
18
|
+
|
|
19
|
+
## Review workflow
|
|
20
|
+
|
|
21
|
+
1. Read only the indexes and summaries first. Record page counts by type, navigation edges, ownership/host edges, unresolved counts, reachability gaps, coverage warnings, rule-pack versions, and rule contribution/drop/conflict counts.
|
|
22
|
+
2. Group gaps by stable evidence shape rather than business page name:
|
|
23
|
+
- unresolved reason;
|
|
24
|
+
- framework/package/base type/annotation;
|
|
25
|
+
- receiver and method call shape;
|
|
26
|
+
- route declaration or factory pattern;
|
|
27
|
+
- missing host, target, caller, projection, or uniqueness evidence.
|
|
28
|
+
3. Prioritize repeated or high-impact clusters. Inspect at most five representative samples per cluster initially. Read additional source only when it changes the diagnosis.
|
|
29
|
+
4. Classify each candidate using [references/audit-contract.md](references/audit-contract.md). Determine whether evidence is missing in Analyzer, rejected by Judge, lost during Lift, omitted from SPEC, missing a host, or indicates a false positive.
|
|
30
|
+
5. Establish a positive example and a similar negative or ambiguous example before proposing a generic or framework rule. If the target is dynamic, reflective, or non-unique, record it as an accepted limitation instead of proposing an unsafe rule.
|
|
31
|
+
6. Compare with a previous report for the same source project when available. Treat newly added pages/edges, disappearing pages/edges, changed direction, and changed ownership as accuracy signals; raw count growth is not automatically an improvement.
|
|
32
|
+
7. Rank opportunities by impact, recurrence, evidence strength, generality, false-positive risk, and implementation cost. Keep at most 20 active opportunities.
|
|
33
|
+
8. Write the two outputs defined below. Follow [references/recommendation-schema.md](references/recommendation-schema.md) exactly.
|
|
34
|
+
|
|
35
|
+
## Outputs
|
|
36
|
+
|
|
37
|
+
Create or replace:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
.migration/rule-audit/rule-opportunities.json
|
|
41
|
+
.migration/rule-audit/rule-opportunities.md
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The JSON must conform to `schemas/rule-opportunities.schema.json` in this Skill. The Markdown is the human review view of the same findings; it must not add claims absent from JSON.
|
|
45
|
+
|
|
46
|
+
Each opportunity must include evidence refs with Android source file and line, the affected AppGraph/SPEC entities, current and expected behavior, proposed change layer, scope, confidence, generality, risk, and a concrete test plan. Separate observations from inferred root causes and label uncertain causes as hypotheses.
|
|
47
|
+
|
|
48
|
+
When no evidence-backed rule improvement exists, write valid outputs with an empty `opportunities` array and explain the reviewed limitations. Do not invent work to avoid an empty report.
|
|
49
|
+
|
|
50
|
+
## Boundaries
|
|
51
|
+
|
|
52
|
+
- Do not modify immutable `.migration/spec/facts/` or `app-graph.json`.
|
|
53
|
+
- Do not write a project screen name into a generic rule proposal.
|
|
54
|
+
- Do not call a suffix match, route-string similarity, or nearest-name guess sufficient evidence.
|
|
55
|
+
- Do not turn host relationships into navigation edges.
|
|
56
|
+
- Do not mark multiple targets as resolved.
|
|
57
|
+
- Do not implement recommendations. A maintainer may later pass an accepted opportunity to the separate `appgraph-navigation-rules` Skill.
|
|
58
|
+
- Audit failure is advisory and must not block HarmonyOS migration. Record the limitation and allow the orchestrator to continue to `report init`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Rule audit contract
|
|
2
|
+
|
|
3
|
+
Use this reference to classify a finding and decide whether it is actionable.
|
|
4
|
+
|
|
5
|
+
## Layers
|
|
6
|
+
|
|
7
|
+
### analyzer-gap
|
|
8
|
+
|
|
9
|
+
The Android source contains stable syntax or type evidence, but `ruleEngine.contributions` has no corresponding facts. A proposal must name the API identity and facts an Analyzer should emit. JSON5 Judge conditions cannot repair this gap.
|
|
10
|
+
|
|
11
|
+
### judge-gap
|
|
12
|
+
|
|
13
|
+
Required facts exist, but no rule confirms them or the contribution is dropped. Cite the existing fact paths, matching/drop result, and the smallest condition change. Preserve ambiguity.
|
|
14
|
+
|
|
15
|
+
### lift-gap
|
|
16
|
+
|
|
17
|
+
The contribution is confirmed, but the expected Screen/host/navigation node or edge is absent from AppGraph. Cite the matched rule and expected graph projection.
|
|
18
|
+
|
|
19
|
+
### spec-gap
|
|
20
|
+
|
|
21
|
+
The AppGraph node or edge exists with valid provenance, but SPEC facts omit or misclassify it. Cite both the AppGraph entity and missing SPEC representation.
|
|
22
|
+
|
|
23
|
+
### host-gap
|
|
24
|
+
|
|
25
|
+
A page is recognized but lacks a deterministic owner or upstream host. Propose a rule only when the host chain follows a stable framework API, factory, entry point, or unique call relationship. Never infer a host from proximity or a single Activity fallback.
|
|
26
|
+
|
|
27
|
+
### false-positive
|
|
28
|
+
|
|
29
|
+
A recognized page, host, or transition is a component, preview, test fixture, factory, helper, or unrelated call. Cite the matched rule and a structural discriminator that excludes the bad result without naming the product entity.
|
|
30
|
+
|
|
31
|
+
### performance
|
|
32
|
+
|
|
33
|
+
A rule or analyzer has observed pathological cost. Cite timings, affected stage, input shape, and a bounded alternative. Do not infer performance problems from project size alone.
|
|
34
|
+
|
|
35
|
+
## Scope
|
|
36
|
+
|
|
37
|
+
- `builtin-generic`: supported Android or Jetpack API with evidence across unrelated projects or authoritative framework structure.
|
|
38
|
+
- `framework-pack`: stable third-party or private framework identity such as package, base type, annotation, receiver type, or route registry.
|
|
39
|
+
- `project-pack`: valid behavior tied to one project's local abstraction without enough evidence for broader scope.
|
|
40
|
+
|
|
41
|
+
## Evidence minimum
|
|
42
|
+
|
|
43
|
+
Every opportunity needs one positive source example and its current generated result. A `builtin-generic` or `framework-pack` opportunity also needs a negative, boundary, or ambiguous example demonstrating how false matches will be prevented. Evidence includes project-relative file, line when available, symbol/call shape, related entity refs, and related rule IDs.
|
|
44
|
+
|
|
45
|
+
## Accepted limitations
|
|
46
|
+
|
|
47
|
+
Use an accepted limitation for reflection, runtime-generated destinations, remote configuration, unresolved dependency types, multiple valid targets, or missing source. Explain what evidence would be required to resolve it. Do not propose a low-confidence guessing rule.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Rule opportunity output
|
|
2
|
+
|
|
3
|
+
Write JSON matching `schemas/rule-opportunities.schema.json` in this Skill and a concise Chinese Markdown rendering.
|
|
4
|
+
|
|
5
|
+
## Top-level fields
|
|
6
|
+
|
|
7
|
+
- `schemaVersion`: `1.0.0`.
|
|
8
|
+
- `skillVersion`: `1.0.0`.
|
|
9
|
+
- `source`: Android project identity, source revision, `factsSha256`, and observed AppGraph/rule-pack versions.
|
|
10
|
+
- `summary`: reviewed counts and a short conclusion.
|
|
11
|
+
- `opportunities`: at most 20 ranked candidates.
|
|
12
|
+
- `acceptedLimitations`: evidence-backed gaps that should remain unresolved.
|
|
13
|
+
- `auditWarnings`: stale, incomplete, or unavailable inputs that limit confidence.
|
|
14
|
+
|
|
15
|
+
## Opportunity fields
|
|
16
|
+
|
|
17
|
+
- `id`: stable lowercase ID derived from category, framework identity, and evidence shape; never use list position.
|
|
18
|
+
- `priority`: `P0`, `P1`, or `P2`. Use P0 only for systemic false results or a blocker affecting a large part of the page/navigation graph.
|
|
19
|
+
- `category`: one audit layer from the contract.
|
|
20
|
+
- `scope`: `builtin-generic`, `framework-pack`, or `project-pack`.
|
|
21
|
+
- `title`: concise problem statement.
|
|
22
|
+
- `frameworkIdentity`: stable package/type/annotation/receiver/call identity.
|
|
23
|
+
- `observation`: directly observed current result.
|
|
24
|
+
- `rootCause`: diagnosis plus `certainty` of `confirmed` or `hypothesis`.
|
|
25
|
+
- `expectedBehavior`: required page, host, edge, projection, exclusion, or performance behavior.
|
|
26
|
+
- `affectedEntities`: page/edge/contribution refs.
|
|
27
|
+
- `evidence`: source locations, generated refs, rule IDs, and the evidence role (`positive`, `negative`, `boundary`, `ambiguous`).
|
|
28
|
+
- `proposal`: target layer, proposed rule shape, and files or rule IDs likely to change; no executable patch.
|
|
29
|
+
- `confidence`, `generality`, `risk`: `high`, `medium`, or `low`, each with a reason.
|
|
30
|
+
- `testPlan`: positive, negative, boundary, ambiguity, Lift/SPEC, and project baseline checks as applicable.
|
|
31
|
+
- `expectedMetricDelta`: directional expectation for pages, navigation, hosts, unresolved, and false positives; use `unknown` when unsupported.
|
|
32
|
+
- `status`: always `proposed` in this Skill.
|
|
33
|
+
|
|
34
|
+
## Markdown order
|
|
35
|
+
|
|
36
|
+
1. Input freshness and coverage summary.
|
|
37
|
+
2. Ranked opportunities with evidence and tests.
|
|
38
|
+
3. Accepted limitations.
|
|
39
|
+
4. Audit warnings.
|
|
40
|
+
|
|
41
|
+
Do not reproduce the full page inventory or migration report.
|