@ohos-cpf/3rdloop 0.0.8 → 0.0.9

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 (219) hide show
  1. package/README.md +25 -0
  2. package/lib/cli.js +22 -5
  3. package/lib/doctor.js +111 -10
  4. package/lib/mcp.js +498 -0
  5. package/package.json +6 -2
  6. package/vendor/Archive/data/.index/meta.json +7 -0
  7. package/vendor/Archive/data/.index/search.db +0 -0
  8. package/vendor/Archive/data/docs/experiences/arkts-check-tool-pitfalls-baseline-comparison.md +13 -0
  9. package/vendor/Archive/data/docs/experiences/arkts-deep-path-import-recognition.md +31 -0
  10. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-entry-file-case-mismatch.md +25 -0
  11. package/vendor/Archive/data/docs/experiences/arkts-demo-coverage-import-trailing-slash.md +20 -0
  12. package/vendor/Archive/data/docs/experiences/arkts-library-source-files-may-be-ts.md +18 -0
  13. package/vendor/Archive/data/docs/experiences/arkts-typedarray-buffer-interface-cast-compile-fixes.md +36 -0
  14. package/vendor/Archive/data/docs/experiences/arkts-xts-compile-error-clustering-fix.md +24 -0
  15. package/vendor/Archive/data/docs/experiences/arkts-xts-doc-expected-value-runtime-probe.md +35 -0
  16. package/vendor/Archive/data/docs/experiences/arkts-xts-fixture-rawfile-testability-injection.md +33 -0
  17. package/vendor/Archive/data/docs/experiences/code-linter-chinese-comment-false-positive.md +16 -0
  18. package/vendor/Archive/data/docs/experiences/esobject-new-expression-variable-tracking.md +33 -0
  19. package/vendor/Archive/data/docs/experiences/filesystem-case-sensitivity-module-resolution.md +31 -0
  20. package/vendor/Archive/data/docs/experiences/gitcode-clone-redirect-warning-normal.md +13 -0
  21. package/vendor/Archive/data/docs/experiences/kn_msgvq1w585ec8319.md +77 -0
  22. package/vendor/Archive/data/docs/experiences/kn_msovnm6rd46de72c.md +98 -0
  23. package/vendor/Archive/data/docs/experiences/kn_mspt8vntfe708576.md +87 -0
  24. package/vendor/Archive/data/docs/experiences/kn_mspu1ei77adbbb48.md +133 -0
  25. package/vendor/Archive/data/docs/experiences/kn_mspubgak8d30bfce.md +128 -0
  26. package/vendor/Archive/data/docs/experiences/kn_mspug2de3cb1f7c3.md +115 -0
  27. package/vendor/Archive/data/docs/experiences/kn_skill_opt_001.md +38 -0
  28. package/vendor/Archive/data/docs/experiences/loop-artifacts-verification-evidence-triangle.md +19 -0
  29. package/vendor/Archive/data/docs/experiences/macos-bash-replace-build-ps1.md +19 -0
  30. package/vendor/Archive/data/docs/experiences/macos-bsd-diff-q-illegal-option.md +15 -0
  31. package/vendor/Archive/data/docs/experiences/migrate-cjs-compatible-api-version-map-limit.md +17 -0
  32. package/vendor/Archive/data/docs/experiences/mp3agic-arkts-library-behavior-quirks.md +21 -0
  33. package/vendor/Archive/data/docs/experiences/ohos-face-detection-scan-api-mapping.md +15 -0
  34. package/vendor/Archive/data/docs/experiences/pr-metadata-and-diff-integrity-verification.md +12 -0
  35. package/vendor/Archive/data/docs/experiences/rn-coverage-index-signature-pseudo-member-false-negative.md +32 -0
  36. package/vendor/Archive/data/docs/experiences/rn-memory-check-severity-calibration.md +21 -0
  37. package/vendor/Archive/data/docs/experiences/rn-platform-specific-api-exclusion-evidence.md +24 -0
  38. package/vendor/Archive/data/docs/experiences/rn-upstream-version-diff-pitfalls.md +17 -0
  39. package/vendor/Archive/data/docs/experiences/rn-version-gap-risk-rating-rules.md +13 -0
  40. package/vendor/Archive/data/docs/experiences/rn-view-lib-native-contract-diff-fabric-spec.md +16 -0
  41. package/vendor/Archive/data/docs/experiences/rnoh-adapter-package-version-field-semantics.md +18 -0
  42. package/vendor/Archive/data/docs/experiences/rnoh-arkts-sqlite-rows-array-format.md +18 -0
  43. package/vendor/Archive/data/docs/experiences/rnoh-dependency-residue-scan-strategy.md +18 -0
  44. package/vendor/Archive/data/docs/experiences/rnoh-example-local-lib-dep-use-tgz.md +53 -0
  45. package/vendor/Archive/data/docs/experiences/rnoh-example-prereserve-rntesterlist.md +17 -0
  46. package/vendor/Archive/data/docs/experiences/rnoh-example-template-compatiblesdkversion-upgrade.md +29 -0
  47. package/vendor/Archive/data/docs/experiences/rnoh-example-turbomodule-shell-adaptation-checklist.md +41 -0
  48. package/vendor/Archive/data/docs/experiences/rnoh-mcp-build-project-tool-risk.md +18 -0
  49. package/vendor/Archive/data/docs/experiences/rnoh-npm-devdependencies-version-precheck.md +29 -0
  50. package/vendor/Archive/data/docs/experiences/rnoh-repo-baseline-version-verification.md +13 -0
  51. package/vendor/Archive/data/docs/experiences/rnoh-repo-upstream-directory-mapping.md +18 -0
  52. package/vendor/Archive/data/docs/experiences/rnoh-stateless-library-zero-leak-verification.md +31 -0
  53. package/vendor/Archive/data/docs/experiences/rnoh-template-npmrc-credential-removal.md +15 -0
  54. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-codegen-wiring-verification.md +19 -0
  55. package/vendor/Archive/data/docs/experiences/rnoh-turbomodule-cpp-methodmap-compatibility-check.md +20 -0
  56. package/vendor/Archive/data/docs/experiences/scenario-doc-vs-source-signature-diff-four-patterns.md +24 -0
  57. package/vendor/Archive/data/docs/experiences/static-analysis-grep-cross-validation.md +29 -0
  58. package/vendor/Archive/demo.js +489 -0
  59. package/vendor/Archive/index.js +43 -0
  60. package/vendor/Archive/package.json +16 -0
  61. package/vendor/Archive/src/constants.js +83 -0
  62. package/vendor/Archive/src/data/stopwords.txt +110 -0
  63. package/vendor/Archive/src/data/synonyms.json +25 -0
  64. package/vendor/Archive/src/data/terms.txt +59 -0
  65. package/vendor/Archive/src/db/sqlite-backend.js +312 -0
  66. package/vendor/Archive/src/db/sqlite-schema.js +64 -0
  67. package/vendor/Archive/src/engine.js +501 -0
  68. package/vendor/Archive/src/index/index-manager.js +203 -0
  69. package/vendor/Archive/src/index/index-state.js +94 -0
  70. package/vendor/Archive/src/index/search-text-builder.js +106 -0
  71. package/vendor/Archive/src/index.js +22 -0
  72. package/vendor/Archive/src/parser/api-identifiers.js +121 -0
  73. package/vendor/Archive/src/parser/api-suffixes.js +48 -0
  74. package/vendor/Archive/src/parser/heading-normalize.js +40 -0
  75. package/vendor/Archive/src/parser/markdown-splitter.js +215 -0
  76. package/vendor/Archive/src/search/catalog-routing.js +87 -0
  77. package/vendor/Archive/src/search/fts-search.js +289 -0
  78. package/vendor/Archive/src/search/query-normalizer.js +159 -0
  79. package/vendor/Archive/src/search/query-rewriter.js +72 -0
  80. package/vendor/Archive/src/search/search-engine.js +72 -0
  81. package/vendor/Archive/src/search/snippet.js +64 -0
  82. package/vendor/Archive/src/store/dir-store.js +103 -0
  83. package/vendor/Archive/src/store/doc-store.js +36 -0
  84. package/vendor/Archive/src/tokenizer/lexicon.js +151 -0
  85. package/vendor/Archive/src/tokenizer/tokenizer.js +167 -0
  86. package/vendor/Archive/src/types.js +86 -0
  87. package/vendor/Archive/src/utils/hash.js +16 -0
  88. package/vendor/Archive/src/utils/lock.js +73 -0
  89. package/vendor/Archive/src/utils/path-safety.js +96 -0
  90. package/vendor/Archive/src/utils/paths.js +39 -0
  91. package/vendor/BadCase/rn/INDEX.md +127 -0
  92. package/vendor/BadCase/rn/cases/KI-001-webview/350/247/206/351/242/221/346/227/240/346/263/225/346/250/252/345/261/217.md +52 -0
  93. package/vendor/BadCase/rn/cases/KI-002-fastImage/347/203/255/346/233/264/346/226/260/345/233/276/347/211/207/345/244/261/350/264/245.md +37 -0
  94. package/vendor/BadCase/rn/cases/KI-003-wechat/347/274/251/347/225/245/345/233/276/346/234/254/345/234/260/345/233/276/347/211/207.md +62 -0
  95. package/vendor/BadCase/rn/cases/KI-004-wechat/345/233/236/350/260/203/346/227/266/345/272/217.md +47 -0
  96. package/vendor/BadCase/rn/cases/KI-005-geolocation/345/257/274/345/205/245/350/267/257/345/276/204/345/220/216/347/274/200.md +31 -0
  97. package/vendor/BadCase/rn/cases/KI-006-SmartRefreshLayout/346/231/272/350/203/275/346/214/207/351/222/210/345/264/251/346/272/203.md +55 -0
  98. package/vendor/BadCase/rn/cases/KI-007-orientation-locker/344/274/240/346/204/237/345/231/250/345/210/235/345/247/213/345/214/226.md +58 -0
  99. package/vendor/BadCase/rn/cases/KI-008-orientation-locker/344/274/240/346/204/237/345/231/250/345/274/225/347/224/250/350/256/241/346/225/260.md +47 -0
  100. package/vendor/BadCase/rn/cases/KI-009-pager-view/345/265/214/345/245/227/346/273/221/345/212/250.md +38 -0
  101. package/vendor/BadCase/rn/cases/KI-010-screens/346/211/213/345/212/277/345/206/262/347/252/201.md +48 -0
  102. package/vendor/BadCase/rn/cases/KI-011-spring-scrollview/346/211/213/345/212/277/351/207/215/345/244/215/347/273/221/345/256/232.md +49 -0
  103. package/vendor/BadCase/rn/cases/KI-012-qr-camera/346/250/252/345/261/217/351/200/202/351/205/215.md +59 -0
  104. package/vendor/BadCase/rn/cases/KI-013-webview/346/267/261/350/211/262/346/250/241/345/274/217/350/203/214/346/231/257/350/211/262.md +37 -0
  105. package/vendor/BadCase/rn/cases/KI-014-amap-geolocation/345/233/236/350/260/203/347/274/272/345/244/261.md +42 -0
  106. package/vendor/BadCase/rn/cases/KI-015-reanimated/350/277/220/350/241/214/346/227/266/346/236/220/346/236/204/345/264/251/346/272/203.md +54 -0
  107. package/vendor/BadCase/rn/cases/KI-016-fs/346/226/207/344/273/266/350/257/273/345/217/226/347/274/226/347/240/201.md +58 -0
  108. package/vendor/BadCase/rn/cases/KI-017-blob-util/345/217/202/346/225/260/347/274/226/347/240/201/351/224/231/350/257/257.md +39 -0
  109. package/vendor/BadCase/rn/cases/KI-018-modal/345/274/271/347/252/227/351/227/252/347/203/201.md +53 -0
  110. package/vendor/BadCase/rn/cases/KI-019-code-push/351/207/215/345/220/257/351/200/273/350/276/221/345/244/261/346/225/210.md +79 -0
  111. package/vendor/BadCase/rn/cases/KI-020-netinfo/346/225/217/346/204/237/344/277/241/346/201/257/346/263/204/351/234/262.md +60 -0
  112. package/vendor/BadCase/rn/cases/KI-021-video/346/234/254/345/234/260/350/247/206/351/242/221/346/222/255/346/224/276/345/274/202/345/270/270.md +79 -0
  113. package/vendor/BadCase/rn/cases/KI-022-audio-recorder-player/346/236/232/344/270/276/347/274/272/345/244/261.md +38 -0
  114. package/vendor/BadCase/rn/cases/KI-023-svg/346/270/220/345/217/230/347/273/247/346/211/277/345/244/261/346/225/210.md +62 -0
  115. package/vendor/BadCase/rn/generalization-log.md +55 -0
  116. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.json +328 -0
  117. package/vendor/BadCase/rn/pr-info/rntpc_react-native-SmartRefreshLayout-32.md +382 -0
  118. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.json +1351 -0
  119. package/vendor/BadCase/rn/pr-info/rntpc_react-native-amap-geolocation-27.md +1176 -0
  120. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.json +538 -0
  121. package/vendor/BadCase/rn/pr-info/rntpc_react-native-audio-recorder-player-22.md +1488 -0
  122. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.json +211 -0
  123. package/vendor/BadCase/rn/pr-info/rntpc_react-native-blob-util-23.md +35 -0
  124. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.json +2048 -0
  125. package/vendor/BadCase/rn/pr-info/rntpc_react-native-code-push-17.md +3866 -0
  126. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.json +198 -0
  127. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fast-image-102.md +40 -0
  128. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.json +751 -0
  129. package/vendor/BadCase/rn/pr-info/rntpc_react-native-fs-38.md +883 -0
  130. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.json +1544 -0
  131. package/vendor/BadCase/rn/pr-info/rntpc_react-native-geolocation-24.md +2598 -0
  132. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.json +246 -0
  133. package/vendor/BadCase/rn/pr-info/rntpc_react-native-modal-20.md +554 -0
  134. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.json +211 -0
  135. package/vendor/BadCase/rn/pr-info/rntpc_react-native-netinfo-31.md +54 -0
  136. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.json +1626 -0
  137. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-45.md +2213 -0
  138. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.json +208 -0
  139. package/vendor/BadCase/rn/pr-info/rntpc_react-native-orientation-locker-51.md +77 -0
  140. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.json +377 -0
  141. package/vendor/BadCase/rn/pr-info/rntpc_react-native-pager-view-32.md +174 -0
  142. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.json +232 -0
  143. package/vendor/BadCase/rn/pr-info/rntpc_react-native-qr-decode-image-camera-37.md +194 -0
  144. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.json +198 -0
  145. package/vendor/BadCase/rn/pr-info/rntpc_react-native-reanimated-82.md +67 -0
  146. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.json +198 -0
  147. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-87.md +38 -0
  148. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.json +208 -0
  149. package/vendor/BadCase/rn/pr-info/rntpc_react-native-screens-93.md +34 -0
  150. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.json +222 -0
  151. package/vendor/BadCase/rn/pr-info/rntpc_react-native-spring-scrollview-38.md +56 -0
  152. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.json +463 -0
  153. package/vendor/BadCase/rn/pr-info/rntpc_react-native-svg-117.md +383 -0
  154. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.json +464 -0
  155. package/vendor/BadCase/rn/pr-info/rntpc_react-native-video-66.md +565 -0
  156. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.json +198 -0
  157. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-112.md +47 -0
  158. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.json +232 -0
  159. package/vendor/BadCase/rn/pr-info/rntpc_react-native-webview-121.md +422 -0
  160. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.json +295 -0
  161. package/vendor/BadCase/rn/pr-info/rntpc_react-native-wechat-lib-15.md +1038 -0
  162. package/vendor/BadCase/rn/rules/R-01-/345/205/250/345/261/217/347/252/227/345/217/243/347/212/266/346/200/201/347/256/241/347/220/206/345/256/214/346/225/264/346/200/247.md +20 -0
  163. package/vendor/BadCase/rn/rules/R-02-/350/265/204/346/272/220URI/345/275/242/346/200/201/345/210/206/346/224/257/350/246/206/347/233/226.md +21 -0
  164. package/vendor/BadCase/rn/rules/R-03-/351/231/204/345/261/236/346/223/215/344/275/234/345/244/261/350/264/245/351/230/273/346/226/255/344/270/273/346/265/201/347/250/213.md +20 -0
  165. package/vendor/BadCase/rn/rules/R-04-/345/274/202/346/255/245/345/233/236/350/260/203/346/263/250/345/206/214/346/227/266/345/272/217.md +20 -0
  166. package/vendor/BadCase/rn/rules/R-05-/346/250/241/345/235/227/345/257/274/345/205/245/350/267/257/345/276/204/350/247/204/350/214/203.md +19 -0
  167. package/vendor/BadCase/rn/rules/R-06-/344/274/240/346/204/237/345/231/250/347/241/254/344/273/266/346/235/203/351/231/220/346/214/211/351/234/200/350/216/267/345/217/226.md +21 -0
  168. package/vendor/BadCase/rn/rules/R-07-/346/231/272/350/203/275/346/214/207/351/222/210/347/224/237/345/221/275/345/221/250/346/234/237/347/256/241/347/220/206.md +20 -0
  169. package/vendor/BadCase/rn/rules/R-08-/346/211/213/345/212/277/344/272/213/344/273/266/346/263/250/345/206/214/344/270/216/345/206/262/347/252/201/345/244/204/347/220/206.md +22 -0
  170. package/vendor/BadCase/rn/rules/R-09-/346/250/252/347/253/226/345/261/217/345/244/232/345/275/242/346/200/201/345/270/203/345/261/200/351/200/202/351/205/215.md +20 -0
  171. package/vendor/BadCase/rn/rules/R-10-/346/267/261/350/211/262/346/250/241/345/274/217/344/270/273/351/242/230/351/242/234/350/211/262/351/200/202/351/205/215.md +19 -0
  172. package/vendor/BadCase/rn/rules/R-11-/346/216/245/345/217/243/345/233/236/350/260/203/345/256/236/347/216/260/345/256/214/346/225/264/346/200/247.md +20 -0
  173. package/vendor/BadCase/rn/rules/R-12-/345/216/237/347/224/237/350/277/220/350/241/214/346/227/266/350/265/204/346/272/220/346/236/220/346/236/204/346/227/266/345/272/217.md +20 -0
  174. package/vendor/BadCase/rn/rules/R-13-/346/226/207/344/273/266/346/225/260/346/215/256/347/274/226/347/240/201/344/270/216/346/240/274/345/274/217/345/205/274/345/256/271.md +21 -0
  175. package/vendor/BadCase/rn/rules/R-14-/345/212/250/347/224/273/345/210/235/345/247/213/345/214/226/346/227/266/345/272/217/344/270/216/347/212/266/346/200/201/345/220/214/346/255/245.md +20 -0
  176. package/vendor/BadCase/rn/rules/R-15-/347/212/266/346/200/201/346/214/201/344/271/205/345/214/226/344/270/216/351/207/215/345/220/257/351/200/273/350/276/221/344/270/200/350/207/264/346/200/247.md +20 -0
  177. package/vendor/BadCase/rn/rules/R-16-/346/225/217/346/204/237/344/277/241/346/201/257/346/227/245/345/277/227/346/263/204/351/234/262.md +19 -0
  178. package/vendor/BadCase/rn/rules/R-17-/345/252/222/344/275/223/350/265/204/346/272/220/347/224/237/345/221/275/345/221/250/346/234/237/344/270/216/351/224/231/350/257/257/345/244/204/347/220/206.md +21 -0
  179. package/vendor/BadCase/rn/rules/R-18-/350/203/275/345/212/233/346/236/232/344/270/276/351/205/215/347/275/256/345/256/214/346/225/264/346/200/247.md +19 -0
  180. package/vendor/BadCase/rn/rules/R-19-/345/233/276/345/275/242/346/270/262/346/237/223/345/261/236/346/200/247/347/273/247/346/211/277/344/270/216/351/207/215/347/275/256.md +19 -0
  181. package/vendor/MCP/README.md +490 -0
  182. package/vendor/MCP/config.json +29 -0
  183. package/vendor/MCP/package.json +23 -0
  184. package/vendor/MCP/scripts/analyze-demo-coverage.cjs +1203 -0
  185. package/vendor/MCP/scripts/analyze-xts-coverage.cjs +1245 -0
  186. package/vendor/MCP/scripts/deveco_docs.js +247 -0
  187. package/vendor/MCP/scripts/extract-interfaces.cjs +1487 -0
  188. package/vendor/MCP/scripts/flutter-analyze-demo-coverage.cjs +666 -0
  189. package/vendor/MCP/scripts/flutter-analyze-xts-coverage.cjs +684 -0
  190. package/vendor/MCP/scripts/flutter-extract-interfaces.cjs +1251 -0
  191. package/vendor/MCP/scripts/flutter-render-spec-doc.cjs +351 -0
  192. package/vendor/MCP/scripts/render-spec-doc.cjs +347 -0
  193. package/vendor/MCP/scripts/rn-analyze-demo-coverage.cjs +645 -0
  194. package/vendor/MCP/scripts/rn-analyze-test-coverage.cjs +614 -0
  195. package/vendor/MCP/scripts/rn-extract-interfaces.cjs +1462 -0
  196. package/vendor/MCP/scripts/rn-render-spec-doc.cjs +327 -0
  197. package/vendor/MCP/scripts/rn-scan-exports.cjs +1336 -0
  198. package/vendor/MCP/src/client/MCPClientPool.js +220 -0
  199. package/vendor/MCP/src/config/ConfigLoader.js +189 -0
  200. package/vendor/MCP/src/executor/ScriptExecutor.js +110 -0
  201. package/vendor/MCP/src/gateway/Gateway.js +451 -0
  202. package/vendor/MCP/src/index.js +87 -0
  203. package/vendor/MCP/src/library/KBToolProvider.js +229 -0
  204. package/vendor/MCP/src/library/logger.js +60 -0
  205. package/vendor/MCP/src/registry/ScriptRegistry.js +238 -0
  206. package/vendor/MCP/src/registry/ToolRegistry.js +111 -0
  207. package/vendor/MCP/src/types.js +115 -0
  208. package/vendor/Server/Skills/arkts-library-test-coverage-check/references/MCP_TOOL_USAGE.md +4 -2
  209. package/vendor/Server/Skills/arkts-library-xts-coverage/SKILL.md +3 -3
  210. package/vendor/Server/Skills/knowledge-import/SKILL.md +1 -1
  211. package/vendor/Server/Skills/knowledge-import/references/MCP_TOOL_USAGE.md +12 -4
  212. package/vendor/Server/Skills/ohos-lib-pr-push/SKILL.md +5 -4
  213. package/vendor/Server/Skills/rn-library-issue-generalizer/SKILL.md +2 -2
  214. package/vendor/Server/Skills/rn-library-known-issue-check/SKILL.md +2 -2
  215. package/vendor/Server/Skills/rnoh-lib-demo-coverage/SKILL.md +1 -2
  216. package/vendor/Server/Skills/rnoh-lib-test-align/references/port-test-demo-guide.md +1 -1
  217. package/vendor/Server/Skills/rnoh-lib-xts-coverage/SKILL.md +1 -2
  218. package/vendor/Server/package.json +5 -1
  219. package/vendor/VERSION +3 -3
@@ -0,0 +1,2213 @@
1
+ # PR #45: fix:加速度传感器需要在使用的时候再打开,而不是初始化的时候打开
2
+
3
+ - **状态**: merged
4
+ - **作者**: qq_434633291111
5
+ - **创建时间**: 2026-08-06T11:29:27+08:00
6
+ - **分支**: br_rnoh0.77 → br_rnoh0.77
7
+ - **变更文件数**: 58
8
+ - **⚠️ 注意**: diff 过大已截断,变更文件列表完整保留
9
+
10
+ ## PR 描述
11
+
12
+ fix:加速度传感器需要在使用的时候再打开,而不是初始化的时候打开
13
+
14
+ ## 变更文件
15
+
16
+ | 文件 | 变更 | +行 | -行 |
17
+ |------|------|-----|-----|
18
+ | `.npmignore` | ? | +3 | -0 |
19
+ | `example/.eslintrc` | added | +19 | -0 |
20
+ | `example/.gitignore` | added | +71 | -0 |
21
+ | `example/.node-version` | added | +6 | -0 |
22
+ | `example/.prettierrc.js` | added | +7 | -0 |
23
+ | `example/.watchmanconfig` | added | +6 | -0 |
24
+ | `example/app.json` | added | +4 | -0 |
25
+ | `example/babel.config.js` | added | +6 | -0 |
26
+ | `example/contexts.ts` | added | +9 | -0 |
27
+ | `example/harmony/.gitignore` | added | +22 | -0 |
28
+ | `example/harmony/AppScope/app.json5` | added | +10 | -0 |
29
+ | `example/harmony/AppScope/resources/base/element/string.json` | added | +8 | -0 |
30
+ | `example/harmony/AppScope/resources/base/media/app_icon.png` | added | +0 | -0 |
31
+ | `example/harmony/build-profile.template.json5` | added | +40 | -0 |
32
+ | `example/harmony/codelinter.json` | added | +32 | -0 |
33
+ | `example/harmony/entry/.gitignore` | added | +7 | -0 |
34
+ | `example/harmony/entry/build-profile.json5` | added | +21 | -0 |
35
+ | `example/harmony/entry/hvigorfile.ts` | added | +8 | -0 |
36
+ | `example/harmony/entry/oh-package.json5` | added | +11 | -0 |
37
+ | `example/harmony/entry/src/main/cpp/.gitignore` | added | +2 | -0 |
38
+ | `example/harmony/entry/src/main/cpp/CMakeLists.txt` | added | +38 | -0 |
39
+ | `example/harmony/entry/src/main/cpp/PackageProvider.cpp` | added | +15 | -0 |
40
+ | `example/harmony/entry/src/main/ets/RNPackagesFactory.ets` | added | +15 | -0 |
41
+ | `example/harmony/entry/src/main/ets/assets/fonts/Pacifico-Regular.ttf` | added | +0 | -0 |
42
+ | `example/harmony/entry/src/main/ets/assets/fonts/StintUltraCondensed-Regular.ttf` | added | +0 | -0 |
43
+ | `example/harmony/entry/src/main/ets/entryability/EntryAbility.ets` | added | +27 | -0 |
44
+ | `example/harmony/entry/src/main/ets/pages/Index.ets` | added | +113 | -0 |
45
+ | `example/harmony/entry/src/main/ets/pages/SurfaceDeadlockTest.ets` | added | +135 | -0 |
46
+ | `example/harmony/entry/src/main/ets/pages/TouchDisplayer.ets` | added | +44 | -0 |
47
+ | `example/harmony/entry/src/main/module.json5` | added | +52 | -0 |
48
+ | `example/harmony/entry/src/main/resources/.gitignore` | added | +1 | -0 |
49
+ | `example/harmony/entry/src/main/resources/base/element/color.json` | added | +8 | -0 |
50
+ | `example/harmony/entry/src/main/resources/base/element/string.json` | added | +16 | -0 |
51
+ | `example/harmony/entry/src/main/resources/base/media/icon.png` | added | +0 | -0 |
52
+ | `example/harmony/entry/src/main/resources/base/profile/main_pages.json` | added | +5 | -0 |
53
+ | `example/harmony/entry/src/main/resources/rawfile/1.txt` | added | +1 | -0 |
54
+ | `example/harmony/format.ps1` | added | +18 | -0 |
55
+ | `example/harmony/hvigor/.gitignore` | added | +1 | -0 |
56
+ | `example/harmony/hvigor/hvigor-config.json5` | added | +21 | -0 |
57
+ | `example/harmony/hvigorfile.ts` | added | +6 | -0 |
58
+ | `example/harmony/oh-package.json5` | added | +13 | -0 |
59
+ | `example/index.js` | added | +11 | -0 |
60
+ | `example/jest.config.js` | added | +11 | -0 |
61
+ | `example/metro.config.js` | added | +30 | -0 |
62
+ | `example/package.json` | added | +61 | -0 |
63
+ | `example/react-native.config.js` | added | +11 | -0 |
64
+ | `example/rnoh-testerino-0.0.12.tgz` | added | +0 | -0 |
65
+ | `example/scripts/create-build-profile.js` | added | +46 | -0 |
66
+ | `example/src/OrientationLockerExample.tsx` | added | +510 | -0 |
67
+ | `example/tsconfig.json` | added | +14 | -0 |
68
+ | `harmony/orientation_locker/src/main/cpp/generated/RNOH/generated/turbo_modules/OreitationLockerNativeModule.cpp` | ? | +2 | -0 |
69
+ | `harmony/orientation_locker/src/main/ets/Logger.ts` | added | +64 | -0 |
70
+ | `harmony/orientation_locker/src/main/ets/RNOrientationLockerTurboModule.ts` | ? | +75 | -48 |
71
+ | `harmony/orientation_locker/src/main/ets/generated/turboModules/OreitationLockerNativeModule.ts` | ? | +4 | -0 |
72
+ | `package.json` | ? | +3 | -3 |
73
+ | `src/OreitationLockerNativeModule.ts` | ? | +5 | -0 |
74
+ | `src/orientation.harmony.js` | ? | +2 | -0 |
75
+ | `src/orientation.js` | ? | +4 | -0 |
76
+
77
+ ## Diff
78
+
79
+ ### `.npmignore`
80
+
81
+ ```diff
82
+ @@ -49,3 +49,6 @@ yarn-error.log
83
+ .git
84
+ .watchmanconfig
85
+ .history
86
+ +*.tgz
87
+ +example
88
+ +build
89
+
90
+ ```
91
+
92
+ ### `example/.eslintrc`
93
+
94
+ ```diff
95
+ @@ -0,0 +1,19 @@
96
+ +/*
97
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
98
+ + * Use of this source code is governed by a MIT license that can be
99
+ + * found in the LICENSE file.
100
+ + */
101
+ +
102
+ +{
103
+ + "extends": "@react-native",
104
+ + "rules": {
105
+ + "react-native/no-inline-styles": "off",
106
+ + "react/no-unstable-nested-components": "off",
107
+ + "react/react-in-jsx-scope": "off",
108
+ + "@typescript-eslint/no-unused-vars": "warn",
109
+ + "react-hooks/exhaustive-deps": "off",
110
+ + "radix": "off",
111
+ + "prettier/prettier": "warn",
112
+ + "max-lines": "off"
113
+ + }
114
+ +}
115
+
116
+
117
+ ```
118
+
119
+ ### `example/.gitignore`
120
+
121
+ ```diff
122
+ @@ -0,0 +1,71 @@
123
+ +.husky
124
+ +bundle.harmony.js
125
+ +package-lock.json
126
+ +*.hbc
127
+ +lintCppResult.txt
128
+ +
129
+ +# ---
130
+ +
131
+ +# OSX
132
+ +#
133
+ +.DS_Store
134
+ +
135
+ +# Xcode
136
+ +#
137
+ +build/
138
+ +*.pbxuser
139
+ +!default.pbxuser
140
+ +*.mode1v3
141
+ +!default.mode1v3
142
+ +*.mode2v3
143
+ +!default.mode2v3
144
+ +*.perspectivev3
145
+ +!default.perspectivev3
146
+ +xcuserdata
147
+ +*.xccheckout
148
+ +*.moved-aside
149
+ +DerivedData
150
+ +*.hmap
151
+ +*.ipa
152
+ +*.xcuserstate
153
+ +
154
+ +# Android/IntelliJ
155
+ +#
156
+ +build/
157
+ +.idea
158
+ +.gradle
159
+ +local.properties
160
+ +*.iml
161
+ +*.hprof
162
+ +.cxx/
163
+ +*.keystore
164
+ +!debug.keystore
165
+ +BuildProfile.ets
166
+ +
167
+ +# node.js
168
+ +#
169
+ +node_modules/
170
+ +npm-debug.log
171
+ +yarn-error.log
172
+ +
173
+ +# fastlane
174
+ +#
175
+ +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
176
+ +# screenshots whenever they are needed.
177
+ +# For more information about the recommended setup visit:
178
+ +# https://docs.fastlane.tools/best-practices/source-control/
179
+ +
180
+ +**/fastlane/report.xml
181
+ +**/fastlane/Preview.html
182
+ +**/fastlane/screenshots
183
+ +**/fastlane/test_output
184
+ +
185
+ +# Bundle artifact
186
+ +*.jsbundle
187
+ +
188
+ +# Ruby / CocoaPods
189
+ +/vendor/bundle/
190
+ +
191
+ +# Temporary files created by Metro to check the health of the file watcher
192
+ +.metro-health-check*
193
+ +*.htrace
194
+
195
+
196
+ ```
197
+
198
+ ### `example/.node-version`
199
+
200
+ ```diff
201
+ @@ -0,0 +1,6 @@
202
+ +/*
203
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
204
+ + * Use of this source code is governed by a MIT license that can be
205
+ + * found in the LICENSE file.
206
+ + */
207
+ +18
208
+
209
+ ```
210
+
211
+ ### `example/.prettierrc.js`
212
+
213
+ ```diff
214
+ @@ -0,0 +1,7 @@
215
+ +module.exports = {
216
+ + arrowParens: 'avoid',
217
+ + bracketSameLine: true,
218
+ + bracketSpacing: true,
219
+ + singleQuote: true,
220
+ + trailingComma: 'all',
221
+ +};
222
+
223
+ ```
224
+
225
+ ### `example/.watchmanconfig`
226
+
227
+ ```diff
228
+ @@ -0,0 +1,6 @@
229
+ +/*
230
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
231
+ + * Use of this source code is governed by a MIT license that can be
232
+ + * found in the LICENSE file.
233
+ + */
234
+ +{}
235
+
236
+
237
+ ```
238
+
239
+ ### `example/app.json`
240
+
241
+ ```diff
242
+ @@ -0,0 +1,4 @@
243
+ +{
244
+ + "name": "app_name",
245
+ + "displayName": "tester"
246
+ +}
247
+
248
+
249
+ ```
250
+
251
+ ### `example/babel.config.js`
252
+
253
+ ```diff
254
+ @@ -0,0 +1,6 @@
255
+ +module.exports = {
256
+ + presets: ['module:@react-native/babel-preset'],
257
+ + plugins: [
258
+ + 'react-native-reanimated/plugin',
259
+ + ],
260
+ +};
261
+
262
+ ```
263
+
264
+ ### `example/contexts.ts`
265
+
266
+ ```diff
267
+ @@ -0,0 +1,9 @@
268
+ +/*
269
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
270
+ + * Use of this source code is governed by a MIT license that can be
271
+ + * found in the LICENSE file.
272
+ + */
273
+ +
274
+ +import React from 'react';
275
+ +
276
+ +export const AppParamsContext = React.createContext(undefined);
277
+
278
+ ```
279
+
280
+ ### `example/harmony/.gitignore`
281
+
282
+ ```diff
283
+ @@ -0,0 +1,22 @@
284
+ +# it may cause some issues when building the project when switching branches
285
+ +package-lock.json
286
+ +# we add this because we want to keep the signing configs out of git
287
+ +rnoh_modules
288
+ +
289
+ +**/oh-package-lock.json5
290
+ +# ---
291
+ +
292
+ +/node_modules
293
+ +/local.properties
294
+ +/.idea
295
+ +**/build
296
+ +/.hvigor
297
+ +.cxx
298
+ +/.clangd
299
+ +/.clang-format
300
+ +/.clang-tidy
301
+ +/oh_modules
302
+ +hvigorw
303
+ +hvigorw.bat
304
+ +hvigor/hvigor-wrapper.js
305
+ +
306
+
307
+ ```
308
+
309
+ ### `example/harmony/AppScope/app.json5`
310
+
311
+ ```diff
312
+ @@ -0,0 +1,10 @@
313
+ +{
314
+ + "app": {
315
+ + "bundleName": "com.harmony.wechat.lib.demo",
316
+ + "vendor": "example",
317
+ + "versionCode": 1000000,
318
+ + "versionName": "1.0.0",
319
+ + "icon": "$media:app_icon",
320
+ + "label": "$string:app_name"
321
+ + }
322
+ +}
323
+
324
+ ```
325
+
326
+ ### `example/harmony/AppScope/resources/base/element/string.json`
327
+
328
+ ```diff
329
+ @@ -0,0 +1,8 @@
330
+ +{
331
+ + "string": [
332
+ + {
333
+ + "name": "app_name",
334
+ + "value": "RN Tester"
335
+ + }
336
+ + ]
337
+ +}
338
+
339
+ ```
340
+
341
+ ### `example/harmony/AppScope/resources/base/media/app_icon.png`
342
+
343
+ ```diff
344
+ Binary files no preview for this file type
345
+ ```
346
+
347
+ ### `example/harmony/build-profile.template.json5`
348
+
349
+ ```diff
350
+ @@ -0,0 +1,40 @@
351
+ +{
352
+ + "app": {
353
+ + "signingConfigs": [
354
+ + ],
355
+ + "products": [
356
+ + {
357
+ + "name": "default",
358
+ + "signingConfig": "default",
359
+ + "compatibleSdkVersion": "5.0.0(12)",
360
+ + "runtimeOS": "HarmonyOS"
361
+ + }
362
+ + ],
363
+ + "buildModeSet": [
364
+ + {
365
+ + "name": "debug",
366
+ + },
367
+ + {
368
+ + "name": "release"
369
+ + }
370
+ + ]
371
+ + },
372
+ + "modules": [
373
+ + {
374
+ + "name": "entry",
375
+ + "srcPath": "./entry",
376
+ + "targets": [
377
+ + {
378
+ + "name": "default",
379
+ + "applyToProducts": [
380
+ + "default"
381
+ + ]
382
+ + }
383
+ + ]
384
+ + },
385
+ + {
386
+ + name: 'orientation_locker',
387
+ + srcPath: '../../harmony/orientation_locker',
388
+ + }
389
+ + ]
390
+ +}
391
+
392
+
393
+ ```
394
+
395
+ ### `example/harmony/codelinter.json`
396
+
397
+ ```diff
398
+ @@ -0,0 +1,32 @@
399
+ +{
400
+ + "files": ["**/*.ts", "**/*.ets"],
401
+ + "ignore": [
402
+ + "**/ohosTest/**/*",
403
+ + "**/node_modules/**/*",
404
+ + "**/hvigorfile.ts",
405
+ + "**/node_modules/**/*",
406
+ + "**/oh_modules/**/*",
407
+ + "**/build/**/*",
408
+ + "**/.preview/**/*"
409
+ + ],
410
+ + "plugins": ["@typescript-eslint"],
411
+ + "ruleSet": [],
412
+ + "rules": {
413
+ + "@typescript-eslint/await-thenable": "warn",
414
+ + "@typescript-eslint/consistent-type-imports": "warn",
415
+ + "@typescript-eslint/explicit-function-return-type": "warn",
416
+ + "@typescript-eslint/explicit-module-boundary-types": "warn",
417
+ + "@typescript-eslint/no-dynamic-delete": "warn",
418
+ + "@typescript-eslint/no-explicit-any": "warn",
419
+ + "@typescript-eslint/no-for-in-array": "warn",
420
+ + "@typescript-eslint/no-this-alias": "warn",
421
+ + "@typescript-eslint/no-unnecessary-type-constraint": "warn",
422
+ + "@typescript-eslint/no-unsafe-argument": "warn",
423
+ + "@typescript-eslint/no-unsafe-assignment": "warn",
424
+ + "@typescript-eslint/no-unsafe-call": "warn",
425
+ + "@typescript-eslint/no-unsafe-member-access": "warn",
426
+ + "@typescript-eslint/no-unsafe-return": "warn",
427
+ + "@typescript-eslint/prefer-literal-enum-member": "warn"
428
+ + },
429
+ + "overrides": []
430
+ +}
431
+
432
+ ```
433
+
434
+ ### `example/harmony/entry/.gitignore`
435
+
436
+ ```diff
437
+ @@ -0,0 +1,7 @@
438
+ +/node_modules
439
+ +/.preview
440
+ +/build
441
+ +/.cxx
442
+ +package-lock.json
443
+ +/oh_modules
444
+ +/assets
445
+
446
+ ```
447
+
448
+ ### `example/harmony/entry/build-profile.json5`
449
+
450
+ ```diff
451
+ @@ -0,0 +1,21 @@
452
+ +{
453
+ + "apiType": 'stageMode',
454
+ + "buildOption": {
455
+ + "externalNativeOptions": {
456
+ + "path": "./src/main/cpp/CMakeLists.txt",
457
+ + "arguments": "",
458
+ + "cppFlags": "-s",
459
+ + "abiFilters": [
460
+ + "arm64-v8a"
461
+ + ]
462
+ + }
463
+ + },
464
+ + "targets": [
465
+ + {
466
+ + "name": "default",
467
+ + },
468
+ + {
469
+ + "name": "ohosTest",
470
+ + }
471
+ + ]
472
+ +}
473
+
474
+
475
+ ```
476
+
477
+ ### `example/harmony/entry/hvigorfile.ts`
478
+
479
+ ```diff
480
+ @@ -0,0 +1,8 @@
481
+ +/*
482
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
483
+ + * Use of this source code is governed by a MIT license that can be
484
+ + * found in the LICENSE file.
485
+ + */
486
+ +
487
+ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
488
+ +export { hapTasks } from '@ohos/hvigor-ohos-plugin';
489
+
490
+ ```
491
+
492
+ ### `example/harmony/entry/oh-package.json5`
493
+
494
+ ```diff
495
+ @@ -0,0 +1,11 @@
496
+ +{
497
+ + "license": "ISC",
498
+ + "devDependencies": {},
499
+ + "name": "entry",
500
+ + "description": "example description",
501
+ + "version": "1.0.0",
502
+ + "dependencies": {
503
+ + "@rnoh/react-native-openharmony": "file:../../node_modules/react-native-harmony/harmony/react_native_openharmony.har",
504
+ + "@react-native-ohos/react-native-orientation-locker": "file:../../node_modules/@react-native-ohos/react-native-orientation-locker/harmony/orientation_locker.har"
505
+ + }
506
+ +}
507
+
508
+
509
+ ```
510
+
511
+ ### `example/harmony/entry/src/main/cpp/.gitignore`
512
+
513
+ ```diff
514
+ @@ -0,0 +1,2 @@
515
+ +jsbundle.h
516
+ +generated/
517
+
518
+ ```
519
+
520
+ ### `example/harmony/entry/src/main/cpp/CMakeLists.txt`
521
+
522
+ ```diff
523
+ @@ -0,0 +1,38 @@
524
+ +project(rnapp)
525
+ +cmake_minimum_required(VERSION 3.4.1)
526
+ +set(CMAKE_SKIP_BUILD_RPATH TRUE)
527
+ +set(RNOH_APP_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
528
+ +set(NODE_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../node_modules")
529
+ +set(OH_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
530
+ +set(RNOH_CPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules/@rnoh/react-native-openharmony/src/main/cpp")
531
+ +set(RNOH_GENERATED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/generated")
532
+ +set(LOG_VERBOSITY_LEVEL 1)
533
+ +set(CMAKE_ASM_FLAGS "-Wno-error=unused-command-line-argument -Qunused-arguments")
534
+ +set(CMAKE_CXX_FLAGS "-fstack-protector-strong -Wl,-z,relro,-z,now,-z,noexecstack -s -fPIE -pie")
535
+ +set(OH_MODULES "${CMAKE_CURRENT_SOURCE_DIR}/../../../oh_modules")
536
+ +
537
+ +set(WITH_HITRACE_SYSTRACE 1) # for other CMakeLists.txt files to use
538
+ +add_compile_definitions(WITH_HITRACE_SYSTRACE)
539
+ +
540
+ +# (VM) Define a variable and assign it to the current module's cpp directory
541
+ +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
542
+ +
543
+ +# Add the Header File directory, including cpp, cpp/include, and tell cmake to find the Header Files introduced by the code here
544
+ +include_directories(${NATIVERENDER_ROOT_PATH}
545
+ + ${NATIVERENDER_ROOT_PATH}/include)
546
+ +
547
+ +add_subdirectory("${RNOH_CPP_DIR}" ./rn)
548
+ +
549
+ +# RNOH_BEGIN: manual_package_linking_1
550
+ +add_subdirectory("${OH_MODULES}/@react-native-ohos/react-native-orientation-locker/src/main/cpp" ./orientation-locker)
551
+ +# RNOH_END: manual_package_linking_1
552
+ +
553
+ +add_library(rnoh_app SHARED
554
+ + "./PackageProvider.cpp"
555
+ + "${RNOH_CPP_DIR}/RNOHAppNapiBridge.cpp"
556
+ +)
557
+ +target_link_libraries(rnoh_app PUBLIC rnoh)
558
+ +
559
+ +# RNOH_BEGIN: manual_package_linking_2
560
+ +target_link_libraries(rnoh_app PUBLIC rnoh_orientation_locker)
561
+ +# RNOH_END: manual_package_linking_2
562
+
563
+ ```
564
+
565
+ ### `example/harmony/entry/src/main/cpp/PackageProvider.cpp`
566
+
567
+ ```diff
568
+ @@ -0,0 +1,15 @@
569
+ +// Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
570
+ +// Use of this source code is governed by a MIT license that can be
571
+ +// found in the LICENSE file.
572
+ +
573
+ +#include "RNOH/PackageProvider.h"
574
+ +#include "OrientationLockerPackage.h"
575
+ +
576
+ +using namespace rnoh;
577
+ +
578
+ +std::vector<std::shared_ptr<Package>> PackageProvider::getPackages(Package::Context ctx)
579
+ +{
580
+ + return {
581
+ + std::make_shared<OrientationLockerPackage>(ctx),
582
+ + };
583
+ +}
584
+
585
+
586
+ ```
587
+
588
+ ### `example/harmony/entry/src/main/ets/RNPackagesFactory.ets`
589
+
590
+ ```diff
591
+ @@ -0,0 +1,15 @@
592
+ +/*
593
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
594
+ + * Use of this source code is governed by a MIT license that can be
595
+ + * found in the LICENSE file.
596
+ + */
597
+ +
598
+ +
599
+ +import type {RNPackageContext, RNPackage} from '@rnoh/react-native-openharmony/ts';
600
+ +import { RNOrientationLockerPackage } from "@react-native-ohos/react-native-orientation-locker/ts";
601
+ +
602
+ +export function createRNPackages(ctx: RNPackageContext): RNPackage[] {
603
+ + return [
604
+ + new RNOrientationLockerPackage(ctx)
605
+ + ];
606
+ +}
607
+
608
+ ```
609
+
610
+ ### `example/harmony/entry/src/main/ets/assets/fonts/Pacifico-Regular.ttf`
611
+
612
+ ```diff
613
+ Binary files no preview for this file type
614
+ ```
615
+
616
+ ### `example/harmony/entry/src/main/ets/assets/fonts/StintUltraCondensed-Regular.ttf`
617
+
618
+ ```diff
619
+ Binary files no preview for this file type
620
+ ```
621
+
622
+ ### `example/harmony/entry/src/main/ets/entryability/EntryAbility.ets`
623
+
624
+ ```diff
625
+ @@ -0,0 +1,27 @@
626
+ +/*
627
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
628
+ + * Use of this source code is governed by a MIT license that can be
629
+ + * found in the LICENSE file.
630
+ + */
631
+ +
632
+ +import {RNAbility} from '@rnoh/react-native-openharmony';
633
+ +import { AbilityConstant, Want } from '@kit.AbilityKit';
634
+ +
635
+ +export default class EntryAbility extends RNAbility {
636
+ +
637
+ + onCreate(want: Want) {
638
+ + super.onCreate(want)
639
+ + this.handleWeChatCallIfNeed(want)
640
+ + }
641
+ +
642
+ + getPagePath() {
643
+ + return 'pages/Index';
644
+ + }
645
+ +
646
+ + onNewWant(want: Want, _launchParam: AbilityConstant.LaunchParam): void {
647
+ + this.handleWeChatCallIfNeed(want)
648
+ + }
649
+ +
650
+ + private handleWeChatCallIfNeed(want: Want) {
651
+ + }
652
+ +}
653
+
654
+ ```
655
+
656
+ ### `example/harmony/entry/src/main/ets/pages/Index.ets`
657
+
658
+ ```diff
659
+ @@ -0,0 +1,113 @@
660
+ +/*
661
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
662
+ + * Use of this source code is governed by a MIT license that can be
663
+ + * found in the LICENSE file.
664
+ + */
665
+ +
666
+ +import {
667
+ + AnyJSBundleProvider,
668
+ + ComponentBuilderContext,
669
+ + FileJSBundleProvider,
670
+ + MetroJSBundleProvider,
671
+ + ResourceJSBundleProvider,
672
+ + RNApp,
673
+ + RNOHErrorDialog,
674
+ + RNOHLogger,
675
+ + TraceJSBundleProviderDecorator,
676
+ + RNOHCoreContext
677
+ +} from '@rnoh/react-native-openharmony';
678
+ +import font from '@ohos.font';
679
+ +import { createRNPackages } from '../RNPackagesFactory';
680
+ +import common from '@ohos.app.ability.common';
681
+ +import BuildProfile from 'BuildProfile';
682
+ +
683
+ +
684
+ +let context = getContext(this) as common.UIAbilityContext;
685
+ +@Builder
686
+ +export function buildCustomRNComponent(ctx: ComponentBuilderContext) {
687
+ + // There seems to be a problem with the placement of ArkTS components in mixed mode. Nested Stack temporarily avoided.
688
+ + Stack() {
689
+ + }
690
+ + .position({ x: 0, y: 0 })
691
+ +
692
+ +}
693
+ +
694
+ +const wrappedCustomRNComponentBuilder = wrapBuilder(buildCustomRNComponent)
695
+ +
696
+ +/**
697
+ + * If you want to use custom fonts, you need to register them here.
698
+ + * We should support react-native-asset to handle registering fonts automatically.
699
+ + */
700
+ +const fonts: font.FontOptions[] = [
701
+ + {
702
+ + familyName: 'Pacifico-Regular',
703
+ + familySrc: '/assets/fonts/Pacifico-Regular.ttf'
704
+ + },
705
+ + {
706
+ + familyName: 'StintUltraCondensed-Regular',
707
+ + familySrc: '/assets/fonts/StintUltraCondensed-Regular.ttf'
708
+ + }
709
+ +]
710
+ +
711
+ +@Entry
712
+ +@Component
713
+ +struct Index {
714
+ + @StorageLink('RNOHCoreContext') private rnohCoreContext: RNOHCoreContext | undefined = undefined
715
+ + @State shouldShow: boolean = false
716
+ + private logger!: RNOHLogger
717
+ + bundlePath: string = 'bundle.harmony.js'
718
+ + @State hasBundle: boolean = false
719
+ +
720
+ + aboutToAppear() {
721
+ + this.logger = this.rnohCoreContext!.logger.clone("Index")
722
+ + const stopTracing = this.logger.clone("aboutToAppear").startTracing()
723
+ + for (const customFont of fonts) {
724
+ + font.registerFont(customFont)
725
+ + }
726
+ + this.shouldShow = true
727
+ + stopTracing()
728
+ + }
729
+ +
730
+ + onBackPress(): boolean | undefined {
731
+ + // NOTE: this is required since `Ability`'s `onBackPressed` function always
732
+ + // terminates or puts the app in the background, but we want Ark to ignore it completely
733
+ + // when handled by RN
734
+ + this.rnohCoreContext!.dispatchBackPress()
735
+ + return true
736
+ + }
737
+ +
738
+ + build() {
739
+ + Column() {
740
+ + if (this.rnohCoreContext && this.shouldShow) {
741
+ + if (this.rnohCoreContext?.isDebugModeEnabled) {
742
+ + RNOHErrorDialog({ ctx: this.rnohCoreContext })
743
+ + }
744
+ + RNApp({
745
+ + rnInstanceConfig: {
746
+ + createRNPackages,
747
+ + enableNDKTextMeasuring: true,
748
+ + enableBackgroundExecutor: false,
749
+ + enableCAPIArchitecture: true,
750
+ + arkTsComponentNames: [],
751
+ + },
752
+ + initialProps: { "foo": "bar" } as Record<string, string>,
753
+ + appKey: "app_name",
754
+ + wrappedCustomRNComponentBuilder: wrappedCustomRNComponentBuilder,
755
+ + onSetUp: (rnInstance) => {
756
+ + rnInstance.enableFeatureFlag("ENABLE_RN_INSTANCE_CLEAN_UP")
757
+ + },
758
+ + jsBundleProvider: new TraceJSBundleProviderDecorator(
759
+ + new AnyJSBundleProvider([
760
+ + new MetroJSBundleProvider(),
761
+ + new FileJSBundleProvider(context.filesDir + '/Bundles/' + this.bundlePath),
762
+ + new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'hermes_bundle.hbc'),
763
+ + new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
764
+ + ]),
765
+ + this.rnohCoreContext.logger),
766
+ + })
767
+ + }
768
+ + }
769
+ + .height('100%')
770
+ + .width('100%')
771
+ + }
772
+ +}
773
+
774
+ ```
775
+
776
+ ### `example/harmony/entry/src/main/ets/pages/SurfaceDeadlockTest.ets`
777
+
778
+ ```diff
779
+ @@ -0,0 +1,135 @@
780
+ +/*
781
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
782
+ + * Use of this source code is governed by a MIT license that can be
783
+ + * found in the LICENSE file.
784
+ + */
785
+ +import { RNInstance, JSBundleProvider, RNAbility, RNSurface } from '@rnoh/react-native-openharmony'
786
+ +import { CustomComponentBuilder } from "@rnoh/react-native-openharmony/src/main/ets/RNOHCorePackage"
787
+ +import { SurfaceConfig2 } from '@rnoh/react-native-openharmony/src/main/ets/RNSurface'
788
+ +
789
+ +
790
+ +@Component
791
+ +export struct SurfaceDeadlockTest {
792
+ + public jsBundleProvider: JSBundleProvider | undefined = undefined
793
+ + public appKeys: string[] = []
794
+ + public numberOfIterations: number = 1
795
+ + @BuilderParam public buildCustomComponent!: CustomComponentBuilder
796
+ + // -------------------------------------------------------------------------------------------------------------------
797
+ + @StorageLink('RNAbility') private rnAbility: RNAbility = {} as RNAbility
798
+ + private rnInstance!: RNInstance
799
+ + @State private shouldShow: boolean = false
800
+ + private shouldDestroyRNInstance: boolean = false
801
+ + private cleanUpCallbacks: (() => void)[] = []
802
+ +
803
+ + aboutToAppear() {
804
+ + this.getOrCreateRNInstance().then(rnInstance => {
805
+ + this.rnInstance = rnInstance
806
+ + const jsBundleExecutionStatus = this.rnInstance.getBundleExecutionStatus(this.jsBundleProvider?.getURL())
807
+ + if (this.jsBundleProvider && jsBundleExecutionStatus === undefined) {
808
+ + this.rnInstance.runJSBundle(this.jsBundleProvider).then(() => {
809
+ + this.shouldShow = true
810
+ + })
811
+ + return;
812
+ + }
813
+ + }).catch((reason: string | Error) => {
814
+ + if (typeof reason === "string")
815
+ + this.rnAbility.getLogger().error(reason)
816
+ + else if (reason instanceof Error) {
817
+ + this.rnAbility.getLogger().error(reason.message)
818
+ + } else {
819
+ + this.rnAbility.getLogger().error("Fatal exception")
820
+ + }
821
+ + })
822
+ + }
823
+ +
824
+ + aboutToDisappear() {
825
+ + if (this.shouldDestroyRNInstance)
826
+ + this.rnAbility.destroyAndUnregisterRNInstance(this.rnInstance)
827
+ + this.cleanUpCallbacks.forEach(cleanUp => cleanUp())
828
+ + }
829
+ +
830
+ + private getOrCreateRNInstance(): Promise<RNInstance> {
831
+ + return this.rnAbility.createAndRegisterRNInstance({ createRNPackages: () => [] })
832
+ + }
833
+ +
834
+ + build() {
835
+ + Stack() {
836
+ + if (this.shouldShow) {
837
+ + ForEach(this.appKeys, (appKey: string, idx) => {
838
+ + Stack() {
839
+ + Blinker({
840
+ + minDelayInMs: 1000,
841
+ + maxDelayInMs: 2000,
842
+ + blinksCount: this.numberOfIterations,
843
+ + randomnessPrecisionInMs: 500
844
+ + }) {
845
+ + RNSurface({
846
+ + ctx: this.rnAbility.createRNOHContext({ rnInstance: this.rnInstance }),
847
+ + surfaceConfig: {
848
+ + initialProps: {},
849
+ + appKey: appKey,
850
+ + } as SurfaceConfig2,
851
+ + buildCustomComponent: this.buildCustomComponent,
852
+ + })
853
+ + }
854
+ + }.height(`${100 / this.appKeys.length}%`)
855
+ + .position({ x: 0, y: `${(idx / this.appKeys.length) * 100}%` })
856
+ + })
857
+ + }
858
+ + }.width("100%")
859
+ + .height("100%")
860
+ + }
861
+ +}
862
+ +
863
+ +
864
+ +@Component
865
+ +struct Blinker {
866
+ + public minDelayInMs: number = 0
867
+ + public maxDelayInMs: number = 1000
868
+ + public blinksCount: number = 0
869
+ + public randomnessPrecisionInMs: number = 250
870
+ + @BuilderParam public renderChildren: () => void
871
+ + private currentBlinksCount = 0
872
+ + @State private isVisible: boolean = false
873
+ + private timeout: number = 0
874
+ +
875
+ + aboutToAppear() {
876
+ + this.blink(this.minDelayInMs)
877
+ + }
878
+ +
879
+ + aboutToDisappear() {
880
+ + clearTimeout(this.timeout)
881
+ + }
882
+ +
883
+ + private blink(ms: number) {
884
+ + this.isVisible = !this.isVisible
885
+ + this.currentBlinksCount += 1
886
+ + if (this.currentBlinksCount >= this.blinksCount) {
887
+ + if (this.timeout) {
888
+ + clearTimeout(this.timeout)
889
+ + }
890
+ + this.isVisible = true
891
+ + return;
892
+ + }
893
+ + this.timeout = setTimeout(() => {
894
+ + this.blink(this.getNextDelay())
895
+ + }, ms)
896
+ + }
897
+ +
898
+ + private getNextDelay(): number {
899
+ + return ((Math.floor(Math.random() * (Number.MAX_VALUE / this.randomnessPrecisionInMs)) * this.randomnessPrecisionInMs) % this.maxDelayInMs) + this.minDelayInMs
900
+ + }
901
+ +
902
+ + build() {
903
+ + Stack() {
904
+ + if (this.isVisible) {
905
+ + this.renderChildren()
906
+ + }
907
+ + }
908
+ + }
909
+ +}
910
+ +
911
+ +
912
+ +
913
+ +
914
+ +
915
+
916
+ ```
917
+
918
+ ### `example/harmony/entry/src/main/ets/pages/TouchDisplayer.ets`
919
+
920
+ ```diff
921
+ @@ -0,0 +1,44 @@
922
+ +/*
923
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
924
+ + * Use of this source code is governed by a MIT license that can be
925
+ + * found in the LICENSE file.
926
+ + */
927
+ +
928
+ +
929
+ +@Component
930
+ +export struct TouchDisplayer {
931
+ + @State currentTouches: TouchObject[] = []
932
+ + @State touchIndicatorOpacity: number = 0
933
+ + @BuilderParam buildChildren: () => void
934
+ + build() {
935
+ + Stack() {
936
+ + this.buildChildren()
937
+ + ForEach(this.currentTouches, (activeTouch: TouchObject) => {
938
+ + Stack() {
939
+ + }
940
+ + .width(64)
941
+ + .height(64)
942
+ + .backgroundColor("blue")
943
+ + .borderWidth(2)
944
+ + .borderColor("white")
945
+ + .opacity(this.touchIndicatorOpacity)
946
+ + .position({ x: activeTouch.x - 32, y: activeTouch.y - 32 })
947
+ + .borderRadius(1000)
948
+ + .hitTestBehavior(HitTestMode.Transparent)
949
+ + })
950
+ + }
951
+ + .width("100%")
952
+ + .height("100%")
953
+ + .hitTestBehavior(HitTestMode.Transparent)
954
+ + .onTouch(e => {
955
+ + this.currentTouches = e.touches
956
+ + this.touchIndicatorOpacity = 0.5
957
+ + animateTo({
958
+ + duration: 500,
959
+ + curve: Curve.Linear,
960
+ + }, () => {
961
+ + this.touchIndicatorOpacity = 0
962
+ + })
963
+ + })
964
+ + }
965
+ +}
966
+
967
+
968
+ ```
969
+
970
+ ### `example/harmony/entry/src/main/module.json5`
971
+
972
+ ```diff
973
+ @@ -0,0 +1,52 @@
974
+ +{
975
+ + "module": {
976
+ + "name": "entry",
977
+ + "type": "entry",
978
+ + "description": "$string:module_desc",
979
+ + "mainElement": "EntryAbility",
980
+ + "deviceTypes": [
981
+ + "default"
982
+ + ],
983
+ + "querySchemes": [
984
+ + "weixin",
985
+ + ],
986
+ + "deliveryWithInstall": true,
987
+ + "installationFree": false,
988
+ + "pages": "$profile:main_pages",
989
+ + // below property is supported from 5.0.0 - it is needed by bundleManager.canOpenLink to check if the app can open some url
990
+ + // "querySchemes": ["maps", "http", "https", "customDomain"],
991
+ + "requestPermissions": [
992
+ + {
993
+ + "name": "ohos.permission.INTERNET"
994
+ + },
995
+ + ],
996
+ + "metadata": [
997
+ + {
998
+ + "name": "OPTLazyForEach",
999
+ + "value": "true",
1000
+ + }
1001
+ + ],
1002
+ + "abilities": [
1003
+ + {
1004
+ + "name": "EntryAbility",
1005
+ + "srcEntry": "./ets/entryability/EntryAbility.ets",
1006
+ + "description": "$string:EntryAbility_desc",
1007
+ + "icon": "$media:icon",
1008
+ + "label": "$string:EntryAbility_label",
1009
+ + "startWindowIcon": "$media:icon",
1010
+ + "startWindowBackground": "$color:start_window_background",
1011
+ + "visible": true,
1012
+ + "skills": [
1013
+ + {
1014
+ + "entities": [
1015
+ + "entity.system.home"
1016
+ + ],
1017
+ + "actions": [
1018
+ + "action.system.home"
1019
+ + ]
1020
+ + }
1021
+ + ]
1022
+ + }
1023
+ + ]
1024
+ + }
1025
+ +}
1026
+
1027
+
1028
+ ```
1029
+
1030
+ ### `example/harmony/entry/src/main/resources/.gitignore`
1031
+
1032
+ ```diff
1033
+ @@ -0,0 +1 @@
1034
+ +rawfile/assets
1035
+
1036
+ ```
1037
+
1038
+ ### `example/harmony/entry/src/main/resources/base/element/color.json`
1039
+
1040
+ ```diff
1041
+ @@ -0,0 +1,8 @@
1042
+ +{
1043
+ + "color": [
1044
+ + {
1045
+ + "name": "start_window_background",
1046
+ + "value": "#FFFFFF"
1047
+ + }
1048
+ + ]
1049
+ +}
1050
+
1051
+
1052
+ ```
1053
+
1054
+ ### `example/harmony/entry/src/main/resources/base/element/string.json`
1055
+
1056
+ ```diff
1057
+ @@ -0,0 +1,16 @@
1058
+ +{
1059
+ + "string": [
1060
+ + {
1061
+ + "name": "module_desc",
1062
+ + "value": "module description"
1063
+ + },
1064
+ + {
1065
+ + "name": "EntryAbility_desc",
1066
+ + "value": "description"
1067
+ + },
1068
+ + {
1069
+ + "name": "EntryAbility_label",
1070
+ + "value": "RN Tester"
1071
+ + }
1072
+ + ]
1073
+ +}
1074
+
1075
+
1076
+ ```
1077
+
1078
+ ### `example/harmony/entry/src/main/resources/base/media/icon.png`
1079
+
1080
+ ```diff
1081
+ Binary files no preview for this file type
1082
+ ```
1083
+
1084
+ ### `example/harmony/entry/src/main/resources/base/profile/main_pages.json`
1085
+
1086
+ ```diff
1087
+ @@ -0,0 +1,5 @@
1088
+ +{
1089
+ + "src": [
1090
+ + "pages/Index"
1091
+ + ]
1092
+ +}
1093
+
1094
+ ```
1095
+
1096
+ ### `example/harmony/entry/src/main/resources/rawfile/1.txt`
1097
+
1098
+ ```diff
1099
+ @@ -0,0 +1 @@
1100
+ +text test
1101
+
1102
+
1103
+ ```
1104
+
1105
+ ### `example/harmony/format.ps1`
1106
+
1107
+ ```diff
1108
+ @@ -0,0 +1,18 @@
1109
+ +/*
1110
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1111
+ + * Use of this source code is governed by a MIT license that can be
1112
+ + * found in the LICENSE file.
1113
+ + */
1114
+ +
1115
+ +$directoryPath = Split-Path -Parent $MyInvocation.MyCommand.Path
1116
+ +$filePaths = Get-ChildItem $directoryPath -Recurse -Include *.h, *.cpp |
1117
+ +Where-Object {
1118
+ + $_.DirectoryName -notmatch 'third-party' -and
1119
+ + $_.DirectoryName -notmatch 'patches' -and
1120
+ + $_.DirectoryName -notmatch 'node_modules' -and
1121
+ + $_.DirectoryName -notmatch '.cxx' -and
1122
+ + $_.DirectoryName -notmatch 'build'
1123
+ +}
1124
+ +foreach ($filePath in $filePaths) {
1125
+ + & "clang-format.exe" -style=file -i $filePath.FullName
1126
+ +}
1127
+
1128
+
1129
+ ```
1130
+
1131
+ ### `example/harmony/hvigor/.gitignore`
1132
+
1133
+ ```diff
1134
+ @@ -0,0 +1 @@
1135
+ +*.tgz
1136
+
1137
+ ```
1138
+
1139
+ ### `example/harmony/hvigor/hvigor-config.json5`
1140
+
1141
+ ```diff
1142
+ @@ -0,0 +1,21 @@
1143
+ +{
1144
+ + "modelVersion": "5.0.0",
1145
+ + "dependencies": {
1146
+ + },
1147
+ + "execution": {
1148
+ + // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */
1149
+ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */
1150
+ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */
1151
+ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */
1152
+ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */
1153
+ + },
1154
+ + "logging": {
1155
+ + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */
1156
+ + },
1157
+ + "debugging": {
1158
+ + "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */
1159
+ + },
1160
+ + "nodeOptions": {
1161
+ + // "maxOldSpaceSize": 4096 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */
1162
+ + }
1163
+ +}
1164
+
1165
+
1166
+ ```
1167
+
1168
+ ### `example/harmony/hvigorfile.ts`
1169
+
1170
+ ```diff
1171
+ @@ -0,0 +1,6 @@
1172
+ +import { appTasks } from '@ohos/hvigor-ohos-plugin';
1173
+ +
1174
+ +export default {
1175
+ + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
1176
+ + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
1177
+ +}
1178
+
1179
+
1180
+ ```
1181
+
1182
+ ### `example/harmony/oh-package.json5`
1183
+
1184
+ ```diff
1185
+ @@ -0,0 +1,13 @@
1186
+ +{
1187
+ + "modelVersion": "5.0.0",
1188
+ + "description": "Please describe the basic information.",
1189
+ + "dependencies": {
1190
+ + },
1191
+ + "devDependencies": {
1192
+ + "@ohos/hypium": "1.0.21",
1193
+ + "@ohos/hamock": "1.0.0"
1194
+ + },
1195
+ + "overrides": {
1196
+ + '@rnoh/react-native-openharmony': 'file:../node_modules/@react-native-oh/react-native-harmony/react_native_openharmony.har'
1197
+ + }
1198
+ +}
1199
+
1200
+ ```
1201
+
1202
+ ### `example/index.js`
1203
+
1204
+ ```diff
1205
+ @@ -0,0 +1,11 @@
1206
+ +/*
1207
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1208
+ + * Use of this source code is governed by a MIT license that can be
1209
+ + * found in the LICENSE file.
1210
+ + */
1211
+ +
1212
+ +import {AppRegistry, View, Text} from 'react-native';
1213
+ +import {name as appName} from './app.json';
1214
+ +import OrientationLockerExample from './src/OrientationLockerExample';
1215
+ +
1216
+ +AppRegistry.registerComponent(appName, () => OrientationLockerExample);
1217
+
1218
+
1219
+ ```
1220
+
1221
+ ### `example/jest.config.js`
1222
+
1223
+ ```diff
1224
+ @@ -0,0 +1,11 @@
1225
+ +/*
1226
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1227
+ + * Use of this source code is governed by a MIT license that can be
1228
+ + * found in the LICENSE file.
1229
+ + */
1230
+ +
1231
+ +/** @type {import('ts-jest').JestConfigWithTsJest} */
1232
+ +module.exports = {
1233
+ + preset: 'ts-jest',
1234
+ + testEnvironment: 'node',
1235
+ +};
1236
+
1237
+ ```
1238
+
1239
+ ### `example/metro.config.js`
1240
+
1241
+ ```diff
1242
+ @@ -0,0 +1,30 @@
1243
+ +/*
1244
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1245
+ + * Use of this source code is governed by a MIT license that can be
1246
+ + * found in the LICENSE file.
1247
+ + */
1248
+ +
1249
+ +const {mergeConfig, getDefaultConfig} = require('@react-native/metro-config');
1250
+ +const {createHarmonyMetroConfig} = require('@react-native-oh/react-native-harmony/metro.config');
1251
+ +
1252
+ +/**
1253
+ + * @type {import("metro-config").ConfigT}
1254
+ + */
1255
+ +const config = {
1256
+ + transformer: {
1257
+ + getTransformOptions: async () => ({
1258
+ + transform: {
1259
+ + experimentalImportSupport: false,
1260
+ + inlineRequires: true,
1261
+ + },
1262
+ + }),
1263
+ + },
1264
+ +};
1265
+ +
1266
+ +module.exports = mergeConfig(
1267
+ + getDefaultConfig(__dirname),
1268
+ + createHarmonyMetroConfig({
1269
+ + reactNativeHarmonyPackageName: '@react-native-oh/react-native-harmony',
1270
+ + }),
1271
+ + config,
1272
+ +);
1273
+
1274
+ ```
1275
+
1276
+ ### `example/package.json`
1277
+
1278
+ ```diff
1279
+ @@ -0,0 +1,61 @@
1280
+ +{
1281
+ + "name": "react-native-harmony-tester",
1282
+ + "version": "1.0.0",
1283
+ + "private": true,
1284
+ + "scripts": {
1285
+ + "i": "cd ../ && npm i && npm run pack && cd ./example && npm i",
1286
+ + "reStart": "npm run install:pkg && npm run codegen && hdc rport tcp:8081 tcp:8081 && react-native start",
1287
+ + "start": "hdc rport tcp:8081 tcp:8081 && react-native start",
1288
+ + "codegen": "react-native codegen-harmony --rnoh-module-path ./harmony/entry/oh_modules/@rnoh/react-native-openharmony",
1289
+ + "pack:pkg": "cd ../ && npm pack && cd ./example",
1290
+ + "install:pkg": "npm uninstall @react-native-ohos/react-native-orientation-locker && npm run pack:pkg && npm i @react-native-ohos/react-native-orientation-locker@file:../react-native-ohos-react-native-orientation-locker-1.8.1-beta.2.tgz",
1291
+ + "dev": "npm run codegen && react-native bundle-harmony --dev --minify=false",
1292
+ + "prod": "npm run codegen && react-native bundle-harmony --dev=false --minify=true",
1293
+ + "postinstall": "node ./scripts/create-build-profile",
1294
+ + "fast:pkg": "cd ../ && npm i --legacy-peer-deps && npm pack && cd ./example && npm i && npm run dev"
1295
+ + },
1296
+ + "dependencies": {
1297
+ + "@gorhom/portal": "^1.0.14",
1298
+ + "@react-native-ohos/react-native-orientation-locker": "file:../react-native-ohos-react-native-orientation-locker-1.8.1-beta.2.tgz",
1299
+ + "react": "18.3.1",
1300
+ + "react-native": "0.77.1",
1301
+ + "@react-native-oh/react-native-harmony": "npm:@react-native-oh/react-native-harmony@0.77.18-1",
1302
+ + "react-native-reanimated": "^3.6.0",
1303
+ + "@rnoh/testerino": "file:./rnoh-testerino-0.0.12.tgz"
1304
+ + },
1305
+ + "devDependencies": {
1306
+ + "@babel/core": "^7.25.2",
1307
+ + "@babel/preset-env": "^7.25.3",
1308
+ + "@babel/runtime": "^7.25.0",
1309
+ + "@react-native-community/cli": "15.0.1",
1310
+ + "@react-native-community/cli-platform-android": "15.0.1",
1311
+ + "@react-native-community/cli-platform-ios": "15.0.1",
1312
+ + "@react-native-community/eslint-config": "^3.2.0",
1313
+ + "@react-native/babel-preset": "0.77.1",
1314
+ + "@react-native/eslint-config": "0.77.1",
1315
+ + "@react-native/metro-config": "^0.77.1",
1316
+ + "@react-native/typescript-config": "0.77.1",
1317
+ + "@rnoh/react-native-harmony-cli": "npm:@react-native-oh/react-native-harmony-cli@0.77.18",
1318
+ + "@tsconfig/react-native": "^3.0.0",
1319
+ + "@types/d3-scale-chromatic": "^3.0.0",
1320
+ + "@types/jest": "^29.5.13",
1321
+ + "@types/react": "^18.2.6",
1322
+ + "@types/react-test-renderer": "^18.0.0",
1323
+ + "babel-jest": "^29.6.3",
1324
+ + "eslint": "^8.19.0",
1325
+ + "jest": "^29.7.0",
1326
+ + "metro-react-native-babel-preset": "0.76.8",
1327
+ + "prettier": "2.8.8",
1328
+ + "react-test-renderer": "18.3.1",
1329
+ + "typescript": "5.5.4"
1330
+ + },
1331
+ + "overrides": {
1332
+ + "@react-native-community/cli": "15.0.1"
1333
+ + },
1334
+ + "resolutions": {
1335
+ + "@react-native-community/cli": "15.0.1"
1336
+ + },
1337
+ + "engines": {
1338
+ + "node": ">=18"
1339
+ + }
1340
+ +}
1341
+
1342
+ ```
1343
+
1344
+ ### `example/react-native.config.js`
1345
+
1346
+ ```diff
1347
+ @@ -0,0 +1,11 @@
1348
+ +/*
1349
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1350
+ + * Use of this source code is governed by a MIT license that can be
1351
+ + * found in the LICENSE file.
1352
+ + */
1353
+ +
1354
+ +module.exports = {
1355
+ + project: {
1356
+ + },
1357
+ + assets: ['./assets/fonts/'],
1358
+ +};
1359
+
1360
+ ```
1361
+
1362
+ ### `example/rnoh-testerino-0.0.12.tgz`
1363
+
1364
+ ```diff
1365
+ Binary files no preview for this file type
1366
+ ```
1367
+
1368
+ ### `example/scripts/create-build-profile.js`
1369
+
1370
+ ```diff
1371
+ @@ -0,0 +1,46 @@
1372
+ +/*
1373
+ + * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
1374
+ + * Use of this source code is governed by a MIT license that can be
1375
+ + * found in the LICENSE file.
1376
+ + */
1377
+ +
1378
+ +
1379
+ +const fs = require('fs');
1380
+ +const JSON5 = require('json5');
1381
+ +const path = require('path');
1382
+ +
1383
+ +const templatePath = path.join(
1384
+ + __dirname,
1385
+ + '..',
1386
+ + 'harmony',
1387
+ + 'build-profile.template.json5',
1388
+ +);
1389
+ +const existingProfilePath = path.join(
1390
+ + __dirname,
1391
+ + '..',
1392
+ + 'harmony',
1393
+ + 'build-profile.json5',
1394
+ +);
1395
+ +
1396
+ +if (fs.existsSync(existingProfilePath)) {
1397
+ + let existingProfile = JSON5.parse(
1398
+ + fs.readFileSync(existingProfilePath, 'utf-8'),
1399
+ + );
1400
+ + let template = JSON5.parse(fs.readFileSync(templatePath, 'utf-8'));
1401
+ + let signingConfigs =
1402
+ + existingProfile.app && existingProfile.app.signingConfigs;
1403
+ +
1404
+ + existingProfile = {...template};
1405
+ +
1406
+ + if (signingConfigs) {
1407
+ + existingProfile.app.signingConfigs = signingConfigs;
1408
+ + }
1409
+ +
1410
+ + fs.writeFileSync(
1411
+ + existingProfilePath,
1412
+ + JSON5.stringify(existingProfile, null, 2),
1413
+ + );
1414
+ +} else {
1415
+ + // File doesn't exist, create a copy from the template
1416
+ + fs.copyFileSync(templatePath, existingProfilePath);
1417
+ +}
1418
+
1419
+
1420
+ ```
1421
+
1422
+ ### `example/src/OrientationLockerExample.tsx`
1423
+
1424
+ ```diff
1425
+ @@ -0,0 +1,510 @@
1426
+ +import Orientation, {
1427
+ + useOrientationChange,
1428
+ + useDeviceOrientationChange,
1429
+ + useLockListener,
1430
+ + OrientationLocker,
1431
+ + UNLOCK,
1432
+ + PORTRAIT,
1433
+ + LANDSCAPE,
1434
+ + LANDSCAPE_LEFT,
1435
+ + LANDSCAPE_RIGHT,
1436
+ + PORTRAIT_UPSIDE_DOWN,
1437
+ + ALL_ORIENTATIONS_BUT_UPSIDE_DOWN,
1438
+ +} from 'react-native-orientation-locker';
1439
+ +import {
1440
+ + StyleSheet,
1441
+ + Text,
1442
+ + TouchableOpacity,
1443
+ + ScrollView,
1444
+ + View,
1445
+ + Alert,
1446
+ +} from 'react-native';
1447
+ +import { useEffect, useState, useCallback } from 'react';
1448
+ +import { Tester, TestSuite, TestCase } from '@rnoh/testerino';
1449
+ +
1450
+ +export default function OrientationLockerExample() {
1451
+ + const [orientation, setOrientation] = useState<string>('PORTRAIT');
1452
+ + const updateOrientation = useCallback((orientation: string) => {
1453
+ + setOrientation(orientation);
1454
+ + }, []);
1455
+ + const [LockListener, setLockListener] = useState<string>();
1456
+ + const [isLockedState, setIsLockedState] = useState<string>();
1457
+ + const [initialOrientation, setInitialOrientation] = useState<string>();
1458
+ + const [autoRotateState, setAutoRotateState] = useState<string>();
1459
+ + const [hookOrientation, setHookOrientation] = useState<string>('PORTRAIT');
1460
+ + const [hookDeviceOrientation, setHookDeviceOrientation] = useState<string>('PORTRAIT');
1461
+ + const [hookLockState, setHookLockState] = useState<string>('未触发');
1462
+ + const [lockerOrientation, setLockerOrientation] = useState<string>();
1463
+ + const [lockerDeviceOrientation, setLockerDeviceOrientation] = useState<string>();
1464
+ + const [lockerSelectValue, setLockerSelectValue] = useState<string>(PORTRAIT);
1465
+ + const [configureState, setConfigureState] = useState<string>('disableFaceUpDown: false(已启用)');
1466
+ + const [deviceOrientation, setDeviceOrientation] = useState<string>();
1467
+ +
1468
+ + const updateDeviceOrientation = useCallback((orientation: string) => {
1469
+ + setDeviceOrientation(orientation);
1470
+ + }, []);
1471
+ +
1472
+ + useEffect(() => {
1473
+ + return () => {
1474
+ + // 退出应用销毁监听
1475
+ + unbindAllListeners();
1476
+ + };
1477
+ + }, []);
1478
+ +
1479
+ + const setLockToPortrait = () => {
1480
+ + Orientation.lockToPortrait();
1481
+ + };
1482
+ + const setLockToLandscape = () => {
1483
+ + Orientation.lockToLandscape();
1484
+ + };
1485
+ + const setLockToLandscapeLeft = () => {
1486
+ + Orientation.lockToLandscapeLeft();
1487
+ + };
1488
+ + const setLockToLandscapeRight = () => {
1489
+ + Orientation.lockToLandscapeRight();
1490
+ + };
1491
+ + const unlockAllOrientations = () => {
1492
+ + Orientation.unlockAllOrientations();
1493
+ + };
1494
+ + const lockToPortraitUpsideDown = () => {
1495
+ + Orientation.lockToPortraitUpsideDown();
1496
+ + };
1497
+ + const lockToAllOrientationsButUpsideDown = () => {
1498
+ + Orientation.lockToAllOrientationsButUpsideDown();
1499
+ + };
1500
+ + const bindDeviceOrientationListener = () => {
1501
+ + Orientation.removeDeviceOrientationListener(updateDeviceOrientation);
1502
+ + Orientation.addDeviceOrientationListener(updateDeviceOrientation);
1503
+ + };
1504
+ + const unbindDeviceOrientationListener = () => {
1505
+ + Orientation.removeDeviceOrientationListener(updateDeviceOrientation);
1506
+ + };
1507
+ + const bindOrientationListener = () => {
1508
+ + Orientation.removeOrientationListener(updateOrientation);
1509
+ + Orientation.addOrientationListener(updateOrientation);
1510
+ + };
1511
+ + const unbindOrientationListener = () => {
1512
+ + Orientation.removeOrientationListener(updateOrientation);
1513
+ + };
1514
+ +
1515
+ + const lockDidChange = useCallback((o: string) => {
1516
+ + setLockListener(`屏幕锁定状态变化: ${o}`);
1517
+ + }, []);
1518
+ + const bindLockListener = () => {
1519
+ + Orientation.removeLockListener(lockDidChange);
1520
+ + Orientation.addLockListener(lockDidChange);
1521
+ + };
1522
+ +
1523
+ + const unbindLockListener = () => {
1524
+ + Orientation.removeLockListener(lockDidChange);
1525
+ + };
1526
+ + const unbindAllListeners = () => {
1527
+ + Orientation.removeAllListeners();
1528
+ + };
1529
+ +
1530
+ + const checkIsLocked = () => {
1531
+ + const locked = Orientation.isLocked();
1532
+ + setIsLockedState(locked ? '已锁定' : '未锁定');
1533
+ + };
1534
+ +
1535
+ + const checkInitialOrientation = () => {
1536
+ + const initial = Orientation.getInitialOrientation();
1537
+ + setInitialOrientation(initial);
1538
+ + };
1539
+ +
1540
+ + const checkAutoRotateState = () => {
1541
+ + Orientation.getAutoRotateState((state: boolean) => {
1542
+ + setAutoRotateState(state ? '已开启' : '已关闭');
1543
+ + });
1544
+ + };
1545
+ +
1546
+ + const configureSetting = (value: boolean) => {
1547
+ + Orientation.configure({ disableFaceUpDown: value });
1548
+ + setConfigureState(value ? 'disableFaceUpDown: true(已禁用)' : 'disableFaceUpDown: false(已启用)');
1549
+ + };
1550
+ +
1551
+ + // Hooks
1552
+ + useOrientationChange((o: string) => {
1553
+ + setHookOrientation(o);
1554
+ + });
1555
+ +
1556
+ + useDeviceOrientationChange((o: string) => {
1557
+ + setHookDeviceOrientation(o);
1558
+ + });
1559
+ +
1560
+ + useLockListener((o: string) => {
1561
+ + setHookLockState(o);
1562
+ + Alert.alert('锁定方向变化', `当前锁定方向: ${o}`);
1563
+ + });
1564
+ +
1565
+ +
1566
+ +
1567
+ + return (
1568
+ + <Tester>
1569
+ + <ScrollView style={{ marginBottom: 50 }}>
1570
+ + <TestSuite name="orientation">
1571
+ + <TestCase tags={['C_API']} itShould='当前设备方向为:'>
1572
+ + <Text style={styles.textContent}>{deviceOrientation}</Text>
1573
+ + </TestCase>
1574
+ + <TestCase tags={['C_API']} itShould='当前屏幕方向为:'>
1575
+ + <Text style={styles.textContent}>{orientation}</Text>
1576
+ + </TestCase>
1577
+ + <TestCase tags={['C_API']} itShould='当前屏幕锁定状态为:'>
1578
+ + <Text style={styles.textContent}>{LockListener}</Text>
1579
+ + </TestCase>
1580
+ +
1581
+ + <TestCase tags={['C_API']} itShould='监听测试:添加设备方向监听 (DeviceOrientation)'>
1582
+ + <TouchableOpacity onPress={bindDeviceOrientationListener}>
1583
+ + <Text>点击绑定 DeviceOrientation 监听</Text>
1584
+ + </TouchableOpacity>
1585
+ + </TestCase>
1586
+ +
1587
+ + <TestCase tags={['C_API']} itShould='监听测试:移除设备方向监听 (DeviceOrientation)'>
1588
+ + <TouchableOpacity onPress={unbindDeviceOrientationListener}>
1589
+ + <Text>点击解绑 DeviceOrientation 监听</Text>
1590
+ + </TouchableOpacity>
1591
+ + </TestCase>
1592
+ +
1593
+ + <TestCase tags={['C_API']} itShould='监听测试:添加屏幕/UI方向监听 (Orientation)'>
1594
+ + <TouchableOpacity onPress={bindOrientationListener}>
1595
+ + <Text>点击绑定 Orientation 监听</Text>
1596
+ + </TouchableOpacity>
1597
+ + </TestCase>
1598
+ +
1599
+ + <TestCase tags={['C_API']} itShould='监听测试:移除屏幕/UI方向监听 (Orientation)'>
1600
+ + <TouchableOpacity onPress={unbindOrientationListener}>
1601
+ + <Text>点击解绑 Orientation 监听</Text>
1602
+ + </TouchableOpacity>
1603
+ + </TestCase>
1604
+ +
1605
+ + <TestCase tags={['C_API']} itShould='监听测试:添加锁定状态监听 (LockListener)'>
1606
+ + <TouchableOpacity onPress={bindLockListener}>
1607
+ + <Text>点击绑定 LockListener 监听</Text>
1608
+ + </TouchableOpacity>
1609
+ + </TestCase>
1610
+ +
1611
+ + <TestCase tags={['C_API']} itShould='监听测试:移除锁定状态监听 (LockListener)'>
1612
+ + <TouchableOpacity onPress={unbindLockListener}>
1613
+ + <Text>点击解绑 LockListener 监听</Text>
1614
+ + </TouchableOpacity>
1615
+ + </TestCase>
1616
+ +
1617
+ + <TestCase tags={['C_API']} itShould='销毁当前组件挂载的全部监听 (removeAllListeners)'>
1618
+ + <TouchableOpacity onPress={unbindAllListeners}>
1619
+ + <Text>点击全部解绑</Text>
1620
+ + </TouchableOpacity>
1621
+ + <Text style={styles.noteText}>注:退出应用时会自动调用,手动调用会解绑所有手动 bind 和 Hooks 挂载的底层监听</Text>
1622
+ + </TestCase>
1623
+ +
1624
+ + <TestCase
1625
+ + itShould="锁定当前屏幕上下翻转"
1626
+ + tags={['C_API']}
1627
+ + initialState={{}}
1628
+ + arrange={({ setState }) => {
1629
+ + return (
1630
+ + <TouchableOpacity
1631
+ + onPress={() => {
1632
+ + lockToPortraitUpsideDown();
1633
+ + const time = setTimeout(() => {
1634
+ + Orientation.getOrientation(
1635
+ + (orientation: string, err: string) => {
1636
+ + if (orientation) {
1637
+ + setState(orientation);
1638
+ + } else {
1639
+ + console.log("当前屏幕上下翻转");
1640
+ + }
1641
+ + },
1642
+ + );
1643
+ + }, 50);
1644
+ + }}
1645
+ + style={styles.button}>
1646
+ + <Text>锁定当前屏幕为上下翻转</Text>
1647
+ + </TouchableOpacity>
1648
+ + );
1649
+ + }}
1650
+ + assert={({ expect, state }) => {
1651
+ + expect(state).equal('PORTRAIT-UPSIDEDOWN');
1652
+ + }}></TestCase>
1653
+ +
1654
+ + <TestCase
1655
+ + itShould="锁定当前屏幕为竖屏"
1656
+ + tags={['C_API']}
1657
+ + initialState={{}}
1658
+ + arrange={({ setState }) => {
1659
+ + return (
1660
+ + <TouchableOpacity
1661
+ + onPress={() => {
1662
+ + setLockToPortrait();
1663
+ + const time = setTimeout(() => {
1664
+ + Orientation.getOrientation(
1665
+ + (orientation: string, err: string) => {
1666
+ + if (orientation) {
1667
+ + setState(orientation);
1668
+ + } else {
1669
+ + console.log("当前屏幕为竖屏");
1670
+ + }
1671
+ + },
1672
+ + );
1673
+ + }, 50);
1674
+ + }}
1675
+ + style={styles.button}>
1676
+ + <Text>锁定当前屏幕为竖屏</Text>
1677
+ + </TouchableOpacity>
1678
+ + );
1679
+ + }}
1680
+ + assert={({ expect, state }) => {
1681
+ + expect(state).equal('PORTRAIT');
1682
+ + }}></TestCase>
1683
+ +
1684
+ + <TestCase
1685
+ + itShould="锁定当前屏幕为横屏"
1686
+ + tags={['C_API']}
1687
+ + initialState={{}}
1688
+ + arrange={({ setState }) => {
1689
+ + return (
1690
+ + <TouchableOpacity
1691
+ + onPress={() => {
1692
+ + setLockToLandscape();
1693
+ + const time = setTimeout(() => {
1694
+ + Orientation.getOrientation(
1695
+ + (orientation: string, err: string) => {
1696
+ + if (orientation) {
1697
+ + setState(orientation);
1698
+ + } else {
1699
+ + console.log("当前屏幕为横屏");
1700
+ + }
1701
+ + },
1702
+ + );
1703
+ + }, 50);
1704
+ + }}
1705
+ + style={styles.button}>
1706
+ + <Text>锁定当前屏幕为横屏</Text>
1707
+ + </TouchableOpacity>
1708
+ + );
1709
+ + }}
1710
+ + assert={({ expect, state }) => {
1711
+ + expect(state).equal('LANDSCAPE-LEFT'); // lockToLandscape() 默认锁定到左横屏
1712
+ + }}></TestCase>
1713
+ + <TestCase
1714
+ + itShould="锁定当前屏幕为横屏,右旋转"
1715
+ + tags={['C_API']}
1716
+ + initialState={{}}
1717
+ + arrange={({ setState }) => {
1718
+ + return (
1719
+ + <TouchableOpacity
1720
+ + onPress={() => {
1721
+ + setLockToLandscapeLeft();
1722
+ + const time = setTimeout(() => {
1723
+ + Orientation.getOrientation(
1724
+ + (orientation: string, err: string) => {
1725
+ + if (orientation) {
1726
+ + setState(orientation);
1727
+ + } else {
1728
+ + console.log("当前屏幕横屏,右旋转");
1729
+ + }
1730
+ + },
1731
+ + );
1732
+ + }, 50);
1733
+ + }}
1734
+ + style={styles.button}>
1735
+ + <Text>锁定当前屏幕为横屏,右旋转</Text>
1736
+ + </TouchableOpacity>
1737
+ + );
1738
+ + }}
1739
+ + assert={({ expect, state }) => {
1740
+ + expect(state).equal('LANDSCAPE-LEFT');
1741
+ + }}></TestCase>
1742
+ + <TestCase
1743
+ + itShould="锁定当前屏幕为横屏,左旋转"
1744
+ + tags={['C_API']}
1745
+ + initialState={{}}
1746
+ + arrange={({ setState }) => {
1747
+ + return (
1748
+ + <TouchableOpacity
1749
+ + onPress={() => {
1750
+ + setLockToLandscapeRight();
1751
+ + const time = setTimeout(() => {
1752
+ + Orientation.getOrientation(
1753
+ + (orientation: string, err: string) => {
1754
+ + if (orientation) {
1755
+ + setState(orientation);
1756
+ + } else {
1757
+ + console.log("当前屏幕横屏,左旋转");
1758
+ + }
1759
+ + },
1760
+ + );
1761
+ + }, 50);
1762
+ + }}
1763
+ + style={styles.button}>
1764
+ + <Text>锁定当前屏幕为横屏,左旋转</Text>
1765
+ + </TouchableOpacity>
1766
+ + );
1767
+ + }}
1768
+ + assert={({ expect, state }) => {
1769
+ + expect(state).equal('LANDSCAPE-RIGHT');
1770
+ + }}></TestCase>
1771
+ + <TestCase
1772
+ + itShould="锁定除了上下翻转的所有方向"
1773
+ + tags={['C_API']}
1774
+ + initialState={{}}
1775
+ + arrange={({ setState }) => {
1776
+ + return (
1777
+ + <TouchableOpacity
1778
+ + onPress={() => {
1779
+ + lockToAllOrientationsButUpsideDown();
1780
+ + const time = setTimeout(() => {
1781
+ + Orientation.getOrientation(
1782
+ + (orientation: string, err: string) => {
1783
+ + if (orientation) {
1784
+ + setState(orientation);
1785
+ + } else {
1786
+ + console.log("锁定除了上下翻转的所有方向");
1787
+ + }
1788
+ + },
1789
+ + );
1790
+ + }, 50);
1791
+ + }}
1792
+ + style={styles.button}>
1793
+ + <Text>锁定除了上下翻转的所有方向</Text>
1794
+ + </TouchableOpacity>
1795
+ + );
1796
+ + }}
1797
+ + assert={({ expect, state }) => {
1798
+ + expect(state).to.be.a('string');
1799
+ + }}></TestCase>
1800
+ +
1801
+ + <TestCase tags={['C_API']} itShould='解锁当前屏幕旋转锁定'>
1802
+ + <TouchableOpacity onPress={() => {
1803
+ + unlockAllOrientations();
1804
+ + }}>
1805
+ + <Text>解锁当前屏幕旋转锁定</Text>
1806
+ + </TouchableOpacity>
1807
+ + </TestCase>
1808
+ +
1809
+ + <TestCase tags={['C_API']} itShould='isLocked:查看当前是否处于锁定状态'>
1810
+ + <TouchableOpacity onPress={() => {
1811
+ + checkIsLocked();
1812
+ + }}>
1813
+ + <Text>点击此处查看锁定状态</Text>
1814
+ + </TouchableOpacity>
1815
+ + <Text style={styles.textContent}>{isLockedState}</Text>
1816
+ + </TestCase>
1817
+ +
1818
+ + <TestCase tags={['C_API']} itShould='getInitialOrientation:获取应用初始方向'>
1819
+ + <TouchableOpacity onPress={() => {
1820
+ + checkInitialOrientation();
1821
+ + }}>
1822
+ + <Text>点击此处获取初始方向</Text>
1823
+ + </TouchableOpacity>
1824
+ + <Text style={styles.textContent}>{initialOrientation}</Text>
1825
+ + </TestCase>
1826
+ +
1827
+ + <TestCase tags={['C_API']} itShould='getAutoRotateState:获取系统自动旋转状态'>
1828
+ + <TouchableOpacity onPress={() => {
1829
+ + checkAutoRotateState();
1830
+ + }}>
1831
+ + <Text>点击此处获取自动旋转状态</Text>
1832
+ + </TouchableOpacity>
1833
+ + <Text style={styles.textContent}>{autoRotateState}</Text>
1834
+ + <Text style={styles.noteText}>注: 读取系统自动旋转开关,开启返回"已开启",关闭返回"已关闭"</Text>
1835
+ + </TestCase>
1836
+ +
1837
+ + <TestCase tags={['C_API']} itShould='configure:配置disableFaceUpDown选项'>
1838
+ + <Text style={styles.textContent}>当前状态: {configureState}</Text>
1839
+ + <TouchableOpacity onPress={() => {
1840
+ + configureSetting(true);
1841
+ + }}>
1842
+ + <Text>configure(disableFaceUpDown: true) - 禁用FACE-UP/DOWN</Text>
1843
+ + </TouchableOpacity>
1844
+ + <TouchableOpacity onPress={() => {
1845
+ + configureSetting(false);
1846
+ + }} style={{ marginTop: 8 }}>
1847
+ +
1848
+ ... [diff 已截断]
1849
+ ```
1850
+
1851
+ ### `example/tsconfig.json`
1852
+
1853
+ ```diff
1854
+ @@ -0,0 +1,14 @@
1855
+ +{
1856
+ + "extends": "@react-native/typescript-config/tsconfig.json",
1857
+ + "compilerOptions": {
1858
+ + "jsx": "react-native",
1859
+ + "paths": {
1860
+ + "react-native": [
1861
+ + "./node_modules/react-native-harmony"
1862
+ + ],
1863
+ + },
1864
+ + },
1865
+ + "exclude": [
1866
+ + "harmony"
1867
+ + ]
1868
+ +}
1869
+
1870
+
1871
+ ```
1872
+
1873
+ ### `harmony/orientation_locker/src/main/cpp/generated/RNOH/generated/turbo_modules/OreitationLockerNativeModule.cpp`
1874
+
1875
+ ```diff
1876
+ @@ -21,6 +21,8 @@ OreitationLockerNativeModule::OreitationLockerNativeModule(const ArkTSTurboModul
1877
+ ARK_METHOD_METADATA(getDeviceOrientation, 1),
1878
+ ARK_METHOD_METADATA(getAutoRotateState, 1),
1879
+ ARK_METHOD_METADATA(configure, 1),
1880
+ + ARK_METHOD_METADATA(openSensor, 0),
1881
+ + ARK_METHOD_METADATA(closeSensor, 0),
1882
+ };
1883
+ }
1884
+
1885
+
1886
+ ```
1887
+
1888
+ ### `harmony/orientation_locker/src/main/ets/Logger.ts`
1889
+
1890
+ ```diff
1891
+ @@ -0,0 +1,64 @@
1892
+ +/*
1893
+ + * MIT License
1894
+ + *
1895
+ + * Copyright (C) 2026 Huawei Device Co., Ltd.
1896
+ + *
1897
+ + * Permission is hereby granted, free of charge, to any person obtaining a copy
1898
+ + * of this software and associated documentation files (the "Software"), to deal
1899
+ + * in the Software without restriction, including without limitation the rights
1900
+ + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1901
+ + * copies of the Software, and to permit persons to whom the Software is
1902
+ + * furnished to do so, subject to the following conditions:
1903
+ + *
1904
+ + * The above copyright notice and this permission notice shall be included in all
1905
+ + * copies or substantial portions of the Software.
1906
+ + *
1907
+ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1908
+ + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1909
+ + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1910
+ + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1911
+ + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1912
+ + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1913
+ + * SOFTWARE.
1914
+ + */
1915
+ +
1916
+ +import hilog from '@ohos.hilog';
1917
+ +
1918
+ +class Logger {
1919
+ + private domain: number;
1920
+ + private prefix: string;
1921
+ + private format: string = '%{public}s, %{public}s';
1922
+ + private isDebug: boolean;
1923
+ +
1924
+ + /**
1925
+ + * constructor.
1926
+ + *
1927
+ + * @param Prefix Identifies the log tag.
1928
+ + * @param domain Domain Indicates the service domain, which is a hexadecimal integer ranging from 0x0 to 0xFFFFF.
1929
+ + */
1930
+ + constructor(prefix: string = 'MyApp', domain: number = 0xFF00, isDebug = false) {
1931
+ + this.prefix = prefix;
1932
+ + this.domain = domain;
1933
+ + this.isDebug = isDebug;
1934
+ + }
1935
+ +
1936
+ + debug(...args: string[]): void {
1937
+ + if (this.isDebug) {
1938
+ + hilog.debug(this.domain, this.prefix, this.format, args);
1939
+ + }
1940
+ + }
1941
+ +
1942
+ + info(...args: string[]): void {
1943
+ + hilog.info(this.domain, this.prefix, this.format, args);
1944
+ + }
1945
+ +
1946
+ + warn(...args: string[]): void {
1947
+ + hilog.warn(this.domain, this.prefix, this.format, args);
1948
+ + }
1949
+ +
1950
+ + error(...args: string[]): void {
1951
+ + hilog.error(this.domain, this.prefix, this.format, args);
1952
+ + }
1953
+ +}
1954
+ +
1955
+ +export default new Logger('orientationLocker', 0xFF00, false)
1956
+
1957
+
1958
+ ```
1959
+
1960
+ ### `harmony/orientation_locker/src/main/ets/RNOrientationLockerTurboModule.ts`
1961
+
1962
+ ```diff
1963
+ @@ -11,7 +11,7 @@ import display from '@ohos.display'
1964
+ import { BusinessError } from '@kit.BasicServicesKit'
1965
+ import sensor from '@ohos.sensor';
1966
+ import settings from '@ohos.settings';
1967
+ -
1968
+ +import Logger from './Logger'
1969
+ const ORIENTATION_UNKNOWN = -1;
1970
+
1971
+ export class RNOrientationLockerTurboModule extends TurboModule implements TM.OreitationLockerNativeModule.Spec {
1972
+ @@ -30,51 +30,72 @@ export class RNOrientationLockerTurboModule extends TurboModule implements TM.Or
1973
+ ctx.rnInstance.emitDeviceEvent('orientationDidChange', { orientation: displayValueString })
1974
+ }
1975
+ })
1976
+ + }
1977
+
1978
+ - sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
1979
+ - const X = -data.x;
1980
+ - const Y = -data.y;
1981
+ - const Z = -data.z;
1982
+ - const xyMagnitude = X * X + Y * Y;
1983
+ - const zSquared = Z * Z;
1984
+ - let orientation = ORIENTATION_UNKNOWN;
1985
+ - if (xyMagnitude * 4 >= zSquared) {
1986
+ - const angleRad = Math.atan2(-Y, X);
1987
+ - let angleDeg = 90 - (angleRad * 180 / Math.PI);
1988
+ - while (angleDeg >= 360) angleDeg -= 360;
1989
+ - while (angleDeg < 0) angleDeg += 360;
1990
+ - orientation = Math.round(angleDeg);
1991
+ - }
1992
+ + openSensor() {
1993
+ + try {
1994
+ + sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
1995
+ + const X = -data.x;
1996
+ + const Y = -data.y;
1997
+ + const Z = -data.z;
1998
+ + const xyMagnitude = X * X + Y * Y;
1999
+ + const zSquared = Z * Z;
2000
+ + let orientation = ORIENTATION_UNKNOWN;
2001
+ + if (xyMagnitude * 4 >= zSquared) {
2002
+ + const angleRad = Math.atan2(-Y, X);
2003
+ + let angleDeg = 90 - (angleRad * 180 / Math.PI);
2004
+ + while (angleDeg >= 360) {
2005
+ + angleDeg -= 360;
2006
+ + }
2007
+ + while (angleDeg < 0) {
2008
+ + angleDeg += 360;
2009
+ + }
2010
+ + orientation = Math.round(angleDeg);
2011
+ + }
2012
+
2013
+ - let deviceOrientationValue:string = this.lastDeviceSensorOrientationValue;
2014
+ - if (xyMagnitude * 4 < zSquared) {
2015
+ - // Z 轴主导,设备平放
2016
+ - // 注意:Z = -data.z,取反后正负颠倒
2017
+ - // data.z > 0(屏幕朝上)→ Z < 0 → FACE-UP
2018
+ - // data.z < 0(屏幕朝下)→ Z > 0 → FACE-DOWN
2019
+ - if (!this.disableFaceUpDown) {
2020
+ - if (Z < 0) {
2021
+ - deviceOrientationValue = "FACE-UP";
2022
+ - } else {
2023
+ - deviceOrientationValue = "FACE-DOWN";
2024
+ - }
2025
+ - }
2026
+ - } else if (orientation === ORIENTATION_UNKNOWN) {
2027
+ - deviceOrientationValue = "UNKNOWN";
2028
+ - } else if (orientation > 355 || orientation < 5) {
2029
+ - deviceOrientationValue = "PORTRAIT";
2030
+ - } else if (orientation > 85 && orientation < 95) {
2031
+ - deviceOrientationValue = "LANDSCAPE-RIGHT";
2032
+ - } else if (orientation > 175 && orientation < 185) {
2033
+ - deviceOrientationValue = "PORTRAIT-UPSIDEDOWN";
2034
+ - } else if (orientation > 265 && orientation < 275) {
2035
+ - deviceOrientationValue = "LANDSCAPE-LEFT";
2036
+ - }
2037
+ - if (this.lastDeviceSensorOrientationValue !== deviceOrientationValue) {
2038
+ - ctx.rnInstance.emitDeviceEvent('deviceOrientationDidChange', { deviceOrientation: deviceOrientationValue })
2039
+ - this.lastDeviceSensorOrientationValue = deviceOrientationValue;
2040
+ - }
2041
+ - }, { interval: 100000000 });
2042
+ + let deviceOrientationValue: string = this.lastDeviceSensorOrientationValue;
2043
+ + if (xyMagnitude * 4 < zSquared) {
2044
+ + // Z 轴主导,设备平放
2045
+ + // 注意:Z = -data.z,取反后正负颠倒
2046
+ + // data.z > 0(屏幕朝上)→ Z < 0 → FACE-UP
2047
+ + // data.z < 0(屏幕朝下)→ Z > 0 → FACE-DOWN
2048
+ + if (!this.disableFaceUpDown) {
2049
+ + if (Z < 0) {
2050
+ + deviceOrientationValue = "FACE-UP";
2051
+ + } else {
2052
+ + deviceOrientationValue = "FACE-DOWN";
2053
+ + }
2054
+ + }
2055
+ + } else if (orientation === ORIENTATION_UNKNOWN) {
2056
+ + deviceOrientationValue = "UNKNOWN";
2057
+ + } else if (orientation > 355 || orientation < 5) {
2058
+ + deviceOrientationValue = "PORTRAIT";
2059
+ + } else if (orientation > 85 && orientation < 95) {
2060
+ + deviceOrientationValue = "LANDSCAPE-RIGHT";
2061
+ + } else if (orientation > 175 && orientation < 185) {
2062
+ + deviceOrientationValue = "PORTRAIT-UPSIDEDOWN";
2063
+ + } else if (orientation > 265 && orientation < 275) {
2064
+ + deviceOrientationValue = "LANDSCAPE-LEFT";
2065
+ + }
2066
+ + if (this.lastDeviceSensorOrientationValue !== deviceOrientationValue) {
2067
+ + this.ctx.rnInstance.emitDeviceEvent('deviceOrientationDidChange',
2068
+ + { deviceOrientation: deviceOrientationValue })
2069
+ + this.lastDeviceSensorOrientationValue = deviceOrientationValue;
2070
+ + }
2071
+ + }, { interval: 100000000 });
2072
+ + } catch (err) {
2073
+ + let e: BusinessError = err as BusinessError;
2074
+ + Logger.error(`Failed sensor.on error. Code: ${e.code}, message: ${e.message}`);
2075
+ + }
2076
+ + }
2077
+ +
2078
+ + closeSensor() {
2079
+ + try {
2080
+ + sensor.off(sensor.SensorId.ACCELEROMETER);
2081
+ + } catch (err) {
2082
+ + let e: BusinessError = err as BusinessError;
2083
+ + Logger.error(`Failed sensor.off error. Code: ${e.code}, message: ${e.message}`);
2084
+ + }
2085
+ }
2086
+
2087
+ private windowClass: window.Window | undefined = undefined
2088
+ @@ -230,9 +251,15 @@ export class RNOrientationLockerTurboModule extends TurboModule implements TM.Or
2089
+ }
2090
+ }
2091
+
2092
+ - __onDestroy__(): void {
2093
+ - super.__onDestroy__()
2094
+ - display.off('change')
2095
+ - sensor.off(sensor.SensorId.ACCELEROMETER)
2096
+ + __onDestroy__(): void {
2097
+ + super.__onDestroy__()
2098
+ + display.off('change')
2099
+ + try {
2100
+ + this.closeSensor();
2101
+ + } catch (error) {
2102
+ + let e: BusinessError = error as BusinessError;
2103
+ + Logger.error(`Failed to closeSensor. Code: ${e.code}, message: ${e.message}`);
2104
+ + }
2105
+ +
2106
+ }
2107
+ }
2108
+
2109
+
2110
+ ```
2111
+
2112
+ ### `harmony/orientation_locker/src/main/ets/generated/turboModules/OreitationLockerNativeModule.ts`
2113
+
2114
+ ```diff
2115
+ @@ -31,5 +31,9 @@ export namespace OreitationLockerNativeModule {
2116
+ getAutoRotateState(callback: (state: boolean) => void): void;
2117
+
2118
+ configure(options: Object): void;
2119
+ +
2120
+ + openSensor(): void;
2121
+ +
2122
+ + closeSensor(): void;
2123
+ }
2124
+ }
2125
+
2126
+ ```
2127
+
2128
+ ### `package.json`
2129
+
2130
+ ```diff
2131
+ @@ -3,12 +3,12 @@
2132
+ "version": "1.8.1-beta.2",
2133
+ "summary": "A react-native module that can listen on orientation changing of device",
2134
+ "description": "A react-native module that can listen on orientation changing of device, get current orientation, lock to preferred orientation.",
2135
+ - "homepage": "https://gitcode.com/openharmony-sig/rntpc_react-native-orientation-locker",
2136
+ + "homepage": "https://gitcode.com/CPF-RN/rntpc_react-native-orientation-locker",
2137
+ "main": "index.js",
2138
+ "types": "index.d.ts",
2139
+ "repository": {
2140
+ "type": "git",
2141
+ - "url": "https://gitcode.com/openharmony-sig/rntpc_react-native-orientation-locker"
2142
+ + "url": "https://gitcode.com/CPF-RN/rntpc_react-native-orientation-locker"
2143
+ },
2144
+ "scripts": {
2145
+ "codegen-lib": "react-native codegen-lib-harmony --no-safety-check --npm-package-name react-native-orientation-locker --cpp-output-path ./harmony/orientation_locker/src/main/cpp/generated --ets-output-path ./harmony/orientation_locker/src/main/ets/generated --turbo-modules-spec-paths ./src/OreitationLockerNativeModule.ts"
2146
+ @@ -28,7 +28,7 @@
2147
+ },
2148
+ "license": "MIT",
2149
+ "bugs": {
2150
+ - "url": "https://gitcode.com/openharmony-sig/rntpc_react-native-orientation-locker/issues"
2151
+ + "url": "https://gitcode.com/CPF-RN/rntpc_react-native-orientation-locker/issues"
2152
+ },
2153
+ "dependencies": {},
2154
+ "peerDependencies": {
2155
+
2156
+ ```
2157
+
2158
+ ### `src/OreitationLockerNativeModule.ts`
2159
+
2160
+ ```diff
2161
+ @@ -39,6 +39,11 @@ export interface Spec extends TurboModule {
2162
+ // 配置选项
2163
+ configure(options: Object): void;
2164
+
2165
+ + // 订阅加速度传感器
2166
+ + openSensor(): void;
2167
+ +
2168
+ + // 取消订阅加速度传感器
2169
+ + closeSensor(): void;
2170
+ }
2171
+ export default TurboModuleRegistry.get<Spec>('OreitationLockerNativeModule') as Spec | null;
2172
+
2173
+
2174
+ ```
2175
+
2176
+ ### `src/orientation.harmony.js`
2177
+
2178
+ ```diff
2179
+ @@ -115,6 +115,7 @@ export default class Orientation {
2180
+ };
2181
+
2182
+ static addDeviceOrientationListener = cb => {
2183
+ + OrientationNative.openSensor();
2184
+ var key = getKey(cb);
2185
+ listeners[key] = deviceEventEmitter.addListener(
2186
+ "deviceOrientationDidChange",
2187
+ @@ -135,6 +136,7 @@ export default class Orientation {
2188
+ }
2189
+ listeners[key].remove();
2190
+ listeners[key] = null;
2191
+ + OrientationNative.closeSensor();
2192
+ };
2193
+
2194
+ static addLockListener = cb => {
2195
+
2196
+ ```
2197
+
2198
+ ### `src/orientation.js`
2199
+
2200
+ ```diff
2201
+ @@ -46,6 +46,10 @@ export default class Orientation {
2202
+
2203
+ static removeDeviceOrientationListener = cb => {};
2204
+
2205
+ + static openSensor = () => {};
2206
+ +
2207
+ + static closeSensor = () => {};
2208
+ +
2209
+ static addLockListener = cb => {};
2210
+
2211
+ static removeLockListener = cb => {};
2212
+
2213
+ ```