android2harmony 0.1.6 → 0.1.8
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/agents/api-analyzer.md +113 -0
- package/agents/app-action.md +122 -0
- package/agents/code-reviewer.md +191 -295
- package/agents/coder.md +377 -0
- package/agents/self-tester.md +8 -8
- package/agents/spec-designer.md +56 -0
- package/package.json +1 -1
- package/skills/a2h-app-req-gen/SKILL.md +174 -0
- package/skills/a2h-app-req-gen/references/spec-handoff.md +99 -0
- package/skills/a2h-app-req-gen/references/subagent-render.md +115 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/build.gradle.kts +41 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradle.properties +3 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew +251 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/gradlew.bat +94 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/settings.gradle.kts +17 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawAnalysisDetector.java +1911 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawIssueRegistry.java +37 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawJson.java +146 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawLocation.java +96 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/java/dev/reqgen/analyzer/jvm/RawRecordWriter.java +227 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/analyzer-jvm/src/main/resources/dev/reqgen/analyzer/jvm/raw-record-v1.schema.json +93 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/assets/dashboard.html +151 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-lint.init.gradle +60 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/gradle/reqgen-model.init.gradle +204 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/package.json +14 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/backend.js +323 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/cli.js +811 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/gap-audit-link.js +62 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/index.js +66 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/merge.js +605 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/name-trace.js +153 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/orchestrate.js +1324 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/output-parse.js +50 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/overview.js +10 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/pool.js +23 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/post-merge-fix.js +146 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt-loader.js +51 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompt.js +967 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/prompts.md +301 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/serve.js +78 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/short-id.js +46 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/snapshot.js +267 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/agent-render/validate.js +122 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/callback-call-sites.js +62 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/build.js +1291 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/gap-audit/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/area-ownership.js +392 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/build.js +1995 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/callback-flow.js +277 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/component-variants.js +725 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/graph/index.js +15 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/index.js +12 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/policy.js +469 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/names/traceability.js +468 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/build.js +1066 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/req-model/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android-interaction-registry.js +174 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/android.js +1062 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/area-titles.js +678 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/builder.js +350 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-api-registry.js +395 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose-call-sites.js +2008 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/compose.js +9059 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/dynamic-repeated-call-sites.js +529 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/interaction-effects.js +1149 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/jvm.js +6272 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/normalize.js +33 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/pending-intents.js +565 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/resource-id.js +42 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/route-state-variants.js +781 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/state.js +234 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/analysis/source-facts/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/extract.js +40 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/index.js +6 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/manifest.js +513 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/resources.js +398 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/views.js +447 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/android/xml-parser.js +175 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/cli.js +398 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/area-graph.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/artifact.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/common.js +16 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/diagnostic.js +10 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/gap-audit.js +14 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/index.js +11 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/name-traceability.js +26 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/raw-fact.js +40 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/req-model.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/run.js +19 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/semantic-completeness.js +11 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/contracts/source-facts.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/core/output-guard.js +41 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/core/stable-id.js +92 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dashboard-cli.js +169 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/diagnostics.js +104 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/dump.js +1028 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump/index.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/dump-cli.js +251 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/artifacts.js +49 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/index.js +18 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/quality-report.js +125 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/render.js +353 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/generation/types.js +2 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/graph/paths.js +474 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/index.js +15 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/protocol.js +408 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/jvm/runner.js +452 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/index.js +8 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/output.js +165 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/pipeline/run-stage1.js +891 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/discovery.js +299 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-model.js +358 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/gradle-runner.js +165 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/resource-profile.js +60 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/project/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff-cli.js +151 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/spec-handoff.js +1004 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-cli.js +109 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-input.js +419 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/stage2-name-trace-input.js +95 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/area-graph.js +444 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/index.js +7 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-model.js +580 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/req-text.js +195 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/semantic-area-graph.js +1431 -0
- package/skills/a2h-app-req-gen/scripts/reqgen/src/validation/types.js +1 -0
- package/skills/a2h-app-req-gen/scripts/reqgen-audit.mjs +298 -0
- package/skills/a2h-app-req-gen/scripts/reqgen-selfcheck.mjs +555 -0
- package/skills/a2h-code-review/SKILL.md +379 -0
- package/skills/{hmos-integration-test → a2h-integration-test}/README.md +6 -6
- package/skills/{hmos-integration-test → a2h-integration-test}/SKILL.md +6 -6
- package/skills/{hmos-integration-test → a2h-integration-test}/scripts/report-tool.mjs +1 -1
- package/skills/a2h-spec-design/SKILL.md +330 -0
- package/skills/a2h-spec-design/scripts/inspect_spec_design_inputs.mjs +207 -0
- package/skills/a2h-spec-design/scripts/move_spec_technical_reference.mjs +198 -0
- package/skills/a2h-spec-design/scripts/validate_spec_design_output.mjs +256 -0
- package/skills/a2h-spec-generate/SKILL.md +696 -0
- package/skills/a2h-spec-generate/references/trace-template.md +115 -0
- package/skills/a2h-spec-generate/scripts/parse_requirements.mjs +537 -0
- package/skills/a2h-spec-generate/template/REQ.txt +22 -0
- package/skills/a2h-spec-generate/template/REQ.xlsx +0 -0
- package/skills/a2h-spec-implement/SKILL.md +548 -0
- package/skills/a2h-spec-implement/references/harmony-pitfalls.md +186 -0
- package/skills/a2h-spec-implement/references/l1-unit-test.md +185 -0
- package/skills/a2h-spec-implement/references/l3-e2e-uitest.md +167 -0
- package/skills/a2h-spec-implement/references/test-core.md +132 -0
- package/skills/a2h-spec-implement/rules/arkts/arkts-standards.md +184 -0
- package/skills/a2h-spec-implement/rules/arkts/conventions/coding-style.md +96 -0
- package/skills/a2h-spec-implement/rules/arkts/conventions/security.md +126 -0
- package/skills/a2h-spec-implement/rules/arkts/language/arkts-rules.md +650 -0
- package/skills/a2h-spec-implement/rules/arkts/language/arkui-structure-rules.md +173 -0
- package/skills/a2h-spec-implement/rules/arkts/language/ts-to-arkts-rewrites.md +58 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +648 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +2089 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +1033 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +1183 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +576 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +297 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/@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 +395 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/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 +903 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/Environment/357/274/232/350/256/276/345/244/207/347/216/257/345/242/203/346/237/245/350/257/242.md +106 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/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 +1178 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/MVVM/346/250/241/345/274/217/357/274/210V1/357/274/211.md +911 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1/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 +355 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v1//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 +11 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/!!/350/257/255/346/263/225/357/274/232/345/217/214/345/220/221/347/273/221/345/256/232.md +216 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +442 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +169 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +763 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +2088 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +1258 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Once/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 +175 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/@Param/357/274/232/347/273/204/344/273/266/345/244/226/351/203/250/350/276/223/345/205/245.md +850 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +862 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +110 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +301 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/MVVM/346/250/241/345/274/217/357/274/210V2/357/274/211.md +1411 -0
- package/skills/a2h-spec-implement/rules/arkts/official/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 +1392 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/getTarget/346/216/245/345/217/243/357/274/232/350/216/267/345/217/226/347/212/266/346/200/201/347/256/241/347/220/206/346/241/206/346/236/266/344/273/243/347/220/206/345/211/215/347/232/204/345/216/237/345/247/213/345/257/271/350/261/241.md +288 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2/makeObserved/346/216/245/345/217/243/357/274/232/345/260/206/351/235/236/350/247/202/345/257/237/346/225/260/346/215/256/345/217/230/344/270/272/345/217/257/350/247/202/345/257/237/346/225/260/346/215/256.md +768 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206V1/345/222/214V2/346/267/267/347/224/250/346/214/207/345/257/274/357/274/210API version 19/345/217/212/344/271/213/345/220/216/357/274/211.md" +829 -0
- package/skills/a2h-spec-implement/rules/arkts/official/mvvm-v2//347/212/266/346/200/201/347/256/241/347/220/206/346/246/202/350/277/260.md +184 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/component-cookbook.md +431 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/state-management.md +152 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/ui-quality.md +67 -0
- package/skills/a2h-spec-implement/rules/arkts/ui/ui-runtime-diagnosis.md +32 -0
- package/skills/a2h-spec-implement/rules/pipeline/build/package-set-collection.md +43 -0
- package/skills/a2h-spec-implement/scripts/_lib/common.mjs +97 -0
- package/skills/a2h-spec-implement/scripts/_lib/config.mjs +70 -0
- package/skills/a2h-spec-implement/scripts/_lib/lessons_cli.mjs +48 -0
- package/skills/a2h-spec-implement/scripts/build_lessons.mjs +24 -0
- package/skills/a2h-spec-implement/scripts/code_pattern_lessons.mjs +26 -0
- package/skills/a2h-spec-implement/scripts/device_ui.mjs +433 -0
- package/skills/a2h-spec-implement/scripts/device_ui_core.mjs +895 -0
- package/skills/a2h-spec-implement/scripts/ledger/aggregate_run.mjs +207 -0
- package/skills/a2h-spec-implement/scripts/manifest/emit_summary_line.mjs +37 -0
- package/skills/a2h-spec-implement/scripts/manifest/finalize_run.mjs +115 -0
- package/skills/a2h-spec-implement/scripts/manifest/render_final_summary.mjs +78 -0
- package/skills/a2h-spec-implement/scripts/manifest/root_mirror.mjs +137 -0
- package/skills/a2h-spec-implement/scripts/package.json +12 -0
- package/skills/a2h-spec-implement/scripts/state_propagation_check.mjs +676 -0
- package/skills/a2h-spec-implement/scripts/test/l2_run.mjs +244 -0
- package/skills/a2h-spec-implement/scripts/test/tp_coverage.mjs +231 -0
- package/skills/a2h-spec-implement/scripts/ui_memory.mjs +161 -0
- package/skills/a2h-spec-implement/scripts/utils/ensure_app.mjs +134 -0
- package/skills/a2h-ui-transfer/SKILL.md +20 -9
- package/skills/a2h-ui-transfer/scripts/arkts_static_check.js +80 -0
- package/skills/hmos-convert-pipeline/SKILL.md +7 -5
- package/skills/hmos-incremental-ui-align/README.md +7 -7
- package/skills/hmos-incremental-ui-align/SKILL.md +1 -1
- package/skills/hmos-incremental-ui-align/page_align.md +1 -1
- package/skills/hmos-spec-generate/SKILL.md +2 -2
- package/skills/hmos-spec-generate/scripts/parse_requirements.mjs +537 -0
- package/skills/hmos-fix-build-errors/SKILL.md +0 -266
- package/skills/hmos-fix-build-errors/references/arkts-strict-patterns.md +0 -219
- package/skills/hmos-fix-build-errors/references/known-patterns.md +0 -157
- package/skills/hmos-fix-build-errors/references/rdb-entity-pattern.md +0 -131
- package/skills/hmos-spec-generate/scripts/parse_requirements.ts +0 -515
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/resolve-metadata-tool.mjs +0 -0
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/self-test-runner.mjs +0 -0
- /package/skills/{hmos-integration-test → a2h-integration-test}/scripts/testcases-tool.mjs +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { normalizeAreaTitleFacts } from "./area-titles.js";
|
|
2
|
+
import { normalizeAndroidFacts, prepareAndroidStructure } from "./android.js";
|
|
3
|
+
import { SourceFactsBuilder } from "./builder.js";
|
|
4
|
+
import { normalizeComposeFacts } from "./compose.js";
|
|
5
|
+
import { normalizeComposeCallSiteFacts } from "./compose-call-sites.js";
|
|
6
|
+
import { normalizeInteractionEffects } from "./interaction-effects.js";
|
|
7
|
+
import { normalizeJvmFacts, prepareJvmStructure } from "./jvm.js";
|
|
8
|
+
import { normalizePendingIntentFacts } from "./pending-intents.js";
|
|
9
|
+
import { normalizeRouteStateVariants } from "./route-state-variants.js";
|
|
10
|
+
import { NormalizationState } from "./state.js";
|
|
11
|
+
export function buildSourceFacts(input) {
|
|
12
|
+
const builder = new SourceFactsBuilder(input.caseId, {
|
|
13
|
+
...input.android.target,
|
|
14
|
+
...(input.sourceSnapshot ? { sourceSnapshot: input.sourceSnapshot } : {}),
|
|
15
|
+
});
|
|
16
|
+
const state = new NormalizationState(
|
|
17
|
+
builder,
|
|
18
|
+
input.android.manifest.packageName,
|
|
19
|
+
);
|
|
20
|
+
// Component identities must exist before calls and XML controls establish
|
|
21
|
+
// their strong owner references.
|
|
22
|
+
prepareAndroidStructure(input.android, state);
|
|
23
|
+
const jvmIndex = prepareJvmStructure(input.jvm, state);
|
|
24
|
+
normalizeAndroidFacts(input.android, state);
|
|
25
|
+
normalizeComposeFacts(input.android, input.jvm, state);
|
|
26
|
+
normalizeJvmFacts(input.jvm, state, jvmIndex);
|
|
27
|
+
normalizeComposeCallSiteFacts(input.android, input.jvm, state);
|
|
28
|
+
normalizeAreaTitleFacts(input.android, state, jvmIndex);
|
|
29
|
+
normalizePendingIntentFacts(input.jvm, state);
|
|
30
|
+
normalizeInteractionEffects(input.jvm, state);
|
|
31
|
+
normalizeRouteStateVariants(input.jvm, state);
|
|
32
|
+
return builder.finish();
|
|
33
|
+
}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
function objectValue(value) {
|
|
2
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: undefined;
|
|
5
|
+
}
|
|
6
|
+
function object(record, key) {
|
|
7
|
+
return objectValue(record.data[key]);
|
|
8
|
+
}
|
|
9
|
+
function objectText(value, key) {
|
|
10
|
+
const candidate = value?.[key];
|
|
11
|
+
return typeof candidate === "string" && candidate.trim().length > 0
|
|
12
|
+
? candidate.trim()
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
15
|
+
function text(record, key) {
|
|
16
|
+
const candidate = record.data[key];
|
|
17
|
+
return typeof candidate === "string" && candidate.trim().length > 0
|
|
18
|
+
? candidate.trim()
|
|
19
|
+
: undefined;
|
|
20
|
+
}
|
|
21
|
+
function boolean(record, key) {
|
|
22
|
+
const candidate = record.data[key];
|
|
23
|
+
return typeof candidate === "boolean" ? candidate : undefined;
|
|
24
|
+
}
|
|
25
|
+
function sourceKind(record) {
|
|
26
|
+
const language = text(record, "language");
|
|
27
|
+
if (language === "kotlin" || record.location?.path.endsWith(".kt")) {
|
|
28
|
+
return "kotlin";
|
|
29
|
+
}
|
|
30
|
+
if (language === "java" || record.location?.path.endsWith(".java")) {
|
|
31
|
+
return "java";
|
|
32
|
+
}
|
|
33
|
+
return "other";
|
|
34
|
+
}
|
|
35
|
+
function recordLocation(record, anchor, symbol) {
|
|
36
|
+
if (!record.location) return undefined;
|
|
37
|
+
return {
|
|
38
|
+
sourceKind: sourceKind(record),
|
|
39
|
+
path: record.location.path,
|
|
40
|
+
anchor,
|
|
41
|
+
range: {
|
|
42
|
+
startLine: Math.max(1, record.location.startLine),
|
|
43
|
+
endLine: Math.max(
|
|
44
|
+
Math.max(1, record.location.startLine),
|
|
45
|
+
record.location.endLine,
|
|
46
|
+
),
|
|
47
|
+
...(record.location.startColumn >= 1
|
|
48
|
+
? { startColumn: record.location.startColumn }
|
|
49
|
+
: {}),
|
|
50
|
+
...(record.location.endColumn >= 1
|
|
51
|
+
? { endColumn: record.location.endColumn }
|
|
52
|
+
: {}),
|
|
53
|
+
},
|
|
54
|
+
...(symbol ? { symbol } : {}),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function appendBinding(bindings, record) {
|
|
58
|
+
const callRef = text(record, "callRef");
|
|
59
|
+
if (!callRef) return;
|
|
60
|
+
const values = bindings.get(callRef) ?? [];
|
|
61
|
+
values.push(record);
|
|
62
|
+
bindings.set(callRef, values);
|
|
63
|
+
}
|
|
64
|
+
function buildIndex(jvm) {
|
|
65
|
+
const callsById = new Map();
|
|
66
|
+
const resolvedCalls = [];
|
|
67
|
+
const bindingsByCallRef = new Map();
|
|
68
|
+
const methodsBySignature = new Map();
|
|
69
|
+
const returnsByCallableRef = new Map();
|
|
70
|
+
for (const record of jvm.body) {
|
|
71
|
+
if (record.kind === "resolved-call" || record.kind === "unresolved-call") {
|
|
72
|
+
callsById.set(record.id, record);
|
|
73
|
+
if (record.kind === "resolved-call") resolvedCalls.push(record);
|
|
74
|
+
} else if (record.kind === "call-argument-binding") {
|
|
75
|
+
appendBinding(bindingsByCallRef, record);
|
|
76
|
+
} else if (record.kind === "method-declaration") {
|
|
77
|
+
const signature = text(record, "signature");
|
|
78
|
+
if (signature) {
|
|
79
|
+
const values = methodsBySignature.get(signature) ?? [];
|
|
80
|
+
values.push(record);
|
|
81
|
+
methodsBySignature.set(signature, values);
|
|
82
|
+
}
|
|
83
|
+
} else if (record.kind === "return") {
|
|
84
|
+
const callableRef = text(record, "enclosingCallableRef");
|
|
85
|
+
if (!callableRef) continue;
|
|
86
|
+
const values = returnsByCallableRef.get(callableRef) ?? [];
|
|
87
|
+
values.push(record);
|
|
88
|
+
returnsByCallableRef.set(callableRef, values);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
callsById,
|
|
93
|
+
resolvedCalls: resolvedCalls.sort((left, right) =>
|
|
94
|
+
left.id.localeCompare(right.id, "en"),
|
|
95
|
+
),
|
|
96
|
+
bindingsByCallRef,
|
|
97
|
+
methodsBySignature,
|
|
98
|
+
returnsByCallableRef,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function expression(binding) {
|
|
102
|
+
return object(binding, "expression");
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Follows only analyzer-provided expression structure. In particular, this
|
|
106
|
+
* deliberately does not parse argumentSource, initializerSource, or source.
|
|
107
|
+
*/
|
|
108
|
+
function callRefFromExpression(value, visited = new Set()) {
|
|
109
|
+
if (!value || visited.has(value)) return undefined;
|
|
110
|
+
visited.add(value);
|
|
111
|
+
const direct = objectText(value, "callRef");
|
|
112
|
+
if (direct) return direct;
|
|
113
|
+
const symbol = objectValue(value.resolvedSymbol);
|
|
114
|
+
const initializer = objectValue(symbol?.initializer);
|
|
115
|
+
return initializer ? callRefFromExpression(initializer, visited) : undefined;
|
|
116
|
+
}
|
|
117
|
+
function classFromExpression(value, visited = new Set()) {
|
|
118
|
+
if (!value || visited.has(value)) return undefined;
|
|
119
|
+
visited.add(value);
|
|
120
|
+
const literal = objectValue(value.classLiteral);
|
|
121
|
+
const direct = objectText(literal, "resolvedClassQualifiedName");
|
|
122
|
+
if (direct) return direct;
|
|
123
|
+
const symbol = objectValue(value.resolvedSymbol);
|
|
124
|
+
const initializer = objectValue(symbol?.initializer);
|
|
125
|
+
return initializer ? classFromExpression(initializer, visited) : undefined;
|
|
126
|
+
}
|
|
127
|
+
function resourceFromExpression(value, resourceType) {
|
|
128
|
+
const resource = objectValue(value?.resourceReference);
|
|
129
|
+
return objectText(resource, "resourceType") === resourceType
|
|
130
|
+
? objectText(resource, "resourceName")
|
|
131
|
+
: undefined;
|
|
132
|
+
}
|
|
133
|
+
function uniqueString(values) {
|
|
134
|
+
const unique = [...new Set(values.filter((value) => value !== undefined))];
|
|
135
|
+
return unique.length === 1 ? unique[0] : undefined;
|
|
136
|
+
}
|
|
137
|
+
function isRemoteViewsClick(call) {
|
|
138
|
+
return (
|
|
139
|
+
text(call, "targetOwnerQualifiedName") === "android.widget.RemoteViews" &&
|
|
140
|
+
text(call, "targetName") === "setOnClickPendingIntent"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
function isRemoteViewsConstructor(call) {
|
|
144
|
+
return (
|
|
145
|
+
text(call, "targetOwnerQualifiedName") === "android.widget.RemoteViews" &&
|
|
146
|
+
text(call, "targetName") === "RemoteViews" &&
|
|
147
|
+
boolean(call, "targetConstructor") === true
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
function isNotificationContentIntent(call) {
|
|
151
|
+
const owner = text(call, "targetOwnerQualifiedName");
|
|
152
|
+
return (
|
|
153
|
+
text(call, "targetName") === "setContentIntent" &&
|
|
154
|
+
(owner === "android.app.Notification.Builder" ||
|
|
155
|
+
owner === "androidx.core.app.NotificationCompat.Builder")
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
function isActivityPendingIntent(call) {
|
|
159
|
+
return (
|
|
160
|
+
text(call, "targetOwnerQualifiedName") === "android.app.PendingIntent" &&
|
|
161
|
+
text(call, "targetName") === "getActivity"
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function isIntentConstructor(call) {
|
|
165
|
+
return (
|
|
166
|
+
text(call, "targetOwnerQualifiedName") === "android.content.Intent" &&
|
|
167
|
+
text(call, "targetName") === "Intent" &&
|
|
168
|
+
boolean(call, "targetConstructor") === true
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
function callsReferencedByBindings(callRef, index) {
|
|
172
|
+
const refs = (index.bindingsByCallRef.get(callRef) ?? [])
|
|
173
|
+
.map((binding) => callRefFromExpression(expression(binding)))
|
|
174
|
+
.filter((ref) => ref !== undefined);
|
|
175
|
+
return [...new Set(refs)]
|
|
176
|
+
.map((ref) => index.callsById.get(ref))
|
|
177
|
+
.filter((call) => call !== undefined);
|
|
178
|
+
}
|
|
179
|
+
function receiverInitializerCall(call, index) {
|
|
180
|
+
const receiver = object(call, "receiverResolvedSymbol");
|
|
181
|
+
const initializer = objectValue(receiver?.initializer);
|
|
182
|
+
const callRef = callRefFromExpression(initializer);
|
|
183
|
+
return callRef ? index.callsById.get(callRef) : undefined;
|
|
184
|
+
}
|
|
185
|
+
function layoutForRemoteViews(clickCall, index) {
|
|
186
|
+
const constructor = receiverInitializerCall(clickCall, index);
|
|
187
|
+
if (!constructor || !isRemoteViewsConstructor(constructor)) {
|
|
188
|
+
return undefined;
|
|
189
|
+
}
|
|
190
|
+
return uniqueString(
|
|
191
|
+
(index.bindingsByCallRef.get(constructor.id) ?? []).map((binding) =>
|
|
192
|
+
resourceFromExpression(expression(binding), "layout"),
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
function intentForPendingIntent(pendingIntent, index) {
|
|
197
|
+
const candidates = callsReferencedByBindings(pendingIntent.id, index).filter(
|
|
198
|
+
isIntentConstructor,
|
|
199
|
+
);
|
|
200
|
+
return candidates.length === 1 ? candidates[0] : undefined;
|
|
201
|
+
}
|
|
202
|
+
function targetForIntent(intent, index) {
|
|
203
|
+
return uniqueString(
|
|
204
|
+
(index.bindingsByCallRef.get(intent.id) ?? []).map((binding) =>
|
|
205
|
+
classFromExpression(expression(binding)),
|
|
206
|
+
),
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
function uniqueRecords(records) {
|
|
210
|
+
return [
|
|
211
|
+
...new Map(records.map((record) => [record.id, record])).values(),
|
|
212
|
+
].sort((left, right) => left.id.localeCompare(right.id, "en"));
|
|
213
|
+
}
|
|
214
|
+
function directActivityPendingIntent(call, index) {
|
|
215
|
+
if (!isActivityPendingIntent(call)) return undefined;
|
|
216
|
+
const intentCall = intentForPendingIntent(call, index);
|
|
217
|
+
const targetClass = intentCall
|
|
218
|
+
? targetForIntent(intentCall, index)
|
|
219
|
+
: undefined;
|
|
220
|
+
return intentCall && targetClass
|
|
221
|
+
? {
|
|
222
|
+
pendingIntentCall: call,
|
|
223
|
+
intentCall,
|
|
224
|
+
targetClass,
|
|
225
|
+
flowRecords: uniqueRecords([call, intentCall]),
|
|
226
|
+
}
|
|
227
|
+
: undefined;
|
|
228
|
+
}
|
|
229
|
+
function activityPendingIntentFromExpression(
|
|
230
|
+
value,
|
|
231
|
+
index,
|
|
232
|
+
visitedDeclarations = new Set(),
|
|
233
|
+
) {
|
|
234
|
+
const callRef = callRefFromExpression(value);
|
|
235
|
+
const call = callRef ? index.callsById.get(callRef) : undefined;
|
|
236
|
+
return call
|
|
237
|
+
? activityPendingIntentFromCall(call, index, visitedDeclarations)
|
|
238
|
+
: undefined;
|
|
239
|
+
}
|
|
240
|
+
function activityPendingIntentFromCall(call, index, visitedDeclarations) {
|
|
241
|
+
const direct = directActivityPendingIntent(call, index);
|
|
242
|
+
if (direct) return direct;
|
|
243
|
+
const targetSignature = text(call, "targetSignature");
|
|
244
|
+
const declarations = targetSignature
|
|
245
|
+
? (index.methodsBySignature.get(targetSignature) ?? [])
|
|
246
|
+
: [];
|
|
247
|
+
const declaration = declarations.length === 1 ? declarations[0] : undefined;
|
|
248
|
+
if (!declaration || visitedDeclarations.has(declaration.id)) {
|
|
249
|
+
return undefined;
|
|
250
|
+
}
|
|
251
|
+
const returns = [
|
|
252
|
+
...(index.returnsByCallableRef.get(declaration.id) ?? []),
|
|
253
|
+
].sort((left, right) => left.id.localeCompare(right.id, "en"));
|
|
254
|
+
if (returns.length === 0) return undefined;
|
|
255
|
+
const nextVisited = new Set(visitedDeclarations);
|
|
256
|
+
nextVisited.add(declaration.id);
|
|
257
|
+
const resolutions = returns.map((record) =>
|
|
258
|
+
activityPendingIntentFromExpression(
|
|
259
|
+
object(record, "expression"),
|
|
260
|
+
index,
|
|
261
|
+
nextVisited,
|
|
262
|
+
),
|
|
263
|
+
);
|
|
264
|
+
// A partially understood factory is not a unique static route. Every
|
|
265
|
+
// observed return must resolve and all of them must agree on one Activity.
|
|
266
|
+
if (resolutions.some((resolution) => resolution === undefined)) {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
const resolved = resolutions;
|
|
270
|
+
const targetClass = uniqueString(
|
|
271
|
+
resolved.map((resolution) => resolution.targetClass),
|
|
272
|
+
);
|
|
273
|
+
if (!targetClass) return undefined;
|
|
274
|
+
const representative = [...resolved].sort((left, right) =>
|
|
275
|
+
left.pendingIntentCall.id.localeCompare(right.pendingIntentCall.id, "en"),
|
|
276
|
+
)[0];
|
|
277
|
+
return {
|
|
278
|
+
pendingIntentCall: representative.pendingIntentCall,
|
|
279
|
+
intentCall: representative.intentCall,
|
|
280
|
+
targetClass,
|
|
281
|
+
flowRecords: uniqueRecords([
|
|
282
|
+
call,
|
|
283
|
+
declaration,
|
|
284
|
+
...returns,
|
|
285
|
+
...resolved.flatMap((resolution) => resolution.flowRecords),
|
|
286
|
+
]),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function activityPendingIntentArgument(call, index) {
|
|
290
|
+
const bindings = (index.bindingsByCallRef.get(call.id) ?? []).filter(
|
|
291
|
+
(binding) => text(binding, "parameterType") === "android.app.PendingIntent",
|
|
292
|
+
);
|
|
293
|
+
return bindings.length === 1
|
|
294
|
+
? activityPendingIntentFromExpression(expression(bindings[0]), index)
|
|
295
|
+
: undefined;
|
|
296
|
+
}
|
|
297
|
+
function resourceIdForClick(clickCall, index) {
|
|
298
|
+
return uniqueString(
|
|
299
|
+
(index.bindingsByCallRef.get(clickCall.id) ?? []).map((binding) =>
|
|
300
|
+
resourceFromExpression(expression(binding), "id"),
|
|
301
|
+
),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function widgetControl(state, clickCall, layout, resourceId) {
|
|
305
|
+
const enclosingClass = text(clickCall, "enclosingClass");
|
|
306
|
+
const enclosingOwner = enclosingClass
|
|
307
|
+
? state.componentByQualifiedName.get(enclosingClass)
|
|
308
|
+
: undefined;
|
|
309
|
+
const enclosingWidget =
|
|
310
|
+
enclosingOwner && state.componentTypeById.get(enclosingOwner) === "widget"
|
|
311
|
+
? enclosingOwner
|
|
312
|
+
: undefined;
|
|
313
|
+
let candidates = [...(state.uiByLayout.get(layout) ?? [])]
|
|
314
|
+
.map((id) => state.builder.fact(id))
|
|
315
|
+
.filter(
|
|
316
|
+
(fact) =>
|
|
317
|
+
fact?.kind === "ui-element" &&
|
|
318
|
+
fact.data.resourceId === resourceId &&
|
|
319
|
+
state.componentTypeById.get(fact.data.ownerKey) === "widget",
|
|
320
|
+
);
|
|
321
|
+
if (enclosingWidget) {
|
|
322
|
+
const owned = candidates.filter(
|
|
323
|
+
(fact) =>
|
|
324
|
+
fact?.kind === "ui-element" && fact.data.ownerKey === enclosingWidget,
|
|
325
|
+
);
|
|
326
|
+
if (owned.length > 0) candidates = owned;
|
|
327
|
+
}
|
|
328
|
+
const unique = [
|
|
329
|
+
...new Map(candidates.map((fact) => [fact?.id, fact])).values(),
|
|
330
|
+
];
|
|
331
|
+
const control = unique.length === 1 ? unique[0] : undefined;
|
|
332
|
+
return control?.kind === "ui-element"
|
|
333
|
+
? {
|
|
334
|
+
controlRef: control.id,
|
|
335
|
+
widgetRef: control.data.ownerKey,
|
|
336
|
+
}
|
|
337
|
+
: undefined;
|
|
338
|
+
}
|
|
339
|
+
function resolveRemoteViewsClick(state, clickCall, index) {
|
|
340
|
+
const layout = layoutForRemoteViews(clickCall, index);
|
|
341
|
+
const resourceId = resourceIdForClick(clickCall, index);
|
|
342
|
+
const pendingIntent = activityPendingIntentArgument(clickCall, index);
|
|
343
|
+
if (!layout || !resourceId || !pendingIntent) return undefined;
|
|
344
|
+
const control = widgetControl(state, clickCall, layout, resourceId);
|
|
345
|
+
if (!control) return undefined;
|
|
346
|
+
return {
|
|
347
|
+
clickCall,
|
|
348
|
+
pendingIntentCall: pendingIntent.pendingIntentCall,
|
|
349
|
+
intentCall: pendingIntent.intentCall,
|
|
350
|
+
targetClass: pendingIntent.targetClass,
|
|
351
|
+
layout,
|
|
352
|
+
resourceId,
|
|
353
|
+
...control,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function addRemoteViewsClickFacts(state, resolution) {
|
|
357
|
+
const clickSignature =
|
|
358
|
+
text(resolution.clickCall, "targetSignature") ??
|
|
359
|
+
"android.widget.RemoteViews#setOnClickPendingIntent";
|
|
360
|
+
const pendingSignature =
|
|
361
|
+
text(resolution.pendingIntentCall, "targetSignature") ??
|
|
362
|
+
"android.app.PendingIntent#getActivity";
|
|
363
|
+
const clickLocation = recordLocation(
|
|
364
|
+
resolution.clickCall,
|
|
365
|
+
`remote-views-click:${resolution.layout}/${resolution.resourceId}`,
|
|
366
|
+
clickSignature,
|
|
367
|
+
);
|
|
368
|
+
const pendingLocation = recordLocation(
|
|
369
|
+
resolution.pendingIntentCall,
|
|
370
|
+
`activity-pending-intent:${resolution.targetClass}`,
|
|
371
|
+
pendingSignature,
|
|
372
|
+
);
|
|
373
|
+
const targetLocation = recordLocation(
|
|
374
|
+
resolution.intentCall,
|
|
375
|
+
`explicit-intent-target:${resolution.targetClass}`,
|
|
376
|
+
resolution.targetClass,
|
|
377
|
+
);
|
|
378
|
+
if (!clickLocation || !pendingLocation || !targetLocation) return;
|
|
379
|
+
const targetRef = state.ensureComponent(
|
|
380
|
+
resolution.targetClass,
|
|
381
|
+
"activity",
|
|
382
|
+
targetLocation,
|
|
383
|
+
"explicit Activity PendingIntent target",
|
|
384
|
+
"high",
|
|
385
|
+
{ uiTechnology: "views" },
|
|
386
|
+
);
|
|
387
|
+
const callbackRef = state.builder.addFact("callback", {
|
|
388
|
+
identity: [
|
|
389
|
+
{ kind: "component", value: resolution.widgetRef },
|
|
390
|
+
{
|
|
391
|
+
kind: "callback",
|
|
392
|
+
value:
|
|
393
|
+
`remote-views:${resolution.clickCall.id}:` +
|
|
394
|
+
resolution.pendingIntentCall.id,
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
confidence: "high",
|
|
398
|
+
support: [
|
|
399
|
+
{
|
|
400
|
+
location: clickLocation,
|
|
401
|
+
role: "RemoteViews PendingIntent click binding",
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
location: pendingLocation,
|
|
405
|
+
role: "resolved activity PendingIntent factory",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
data: {
|
|
409
|
+
ownerKey: resolution.widgetRef,
|
|
410
|
+
callableSymbol: `${clickSignature} -> ${pendingSignature}`,
|
|
411
|
+
callbackKind: "function",
|
|
412
|
+
},
|
|
413
|
+
relatedFactRefs: [resolution.widgetRef],
|
|
414
|
+
});
|
|
415
|
+
const interactionRef = state.builder.addFact("interaction-binding", {
|
|
416
|
+
identity: [
|
|
417
|
+
{ kind: "control", value: resolution.controlRef },
|
|
418
|
+
{ kind: "callback", value: callbackRef },
|
|
419
|
+
{ kind: "semantic-key", value: clickSignature },
|
|
420
|
+
],
|
|
421
|
+
confidence: "high",
|
|
422
|
+
support: [
|
|
423
|
+
{
|
|
424
|
+
location: clickLocation,
|
|
425
|
+
role: "resolved RemoteViews click interaction",
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
data: {
|
|
429
|
+
elementKey: resolution.controlRef,
|
|
430
|
+
callbackKey: callbackRef,
|
|
431
|
+
trigger: "click",
|
|
432
|
+
bindingApi: clickSignature,
|
|
433
|
+
},
|
|
434
|
+
relatedFactRefs: [resolution.controlRef, callbackRef],
|
|
435
|
+
});
|
|
436
|
+
const effectRef = state.builder.addFact("behavior-effect", {
|
|
437
|
+
identity: [
|
|
438
|
+
{ kind: "callback", value: callbackRef },
|
|
439
|
+
{ kind: "component", value: targetRef },
|
|
440
|
+
{ kind: "semantic-key", value: "activity-pending-intent" },
|
|
441
|
+
],
|
|
442
|
+
confidence: "high",
|
|
443
|
+
support: [
|
|
444
|
+
{
|
|
445
|
+
location: pendingLocation,
|
|
446
|
+
role: "PendingIntent launches an Activity",
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
location: targetLocation,
|
|
450
|
+
role: "explicit Intent component target",
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
data: {
|
|
454
|
+
callbackKey: callbackRef,
|
|
455
|
+
effectKind: "navigate-area",
|
|
456
|
+
targetKey: targetRef,
|
|
457
|
+
},
|
|
458
|
+
relatedFactRefs: [callbackRef, targetRef, interactionRef],
|
|
459
|
+
});
|
|
460
|
+
state.builder.addFact("navigation-target", {
|
|
461
|
+
identity: [
|
|
462
|
+
{ kind: "semantic-key", value: effectRef },
|
|
463
|
+
{ kind: "component", value: targetRef },
|
|
464
|
+
],
|
|
465
|
+
confidence: "high",
|
|
466
|
+
support: [
|
|
467
|
+
{
|
|
468
|
+
location: targetLocation,
|
|
469
|
+
role: "explicit Activity class in PendingIntent Intent",
|
|
470
|
+
},
|
|
471
|
+
],
|
|
472
|
+
data: {
|
|
473
|
+
effectKey: effectRef,
|
|
474
|
+
targetKind: "component",
|
|
475
|
+
targetKey: targetRef,
|
|
476
|
+
},
|
|
477
|
+
relatedFactRefs: [effectRef, targetRef],
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
function addNotificationEntry(state, consumer, resolution) {
|
|
481
|
+
const consumerSignature =
|
|
482
|
+
text(consumer, "targetSignature") ??
|
|
483
|
+
`${text(consumer, "targetOwnerQualifiedName") ?? "Notification.Builder"}` +
|
|
484
|
+
"#setContentIntent";
|
|
485
|
+
const consumerLocation = recordLocation(
|
|
486
|
+
consumer,
|
|
487
|
+
`notification-content-intent:${consumer.id}`,
|
|
488
|
+
consumerSignature,
|
|
489
|
+
);
|
|
490
|
+
const targetLocation = recordLocation(
|
|
491
|
+
resolution.intentCall,
|
|
492
|
+
`explicit-intent-target:${resolution.targetClass}`,
|
|
493
|
+
resolution.targetClass,
|
|
494
|
+
);
|
|
495
|
+
if (!consumerLocation || !targetLocation) return;
|
|
496
|
+
const targetRef = state.ensureComponent(
|
|
497
|
+
resolution.targetClass,
|
|
498
|
+
"activity",
|
|
499
|
+
targetLocation,
|
|
500
|
+
"explicit notification Activity target",
|
|
501
|
+
"high",
|
|
502
|
+
{ uiTechnology: "views" },
|
|
503
|
+
);
|
|
504
|
+
const flowSupport = resolution.flowRecords.flatMap((record) => {
|
|
505
|
+
const location = recordLocation(
|
|
506
|
+
record,
|
|
507
|
+
`notification-pending-intent-flow:${record.id}`,
|
|
508
|
+
text(record, "targetSignature") ??
|
|
509
|
+
text(record, "signature") ??
|
|
510
|
+
resolution.targetClass,
|
|
511
|
+
);
|
|
512
|
+
return location
|
|
513
|
+
? [
|
|
514
|
+
{
|
|
515
|
+
location,
|
|
516
|
+
role:
|
|
517
|
+
record.kind === "return"
|
|
518
|
+
? "local PendingIntent factory return"
|
|
519
|
+
: "resolved notification PendingIntent flow",
|
|
520
|
+
},
|
|
521
|
+
]
|
|
522
|
+
: [];
|
|
523
|
+
});
|
|
524
|
+
state.builder.addFact("entry-point", {
|
|
525
|
+
identity: [
|
|
526
|
+
{ kind: "entry", value: "notification" },
|
|
527
|
+
{ kind: "component", value: targetRef },
|
|
528
|
+
{ kind: "semantic-key", value: consumer.id },
|
|
529
|
+
],
|
|
530
|
+
confidence: "high",
|
|
531
|
+
support: [
|
|
532
|
+
{
|
|
533
|
+
location: consumerLocation,
|
|
534
|
+
role: "Notification builder consumes content PendingIntent",
|
|
535
|
+
},
|
|
536
|
+
...flowSupport,
|
|
537
|
+
],
|
|
538
|
+
data: {
|
|
539
|
+
entryKind: "notification",
|
|
540
|
+
category: "alternate",
|
|
541
|
+
componentKey: targetRef,
|
|
542
|
+
conditionKeys: [],
|
|
543
|
+
},
|
|
544
|
+
relatedFactRefs: [targetRef],
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Resolves only consumed activity PendingIntents. Merely constructing or
|
|
549
|
+
* returning a PendingIntent is intentionally not treated as a notification
|
|
550
|
+
* entry; an entry kind requires a corresponding consuming API.
|
|
551
|
+
*/
|
|
552
|
+
export function normalizePendingIntentFacts(jvm, state) {
|
|
553
|
+
if (!jvm) return;
|
|
554
|
+
const index = buildIndex(jvm);
|
|
555
|
+
for (const call of index.resolvedCalls) {
|
|
556
|
+
if (isRemoteViewsClick(call)) {
|
|
557
|
+
const resolution = resolveRemoteViewsClick(state, call, index);
|
|
558
|
+
if (resolution) addRemoteViewsClickFacts(state, resolution);
|
|
559
|
+
}
|
|
560
|
+
if (isNotificationContentIntent(call)) {
|
|
561
|
+
const resolution = activityPendingIntentArgument(call, index);
|
|
562
|
+
if (resolution) addNotificationEntry(state, call, resolution);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android resource names and generated ViewBinding properties use two
|
|
3
|
+
* spellings for the same source identity. A layout ID may be written as
|
|
4
|
+
* `search_btn`, while the generated property is `searchBtn`. Some projects
|
|
5
|
+
* also use lower-camel IDs directly in XML, so neither spelling can be
|
|
6
|
+
* treated as the one canonical serialized value.
|
|
7
|
+
*
|
|
8
|
+
* These helpers deliberately keep a small, reversible alias set. We do not
|
|
9
|
+
* remove separators or case-fold arbitrary strings: if two controls share an
|
|
10
|
+
* alias in one owner, callers will see both candidates and retain the
|
|
11
|
+
* ambiguity instead of silently joining them.
|
|
12
|
+
*/
|
|
13
|
+
export function camelToSnakeResourceId(value) {
|
|
14
|
+
return value
|
|
15
|
+
.replace(/([a-z0-9])([A-Z])/gu, "$1_$2")
|
|
16
|
+
.replace(/([A-Z]+)([A-Z][a-z])/gu, "$1_$2")
|
|
17
|
+
.toLowerCase();
|
|
18
|
+
}
|
|
19
|
+
export function snakeToLowerCamelResourceId(value) {
|
|
20
|
+
return value.replace(/_+([A-Za-z0-9])/gu, (_match, next) =>
|
|
21
|
+
next.toUpperCase(),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
export function resourceIdAliases(value) {
|
|
25
|
+
const exact = value.trim();
|
|
26
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(exact)) {
|
|
27
|
+
return exact.length > 0 ? [exact] : [];
|
|
28
|
+
}
|
|
29
|
+
const snake = camelToSnakeResourceId(exact);
|
|
30
|
+
return [
|
|
31
|
+
...new Set([
|
|
32
|
+
exact,
|
|
33
|
+
snake,
|
|
34
|
+
snakeToLowerCamelResourceId(exact),
|
|
35
|
+
snakeToLowerCamelResourceId(snake),
|
|
36
|
+
]),
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
export function resourceIdsEquivalent(left, right) {
|
|
40
|
+
const rightAliases = new Set(resourceIdAliases(right));
|
|
41
|
+
return resourceIdAliases(left).some((alias) => rightAliases.has(alias));
|
|
42
|
+
}
|