From f5c84354b7da46c5072d51e7ef2b1882233a9364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B0=B8=E5=87=AF?= Date: Mon, 14 Apr 2025 17:05:30 +0800 Subject: [PATCH] add XComponent project MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 刘永凯 --- .gitignore | 11 + AppScope/app.json5 | 25 + AppScope/resources/base/element/string.json | 8 + AppScope/resources/base/media/app_icon.png | Bin 0 -> 2777 bytes README_zh.md | 102 +++ build-profile.json5 | 51 ++ entry/.gitignore | 6 + entry/build-profile.json5 | 56 ++ entry/hvigorfile.ts | 6 + entry/oh-package.json5 | 11 + entry/src/main/cpp/CMakeLists.txt | 69 ++ entry/src/main/cpp/common/common.h | 31 + entry/src/main/cpp/manager/plugin_manager.cpp | 218 +++++++ entry/src/main/cpp/manager/plugin_manager.h | 41 ++ entry/src/main/cpp/napi_init.cpp | 65 ++ entry/src/main/cpp/render/egl_core.cpp | 607 ++++++++++++++++++ entry/src/main/cpp/render/egl_core.h | 59 ++ entry/src/main/cpp/render/plugin_render.cpp | 62 ++ entry/src/main/cpp/render/plugin_render.h | 47 ++ .../main/cpp/types/libnativerender/Index.d.ts | 24 + .../types/libnativerender/oh-package.json5 | 6 + .../main/ets/entryability/EntryAbility.ets | 56 ++ entry/src/main/ets/pages/Index.ets | 106 +++ entry/src/main/module.json5 | 52 ++ .../main/resources/base/element/color.json | 8 + .../main/resources/base/element/string.json | 16 + entry/src/main/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/main_pages.json | 5 + .../main/resources/en_US/element/string.json | 16 + .../main/resources/zh_CN/element/string.json | 16 + .../ets/TestRunner/OpenHarmonyTestRunner.ts | 63 ++ entry/src/ohosTest/ets/test/List.test.ets | 20 + .../ets/test/XComponentAbility.test.ets | 74 +++ .../ohosTest/ets/testability/TestAbility.ets | 63 ++ .../ohosTest/ets/testability/pages/Index.ets | 52 ++ entry/src/ohosTest/module.json5 | 51 ++ .../resources/base/element/color.json | 8 + .../resources/base/element/string.json | 16 + .../ohosTest/resources/base/media/icon.png | Bin 0 -> 6790 bytes .../resources/base/profile/test_pages.json | 5 + hvigor/hvigor-config.json5 | 37 ++ hvigor/hvigor-wrapper.js | 1 + hvigorfile.ts | 6 + hvigorw | 54 ++ hvigorw.bat | 54 ++ oh-package.json5 | 14 + ohosTest.md | 9 + screenshots/device/changeColor.png | Bin 0 -> 31936 bytes screenshots/device/drawStar.png | Bin 0 -> 33051 bytes screenshots/device/main.png | Bin 0 -> 28584 bytes 50 files changed, 2307 insertions(+) create mode 100644 .gitignore create mode 100644 AppScope/app.json5 create mode 100644 AppScope/resources/base/element/string.json create mode 100644 AppScope/resources/base/media/app_icon.png create mode 100644 README_zh.md create mode 100644 build-profile.json5 create mode 100644 entry/.gitignore create mode 100644 entry/build-profile.json5 create mode 100644 entry/hvigorfile.ts create mode 100644 entry/oh-package.json5 create mode 100644 entry/src/main/cpp/CMakeLists.txt create mode 100644 entry/src/main/cpp/common/common.h create mode 100644 entry/src/main/cpp/manager/plugin_manager.cpp create mode 100644 entry/src/main/cpp/manager/plugin_manager.h create mode 100644 entry/src/main/cpp/napi_init.cpp create mode 100644 entry/src/main/cpp/render/egl_core.cpp create mode 100644 entry/src/main/cpp/render/egl_core.h create mode 100644 entry/src/main/cpp/render/plugin_render.cpp create mode 100644 entry/src/main/cpp/render/plugin_render.h create mode 100644 entry/src/main/cpp/types/libnativerender/Index.d.ts create mode 100644 entry/src/main/cpp/types/libnativerender/oh-package.json5 create mode 100644 entry/src/main/ets/entryability/EntryAbility.ets create mode 100644 entry/src/main/ets/pages/Index.ets create mode 100644 entry/src/main/module.json5 create mode 100644 entry/src/main/resources/base/element/color.json create mode 100644 entry/src/main/resources/base/element/string.json create mode 100644 entry/src/main/resources/base/media/icon.png create mode 100644 entry/src/main/resources/base/profile/main_pages.json create mode 100644 entry/src/main/resources/en_US/element/string.json create mode 100644 entry/src/main/resources/zh_CN/element/string.json create mode 100644 entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts create mode 100644 entry/src/ohosTest/ets/test/List.test.ets create mode 100644 entry/src/ohosTest/ets/test/XComponentAbility.test.ets create mode 100644 entry/src/ohosTest/ets/testability/TestAbility.ets create mode 100644 entry/src/ohosTest/ets/testability/pages/Index.ets create mode 100644 entry/src/ohosTest/module.json5 create mode 100644 entry/src/ohosTest/resources/base/element/color.json create mode 100644 entry/src/ohosTest/resources/base/element/string.json create mode 100644 entry/src/ohosTest/resources/base/media/icon.png create mode 100644 entry/src/ohosTest/resources/base/profile/test_pages.json create mode 100644 hvigor/hvigor-config.json5 create mode 100644 hvigor/hvigor-wrapper.js create mode 100644 hvigorfile.ts create mode 100644 hvigorw create mode 100644 hvigorw.bat create mode 100644 oh-package.json5 create mode 100644 ohosTest.md create mode 100644 screenshots/device/changeColor.png create mode 100644 screenshots/device/drawStar.png create mode 100644 screenshots/device/main.png diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbabf77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test \ No newline at end of file diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000..efa5c58 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "app": { + "bundleName": "com.samples.arktsxcomponent", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..2379797 --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ArkTSXComponent" + } + ] +} diff --git a/AppScope/resources/base/media/app_icon.png b/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 GIT binary patch literal 2777 zcmV;~3MTc5P)9*YHQQH znh@I(s7WDIN`nJ+5@|<)iZcg=qN74U#DNnD1Se7u4fs(|1ivr?9ayP|B3iYCD$mfQ zCQ{S1n2)}^yxe#1J=_0pt-a1UPwQ^Z*?X_`Uu*sM+8<}X+baE^a`3seUF}?bEaiMO zrD`Qrd5@qw^epHZ>Df|p-qKBUEB%*?!m0{PHC6j|RplEgR~PkM5a^}N)Sfwi>W;Uz zdhwo_4HXBU%kRl^w@&7iKPx$e-n9%#IU!&oMI~iNsw0n19qSX;dS>I`G_G=WdcN9r z;_Rtv9XC<7kbL+HHxJ782T~pg05t)tf^>2vNJqfYt{YmqQDoBxkv+ra*BxxhcuK2v zm5%@Y)biQz)R8O%e=o%n${;ojY;EUP>`Qj6Cq)7GHm)C%2%^+hI;Z4T#a|oKIvshv z5H%!I+|I4PEXaXj04%ybsVolr%vhKnW7AEhC?eP!o1{y;8m2R#;}{6VZPc!+)ou0C zVWz$|1#2(|L5z%EYRxOzP+uLB>qYGuajX-<#^u;Kw&2uh&93)h>nHaFA%{&2PW=Nn zr?*a;gk3xvRhQIRa1de-!r(ss&?tRmZ=L2FMkhxI3lK6Jn<>5c*ID|@KU#^MCIo6> zpFA{|R(4fsBwHIW z9v!7G|7enadv4}~*8q_h%tD^j$7=PCnn0=dR0GKA(fgb9`2IRg6ksBIo+Gdw#|-3eSe=3tmDe zIqVN)tScM`0W#Z>2wc>~2Uv=3L)~D4gXqZtPQ8rifbYJqwkG>bv}95G7+};9Br?hF zWSa3b)X}z#79W9kukM%6-b_54WDJm~Ub=gsrJ0lz-8&lrQ7zfK1qzuZQkZvcE3|~S zZWmk0ETaNIHnMALn>akuvHLf5c4`y%!f+u>ZGp%@q_;T!`76_snc_?K;Wx%YpF;5K zw^F+BCYUPy`fpRif@5O@Im5cf?evD$>KlAgX;D0*HiO0`Yg3j;R4jT(9h(L_TsY6yxk*@ZBe%+dMqY=cB5oGs{D$QwOFbH)G$iVf<3Olcd7^#fr- zM{!ILWt#coT)s9ySkwDCPHv0oww8g8K%Yr{aR}msELVX(}JQr%F4Q8=KKn*OjSO*uSp;JK%GwhRF_K??vGC$ZqmJX z@+}8sQ)9Z}3*DiWl+L_7OXn_^{SW~2&C*b^;%IP!j$lkre7H&bMR1}7aTT*G8P}|G zHM1)hZDe{r_E3{{Y=d}}_PxJO_w4MaE4)$<<3JwzPdwPzfNemK(-X;{UCzmVr0zu5 zEnT}fzx)oVd!*W77`1Ig`DFcZ6TkPaI$hO1+`cGb$({ukz&{p4Ic-Xnwrg-KEkDqW zW3l$7Q`V$!1T(=QL1jgjIachdr75>-8>1A^h+;rTrD^nnwf?bw(Rang!*16Odj$Pn z@)JN5&5w~}ae6d};oa|&G>sT!)ixE#5;QW(u(=bqYHXcOflE%@t4A?n5fTUm0F~8_ zwpoz9rrU`@G=vsNjDRY(CrF(jIjqg8bd|CP02>eFag7T?u;C^ir+Z7YKmBYw;%%XdT2T}a$X4yR7EI;zaof3a)5Z;`OwVi%D?gbkBj!{;z2tOBSFk&E1DeiZXD**uvNqL}+|pO{ ztO$}2NMRit2ddU?)7Prq&*&H3X>&=E{-+j4iUz zrvL;?0$^@lyl=LHz9G^$SJV6ID__@7z->Bh>Vm=6AK&5bP%@heveHja5F@agGgUsY z@L@W2+^*NVoId0!kS~4XkWb%y;f}XBf>S+NIw9aHK;vN+4mJ|em)_QjIVfb2$;bwv zDKmoq6AThgKydS6Hs+UpKPWq|UA}s=UOEBZNM3oNT5qTAabY)X>L6jxfGDuu7&GD_ z=@@m?sJ-o2GS}&hNRW}-zHkr>o4&138@a8IC-FjSBxzjx?(*3@YmdmWGAd%0QvXzS zJ53JpX%Fp!=>v&`Hd7F@+Atw2vx9%^2M-APg0Jd|ePsRn3*B$#9Z5hCou4fo7W#SN z#}-@-N=##yQDh26pNzr9f*Q88krhI5@DHcf{dU-~PLSs}MvI4s1i|<=qxD~9`7>*~ znlw5lr$_6mTG4XbBNF_79BzvZ!TeIP)exdk3)kSHjYdW1P10ZJ_NCJSlrCuIU#gqw f88(SSw!Z%ZUzhC#9QlKF00000NkvXXu0mjfG$}gK literal 0 HcmV?d00001 diff --git a/README_zh.md b/README_zh.md new file mode 100644 index 0000000..cff0d35 --- /dev/null +++ b/README_zh.md @@ -0,0 +1,102 @@ +# XComponent + +### 介绍 + +本示例主要介绍开发者如何使用ArkTS XComponent组件进行自绘制,主要包括:XComponent组件使用, +SurfaceId获取方法,Surface生命周期回调使用,NativeWindow创建等知识点。开发者基于ArkTS侧获取的SurfaceId, +在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例后,使用OpenGL ES/EGL接口在XComponent组件上进行图形绘制。功能主要包括点击按钮绘制一个五角星,并可以通过点击XComponent区域改变五角星的颜色。 + +### 效果预览 + +| 主页 | 绘制五角星 | 改变颜色 | +|--------------------------------------|-----------------------------------------------|-----------------------------------------------------| +| ![main](screenshots/device/main.png) | ![draw star](screenshots/device/drawStar.png) | ![change color](screenshots/device/changeColor.png) | + +使用说明 + +1. 安装编译生成的hap包,并打开应用。 + +2. 点击页面底部“Draw Star”按钮,页面将绘制一个五角星。 + +3. 点击XComponent组件区域(页面中灰色区域)改变五角星颜色。 + + +### 工程目录 + +``` +├──entry/src/main +│ ├──cpp // C++代码区 +│ │ ├──CMakeLists.txt // CMake配置文件 +│ │ ├──napi_init.cpp // Napi模块注册 +│ │ ├──common +│ │ │ └──common.h // 常量定义文件 +│ │ ├──manager // 生命周期管理模块 +│ │ │ ├──plugin_manager.cpp +│ │ │ └──plugin_manager.h +│ │ ├──render // 渲染模块 +│ │ │ ├──egl_core.cpp +│ │ │ ├──egl_core.h +│ │ │ ├──plugin_render.cpp +│ │ │ └──plugin_render.h +│ ├──ets // ets代码区 +│ │ ├──entryability +│ │ │ └──EntryAbility.ts // 程序入口类 +│ │ └──pages // 页面文件 +│ │ └──Index.ets // 主界面 +| ├──resources // 资源文件目录 +``` + +### 具体实现 + +通过在IDE中创建Native c++ 工程,在c++代码中定义对外接口为DrawPattern,在ArkTS侧调用该接口可在页面上绘制出一个五角星。在 +c++代码中定义对外接口为ChangeColor,点击XComponent组件时,在ArkTs侧调用该接口可在页面绘制一个大小相同、颜色不同的五角星,达到改变颜色的目的。 + +在XComponentController的OnSurfaceCreated回调中,传入XComponent的surfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId创建NativeWindow实例并初始化 +EGL环境。在XComponentController的OnsurfaceChanged回调中,传入surfaceId、宽和高,并以此为输入调用EGL相关的接口改变对应NativeWindow的尺寸和内容。 + +源码参考:[main目录](entry/src/main/)下的文件。涉及到的相关接口: + +#### ArkTS组件 +XComponentController + +| 接口名 | 描述 | +|-------------------------------------------|--------------------------| +| getXComponentSurfaceId(): string | 获取XComponent对应Surface的ID | +| onSurfaceCreated(surfaceId: string): void |当XComponent持有的Surface创建后进行该回调| +|onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void|当XComponent持有的Surface尺寸更新时进行该回调,包括初始尺寸设定| +|onSurfaceDestroyed(surfaceId: string): void|当XComponent持有的Surface销毁后进行该回调| + +#### C API +| 接口名 | 描述 | +|-------------------------------------------|--------------------------| +| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | 通过surfaceId创建对应的OHNativeWindow | +| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window)|将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉| + + +### 相关权限 + +不涉及。 + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:rk3568 + +2. 本示例为Stage模型,支持API12版本SDK,SDK版本号(API Version 12 Release),镜像版本号(5.0 Release) + +3. 本示例需要使用DevEco Studio 版本号(4.0 Release)及以上版本才可编译运行 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/BasicFeature/Native/ArkTSXComponent/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..9c467cb --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.0(12)", + "targetSdkVersion": "5.0.0(12)", + "runtimeOS": "HarmonyOS", + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/entry/.gitignore b/entry/.gitignore new file mode 100644 index 0000000..e2713a2 --- /dev/null +++ b/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/entry/build-profile.json5 b/entry/build-profile.json5 new file mode 100644 index 0000000..af886b0 --- /dev/null +++ b/entry/build-profile.json5 @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + "abiFilters": ["arm64-v8a", "x86_64"] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/entry/hvigorfile.ts b/entry/hvigorfile.ts new file mode 100644 index 0000000..c6edcd9 --- /dev/null +++ b/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/entry/oh-package.json5 b/entry/oh-package.json5 new file mode 100644 index 0000000..811a0b1 --- /dev/null +++ b/entry/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libnativerender.so": "file:./src/main/cpp/types/libnativerender" + } +} \ No newline at end of file diff --git a/entry/src/main/cpp/CMakeLists.txt b/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..9364964 --- /dev/null +++ b/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,69 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.4.1) +project(XComponent) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +add_definitions(-DOHOS_PLATFORM) + +include_directories( + ${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include +) + +add_library(nativerender SHARED + render/egl_core.cpp + render/plugin_render.cpp + manager/plugin_manager.cpp + napi_init.cpp +) + +find_library( + # Sets the name of the path variable. + EGL-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + EGL +) + +find_library( + # Sets the name of the path variable. + GLES-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + GLESv3 +) + +find_library( + # Sets the name of the path variable. + hilog-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + hilog_ndk.z +) + +find_library( + # Sets the name of the path variable. + libace-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + ace_ndk.z +) + +find_library( + # Sets the name of the path variable. + libnapi-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + ace_napi.z +) + +find_library( + # Sets the name of the path variable. + libuv-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + uv +) + +target_link_libraries(nativerender PUBLIC + ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) \ No newline at end of file diff --git a/entry/src/main/cpp/common/common.h b/entry/src/main/cpp/common/common.h new file mode 100644 index 0000000..c776010 --- /dev/null +++ b/entry/src/main/cpp/common/common.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NATIVE_XCOMPONENT_COMMON_H +#define NATIVE_XCOMPONENT_COMMON_H + +#include +#include +#include +#include +#include + +namespace NativeXComponentSample { +/** + * Log print domain. + */ +const unsigned int LOG_PRINT_DOMAIN = 0xFF00; +} // namespace NativeXComponentSample +#endif // NATIVE_XCOMPONENT_COMMON_H diff --git a/entry/src/main/cpp/manager/plugin_manager.cpp b/entry/src/main/cpp/manager/plugin_manager.cpp new file mode 100644 index 0000000..8942dc8 --- /dev/null +++ b/entry/src/main/cpp/manager/plugin_manager.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "plugin_manager.h" +#include +#include +#include +#include +#include +#include "../common/common.h" +#include + +namespace NativeXComponentSample { + +namespace { + int64_t ParseId(napi_env env, napi_callback_info info) + { + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null"); + return -1; + } + size_t argc = 1; + napi_value args[1] = {nullptr}; + if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed"); + return -1; + } + int64_t value = 0; + bool lossless = true; + if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed"); + return -1; + } + return value; + } +} + +std::unordered_map PluginManager::pluginRenderMap_; +std::unordered_map PluginManager::windowMap_; + +PluginManager::~PluginManager() +{ + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "~PluginManager"); + for (auto iter = pluginRenderMap_.begin(); iter != pluginRenderMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + pluginRenderMap_.clear(); + for (auto iter = windowMap_.begin(); iter != windowMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + windowMap_.clear(); +} + +PluginRender* PluginManager::GetPluginRender(int64_t& id) +{ + if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) { + return pluginRenderMap_[id]; + } + return nullptr; +} + +napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + OHNativeWindow *nativeWindow; + PluginRender *pluginRender; + if (windowMap_.find(surfaceId) == windowMap_.end()) { + OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); + windowMap_[surfaceId] = nativeWindow; + } + if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { + pluginRender = new PluginRender(surfaceId); + pluginRenderMap_[surfaceId] = pluginRender; + } + pluginRender->InitNativeWindow(nativeWindow); + return nullptr; +} + +napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId); + if (pluginRenderMapIter != pluginRenderMap_.end()) { + delete pluginRenderMapIter->second; + pluginRenderMap_.erase(pluginRenderMapIter); + } + auto windowMapIter = windowMap_.find(surfaceId); + if (windowMapIter != windowMap_.end()) { + OH_NativeWindow_DestroyNativeWindow(windowMapIter->second); + windowMap_.erase(windowMapIter); + } + return nullptr; +} + +napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) +{ + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "ChangeSurface: OnLoad env or info is null"); + return nullptr; + } + int64_t surfaceId = 0; + size_t argc = 3; + napi_value args[3] = {nullptr}; + + if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "ChangeSurface: GetContext napi_get_cb_info failed"); + } + bool lossless = true; + int index = 0; + if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed"); + } + double width; + if (napi_ok != napi_get_value_double(env, args[index++], &width)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed"); + } + double height; + if (napi_ok != napi_get_value_double(env, args[index++], &height)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed"); + } + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed"); + return nullptr; + } + pluginRender->UpdateNativeWindowSize(width, height); + return nullptr; +} + +napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed"); + return nullptr; + } + pluginRender->ChangeColor(); + return nullptr; +} + +napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed"); + return nullptr; + } + pluginRender->DrawPattern(); + return nullptr; +} + +napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: Get pluginRender failed"); + return nullptr; + } + napi_value hasDraw; + napi_value hasChangeColor; + napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw)); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_create_int32 hasDraw_ error"); + return nullptr; + } + ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor)); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_create_int32 hasChangeColor_ error"); + return nullptr; + } + napi_value obj; + ret = napi_create_object(env, &obj); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, + "PluginManager", "GetXComponentStatus: napi_create_object error"); + return nullptr; + } + ret = napi_set_named_property(env, obj, "hasDraw", hasDraw); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_set_named_property hasDraw error"); + return nullptr; + } + ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_set_named_property hasChangeColor error"); + return nullptr; + } + return obj; +} +} // namespace NativeXComponentSample diff --git a/entry/src/main/cpp/manager/plugin_manager.h b/entry/src/main/cpp/manager/plugin_manager.h new file mode 100644 index 0000000..3734c98 --- /dev/null +++ b/entry/src/main/cpp/manager/plugin_manager.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NATIVE_XCOMPONENT_PLUGIN_MANAGER_H +#define NATIVE_XCOMPONENT_PLUGIN_MANAGER_H + +#include +#include +#include +#include +#include +#include "../render/plugin_render.h" + +namespace NativeXComponentSample { +class PluginManager { +public: + ~PluginManager(); + static PluginRender* GetPluginRender(int64_t& id); + static napi_value ChangeColor(napi_env env, napi_callback_info info); + static napi_value DrawPattern(napi_env env, napi_callback_info info); + static napi_value SetSurfaceId(napi_env env, napi_callback_info info); + static napi_value ChangeSurface(napi_env env, napi_callback_info info); + static napi_value DestroySurface(napi_env env, napi_callback_info info); + static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); +public: + static std::unordered_map pluginRenderMap_; + static std::unordered_map windowMap_; +}; +} // namespace NativeXComponentSample +#endif // NATIVE_XCOMPONENT_PLUGIN_MANAGER_H diff --git a/entry/src/main/cpp/napi_init.cpp b/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000..9a96bc9 --- /dev/null +++ b/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "common/common.h" +#include "manager/plugin_manager.h" + +namespace NativeXComponentSample { + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins"); + if ((env == nullptr) || (exports == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null"); + return nullptr; + } + napi_property_descriptor desc[] = { + {"ChangeColor", nullptr, PluginManager::ChangeColor, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"ChangeSurface", nullptr, PluginManager::ChangeSurface, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"DrawPattern", nullptr, PluginManager::DrawPattern, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"DestroySurface", nullptr, PluginManager::DestroySurface, + nullptr, nullptr, nullptr, napi_default, nullptr} + }; + if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); + return nullptr; + } + return exports; +} +EXTERN_C_END + +static napi_module nativerenderModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "nativerender", + .nm_priv = ((void*)0), + .reserved = { 0 } }; +} // namespace NativeXComponentSample +extern "C" __attribute__((constructor)) void RegisterModule(void) +{ + napi_module_register(&NativeXComponentSample::nativerenderModule); +} \ No newline at end of file diff --git a/entry/src/main/cpp/render/egl_core.cpp b/entry/src/main/cpp/render/egl_core.cpp new file mode 100644 index 0000000..7112fd0 --- /dev/null +++ b/entry/src/main/cpp/render/egl_core.cpp @@ -0,0 +1,607 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "egl_core.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" +#include "plugin_render.h" + +namespace NativeXComponentSample { +namespace { +constexpr int32_t NUM_4 = 4; +/** + * Vertex shader. + */ +const char VERTEX_SHADER[] = "#version 300 es\n" + "layout(location = 0) in vec4 a_position;\n" + "layout(location = 1) in vec4 a_color; \n" + "out vec4 v_color; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" + " v_color = a_color; \n" + "} \n"; + +/** + * Fragment shader. + */ +const char FRAGMENT_SHADER[] = "#version 300 es\n" + "precision mediump float; \n" + "in vec4 v_color; \n" + "out vec4 fragColor; \n" + "void main() \n" + "{ \n" + " fragColor = v_color; \n" + "} \n"; + +/** + * Background color #f4f4f4. + */ +const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f}; + +/** + * Draw color #7E8FFB. + */ +const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; + +/** + * Change color #92D6CC. + */ +const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; + +/** + * Background area. + */ +const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { + -1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + -1.0f, -1.0f}; + +/** + * Get context parameter count. + */ +const size_t GET_CONTEXT_PARAM_CNT = 1; + +/** + * Fifty percent. + */ +const float FIFTY_PERCENT = 0.5; + +/** + * Pointer size. + */ +const GLint POINTER_SIZE = 2; + +/** + * Triangle fan size. + */ +const GLsizei TRIANGLE_FAN_SIZE = 4; + +/** + * Egl red size default. + */ +const int EGL_RED_SIZE_DEFAULT = 8; + +/** + * Egl green size default. + */ +const int EGL_GREEN_SIZE_DEFAULT = 8; + +/** + * Egl blue size default. + */ +const int EGL_BLUE_SIZE_DEFAULT = 8; + +/** + * Egl alpha size default. + */ +const int EGL_ALPHA_SIZE_DEFAULT = 8; + +/** + * Default x position. + */ +const int DEFAULT_X_POSITION = 0; + +/** + * Default y position. + */ +const int DEFAULT_Y_POSITION = 0; + +/** + * Gl red default. + */ +const GLfloat GL_RED_DEFAULT = 0.0; + +/** + * Gl green default. + */ +const GLfloat GL_GREEN_DEFAULT = 0.0; + +/** + * Gl blue default. + */ +const GLfloat GL_BLUE_DEFAULT = 0.0; + +/** + * Gl alpha default. + */ +const GLfloat GL_ALPHA_DEFAULT = 1.0; + +/** + * Program error. + */ +const GLuint PROGRAM_ERROR = 0; + +/** + * Shape vertices size. + */ +const int SHAPE_VERTICES_SIZE = 8; + +/** + * Position handle name. + */ +const char POSITION_NAME[] = "a_position"; + +/** + * Position error. + */ +const GLint POSITION_ERROR = -1; + +/** + * Config attribute list. + */ +const EGLint ATTRIB_LIST[] = { + // Key,value. + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, EGL_RED_SIZE_DEFAULT, + EGL_GREEN_SIZE, EGL_GREEN_SIZE_DEFAULT, + EGL_BLUE_SIZE, EGL_BLUE_SIZE_DEFAULT, + EGL_ALPHA_SIZE, EGL_ALPHA_SIZE_DEFAULT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + // End. + EGL_NONE}; + +/** + * Context attributes. + */ +const EGLint CONTEXT_ATTRIBS[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; +} // namespace +bool EGLCore::EglContextInit(void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute"); + eglWindow_ = static_cast(window); + + // Init display. + eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay_ == EGL_NO_DISPLAY) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); + return false; + } + + EGLint majorVersion; + EGLint minorVersion; + if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display"); + return false; + } + + // Select configuration. + const EGLint maxConfigSize = 1; + EGLint numConfigs; + if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); + return false; + } + + return CreateEnvironment(); +} + +bool EGLCore::CreateEnvironment() +{ + // Create surface. + if (eglWindow_ == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null"); + return false; + } + eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); + if (eglSurface_ == nullptr) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface"); + return false; + } + // Create context. + eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); + if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); + return false; + } + // Create program. + program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); + if (program_ == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); + return false; + } + return true; +} + +void EGLCore::Background() +{ + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); + return; + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); + return; + } +} + +void EGLCore::Draw(int& hasDraw) +{ + flag_ = false; + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); + return; + } + + // Divided into five quadrilaterals and calculate one of the quadrilateral's Vertices + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + // Convert DEG(54° & 18°) to RAD + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + // Convert DEG(18°) to RAD + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + // Convert DEG(18°) to RAD + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + + // Convert DEG(72°) to RAD + GLfloat rad = M_PI / 180 * 72; + // Rotate four times + for (int i = 0; i < NUM_4; ++i) { + Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); + Rotate2d(centerX, centerY, &leftX, &leftY, rad); + Rotate2d(centerX, centerY, &rightX, &rightY, rad); + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); + return; + } + hasDraw = 1; + + flag_ = true; +} + +void EGLCore::ChangeColor(int& hasChangeColor) +{ + if (!flag_) { + return; + } + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); + return; + } + + // Divided into five quadrilaterals and calculate one of the quadrilateral's Vertices + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + // Convert DEG(54° & 18°) to RAD + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + // Convert DEG(18°) to RAD + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + // Convert DEG(18°) to RAD + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawNewStar(0, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + + // Convert DEG(72°) to RAD + GLfloat rad = M_PI / 180 * 72; + // Rotate four times + for (int i = 0; i < NUM_4; ++i) { + Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); + Rotate2d(centerX, centerY, &leftX, &leftY, rad); + Rotate2d(centerX, centerY, &rightX, &rightY, rad); + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); + } + hasChangeColor = 1; +} + +GLint EGLCore::PrepareDraw() +{ + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || + (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); + return POSITION_ERROR; + } + + // The gl function has no return value. + glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); + glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(program_); + + return glGetAttribLocation(program_, POSITION_NAME); +} + +bool EGLCore::ExecuteDraw(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glEnableVertexAttribArray(position); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + + return true; +} + +bool EGLCore::ExecuteDrawStar( + GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glVertexAttribPointer(1, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, color); + glEnableVertexAttribArray(position); + glEnableVertexAttribArray(1); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + glDisableVertexAttribArray(1); + + return true; +} + +bool EGLCore::ExecuteDrawNewStar( + GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glEnableVertexAttribArray(position); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + + return true; +} + +void EGLCore::Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat* rotateX, GLfloat* rotateY, GLfloat theta) +{ + GLfloat tempX = cos(theta) * (*rotateX - centerX) - sin(theta) * (*rotateY - centerY); + GLfloat tempY = sin(theta) * (*rotateX - centerX) + cos(theta) * (*rotateY - centerY); + *rotateX = tempX + centerX; + *rotateY = tempY + centerY; +} + +bool EGLCore::FinishDraw() +{ + // The gl function has no return value. + glFlush(); + glFinish(); + return eglSwapBuffers(eglDisplay_, eglSurface_); +} + +GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) +{ + if ((type <= 0) || (shaderSrc == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); + return PROGRAM_ERROR; + } + + GLuint shader = glCreateShader(type); + if (shader == 0) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); + return PROGRAM_ERROR; + } + + // The gl function has no return value. + glShaderSource(shader, 1, &shaderSrc, nullptr); + glCompileShader(shader); + + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (compiled != 0) { + return shader; + } + + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen <= 1) { + glDeleteShader(shader); + return PROGRAM_ERROR; + } + + char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); + if (infoLog != nullptr) { + memset(infoLog, 0, infoLen + 1); + glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); + free(infoLog); + infoLog = nullptr; + } + glDeleteShader(shader); + return PROGRAM_ERROR; +} + +GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) +{ + if ((vertexShader == nullptr) || (fragShader == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null"); + return PROGRAM_ERROR; + } + + GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); + if (vertex == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); + return PROGRAM_ERROR; + } + + GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); + if (fragment == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); + return PROGRAM_ERROR; + } + + GLuint program = glCreateProgram(); + if (program == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); + glDeleteShader(vertex); + glDeleteShader(fragment); + return PROGRAM_ERROR; + } + + // The gl function has no return value. + glAttachShader(program, vertex); + glAttachShader(program, fragment); + glLinkProgram(program); + + GLint linked; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + if (linked != 0) { + glDeleteShader(vertex); + glDeleteShader(fragment); + return program; + } + + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); + GLint infoLen = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); + memset(infoLog, 0, infoLen + 1); + glGetProgramInfoLog(program, infoLen, nullptr, infoLog); + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); + free(infoLog); + infoLog = nullptr; + } + glDeleteShader(vertex); + glDeleteShader(fragment); + glDeleteProgram(program); + return PROGRAM_ERROR; +} + +void EGLCore::UpdateSize(int width, int height) +{ + width_ = width; + height_ = height; + if (width_ > 0) { + widthPercent_ = FIFTY_PERCENT * height_ / width_; + } +} + +void EGLCore::Release() +{ + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); + } + + if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); + } + + if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); + } +} +} // namespace NativeXComponentSample diff --git a/entry/src/main/cpp/render/egl_core.h b/entry/src/main/cpp/render/egl_core.h new file mode 100644 index 0000000..0f25ca0 --- /dev/null +++ b/entry/src/main/cpp/render/egl_core.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NATIVE_XCOMPONENT_EGL_CORE_H +#define NATIVE_XCOMPONENT_EGL_CORE_H + +#include +#include +#include + +namespace NativeXComponentSample { +class EGLCore { +public: + explicit EGLCore() {}; + ~EGLCore() {} + bool EglContextInit(void* window); + bool CreateEnvironment(); + void Draw(int& hasDraw); + void Background(); + void ChangeColor(int& hasChangeColor); + void Release(); + void UpdateSize(int width, int height); + +private: + GLuint LoadShader(GLenum type, const char* shaderSrc); + GLuint CreateProgram(const char* vertexShader, const char* fragShader); + GLint PrepareDraw(); + bool ExecuteDraw(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize); + bool ExecuteDrawStar(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize); + bool ExecuteDrawNewStar(GLint position, const GLfloat* color, + const GLfloat shapeVertices[], unsigned long vertSize); + void Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat* rotateX, GLfloat* rotateY, GLfloat theta); + bool FinishDraw(); + +private: + EGLNativeWindowType eglWindow_; + EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; + EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR; + EGLSurface eglSurface_ = EGL_NO_SURFACE; + EGLContext eglContext_ = EGL_NO_CONTEXT; + GLuint program_; + bool flag_ = false; + int width_; + int height_; + GLfloat widthPercent_; +}; +} // namespace NativeXComponentSample +#endif // NATIVE_XCOMPONENT_EGL_CORE_H diff --git a/entry/src/main/cpp/render/plugin_render.cpp b/entry/src/main/cpp/render/plugin_render.cpp new file mode 100644 index 0000000..c19c5f4 --- /dev/null +++ b/entry/src/main/cpp/render/plugin_render.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "plugin_render.h" + +namespace NativeXComponentSample { + +PluginRender::PluginRender(int64_t& id) +{ + this->id_ = id; + this->eglCore_ = new EGLCore(); + hasDraw_ = 0; + hasChangeColor_ = 0; +} + +void PluginRender::ChangeColor() +{ + eglCore_->ChangeColor(hasChangeColor_); +} + +void PluginRender::DrawPattern() +{ + eglCore_->Draw(hasDraw_); +} + +void PluginRender::InitNativeWindow(OHNativeWindow *window) +{ + eglCore_->EglContextInit(window); +} + +void PluginRender::UpdateNativeWindowSize(int width, int height) +{ + eglCore_->UpdateSize(width, height); + if (!hasChangeColor_ && !hasDraw_) { + eglCore_->Background(); + } +} + +int32_t PluginRender::HasDraw() +{ + return hasDraw_; +} + +int32_t PluginRender::HasChangedColor() +{ + return hasChangeColor_; +} +} // namespace NativeXComponentSample diff --git a/entry/src/main/cpp/render/plugin_render.h b/entry/src/main/cpp/render/plugin_render.h new file mode 100644 index 0000000..16cffcd --- /dev/null +++ b/entry/src/main/cpp/render/plugin_render.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef NATIVE_XCOMPONENT_PLUGIN_RENDER_H +#define NATIVE_XCOMPONENT_PLUGIN_RENDER_H + +#include +#include +#include "egl_core.h" + +namespace NativeXComponentSample { +class PluginRender { +public: + explicit PluginRender(int64_t& id); + ~PluginRender() + { + if (eglCore_ != nullptr) { + eglCore_->Release(); + delete eglCore_; + eglCore_ = nullptr; + } + } + void ChangeColor(); + void DrawPattern(); + int32_t HasDraw(); + int32_t HasChangedColor(); + void InitNativeWindow(OHNativeWindow* window); + void UpdateNativeWindowSize(int width, int height); +private: + EGLCore* eglCore_; + int64_t id_; + int32_t hasDraw_; + int32_t hasChangeColor_; +}; +} // namespace NativeXComponentSample +#endif // NATIVE_XCOMPONENT_PLUGIN_RENDER_H diff --git a/entry/src/main/cpp/types/libnativerender/Index.d.ts b/entry/src/main/cpp/types/libnativerender/Index.d.ts new file mode 100644 index 0000000..e5bed94 --- /dev/null +++ b/entry/src/main/cpp/types/libnativerender/Index.d.ts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +type XComponentContextStatus = { + hasDraw: boolean, + hasChangeColor: boolean, +}; +export const SetSurfaceId: (id: BigInt) => any; +export const ChangeSurface: (id: BigInt, w: number, h: number) =>any; +export const DrawPattern: (id: BigInt) => any; +export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus +export const ChangeColor: (id: BigInt) => any; +export const DestroySurface: (id: BigInt) => any; diff --git a/entry/src/main/cpp/types/libnativerender/oh-package.json5 b/entry/src/main/cpp/types/libnativerender/oh-package.json5 new file mode 100644 index 0000000..f66d915 --- /dev/null +++ b/entry/src/main/cpp/types/libnativerender/oh-package.json5 @@ -0,0 +1,6 @@ +{ + "name": "libnativerender.so", + "types": "./Index.d.ts", + "version": "", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000..30e8936 --- /dev/null +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +}; diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..9336b63 --- /dev/null +++ b/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import nativeRender from 'libnativerender.so'; + +class MyXComponentController extends XComponentController{ + onSurfaceCreated(surfaceId: string): void { + console.log(`onSurfaceCreated surfaceId: ${surfaceId}`); + nativeRender.SetSurfaceId(BigInt(surfaceId)); + } + onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { + console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`); + nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight); + } + onSurfaceDestroyed(surfaceId: string): void { + console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`); + nativeRender.DestroySurface(BigInt(surfaceId)); + } +} + +@Entry +@Component +struct Index { + @State currentStatus: string = "index"; + xComponentController: XComponentController = new MyXComponentController(); + build() { + Column() { + Row() { + Text('Native XComponent Sample') + .fontSize('24fp') + .fontWeight(500) + .margin({ + left: 24, + top: 12 + }) + } + .margin({ top: 24 }) + .width('100%') + .height(56) + + Column({ space: 10 }) { + XComponent({ + type: XComponentType.SURFACE, + controller: this.xComponentController + }) + Text(this.currentStatus) + .fontSize('24fp') + .fontWeight(500) + } + .onClick(() => { + let surfaceId = this.xComponentController.getXComponentSurfaceId(); + nativeRender.ChangeColor(BigInt(surfaceId)); + let hasChangeColor: boolean = false; + if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { + hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor; + } + if (hasChangeColor) { + this.currentStatus = "change color"; + } + }) + .margin({ + top: 27, + left: 12, + right: 12 + }) + .height('40%') + .width('90%') + Row() { + Button('Draw Star') + .fontSize('16fp') + .fontWeight(500) + .margin({ bottom: 24 }) + .onClick(() => { + let surfaceId = this.xComponentController.getXComponentSurfaceId(); + nativeRender.DrawPattern(BigInt(surfaceId)); + let hasDraw: boolean = false; + if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { + hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw; + } + if (hasDraw) { + this.currentStatus = "draw star"; + } + }) + .width('53.6%') + .height(40) + } + .width('100%') + .justifyContent(FlexAlign.Center) + .alignItems(VerticalAlign.Bottom) + .layoutWeight(1) + } + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 new file mode 100644 index 0000000..643a008 --- /dev/null +++ b/entry/src/main/module.json5 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/color.json b/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/element/string.json b/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000..f945955 --- /dev/null +++ b/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/entry/src/main/resources/base/media/icon.png b/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 zcmX|G1ymHk)?T_}Vd;>R?p|tHQo6fg38|$UVM!6BLrPFWk?s;$LOP{GmJpBl$qoSA!PUg~PA65-S00{{S`XKG6NkG0RgjEntPrmV+?0|00mu7;+5 zrdpa{2QLqPJ4Y{j7=Mrl{BaxrkdY69+c~(w{Fv-v&aR%aEI&JYSeRTLWm!zbv;?)_ ziZB;fwGbbeL5Q}YLx`J$lp~A09KK8t_z}PZ=4ZzgdeKtgoc+o5EvN9A1K1_<>M?MBqb#!ASf&# zEX?<)!RH(7>1P+j=jqG(58}TVN-$psA6K}atCuI!KTJD&FMmH-78ZejBm)0qc{ESp z|LuG1{QnBUJRg_E=h1#XMWt2%fcoN@l7eAS!Es?Q+;XsRNPhiiE=@AqlLkJzF`O18 zbsbSmKN=aaq8k3NFYZfDWpKmM!coBU0(XnL8R{4=i|wi{!uWYM2je{U{B*K2PVdu&=E zTq*-XsEsJ$u5H4g6DIm2Y!DN`>^v|AqlwuCD;w45K0@eqauiqWf7l&o)+YLHm~|L~ z7$0v5mkobriU!H<@mVJHLlmQqzQ3d6Rh_-|%Yy2li*tHO>_vcnuZ7OR_xkAIuIU&x z-|8Y0wj|6|a6_I(v91y%k_kNw6pnkNdxjqG8!%Vz_d%c_!X+6-;1`GC9_FpjoHev5fEV7RhJ>r=mh-jp$fqbqRJ=obwdgLDVP5+s zy1=_DWG0Y-Jb3t^WXmkr(d9~08k-|#Ly zaNOmT(^9tIb&eb4%CzIT zAm3CUtWSr1t4?h1kk#NBi{U|pJslvME{q|_eS^3En>SOqSxyuN1x;Is@8~m?*>}** znrRFArP!K_52RpX*&JHMR<^lVdm8ypJ}0R(SD(51j;6@ni$6bQ+2XL+R^|NnSp5}(kzvMZ^(@4fD_{QVu$(&K6H|C37TG1Am9Re{<<3gd zh@`>;BqkXMW&p0T6rt|iB$)~CvFe(XC)F9WgAZn*0@t$oZo;!*}r@_`h?KKH&6A@3= zISXoQB+~`op>NP-buiA*^0n{@i{_?MRG)&k)c)k_F+-2Lud!S9pc+i`s74NpBCaGF zXN+pHkubw*msGBTY27BKHv)RRh3;nMg4&$fD_6X9Vt~;_4D+5XPH~#Kn-yjcy!$}1 zigv#FNY>TqMhtIBb@UoF!cE~Q8~;!Pek>SQQwHnHuWKoVBosAiOr}q>!>aE*Krc)V zBUMEcJ5NU0g8}-h6i1zpMY9>m4ne?=U2~`w7K7Q0gB_=p@$5K7p6}thw z-~3dMj?YNX2X$lZ+7ngQ$=s}3mizNN@kE%OtB)?c&i~2L55z8^=yz;xMHLmlY>&Q# zJj?!)M#q_SyfkQh)k?j8IfLtB)ZCp|*vf4_B zos?73yd^h-Ac+;?E4*bpf=o*^3x3-`TVjbY4n6!EN10K6o@fxdyps05Vo3PU)otB} z`3kR+2w7_C#8Z!q`J)p{Vh!+m9-UP!$STp+Hb}}#@#_u^SsUQg<}59< zTvH3%XS4G+6FF^(m6bVF&nSUIXcl;nw{=H$%fgeJ>CgDYiLdpDXr{;-AnG z8dvcrHYVMI&`R6;GWekI@Ir3!uo)oz4^{6q0m^}@f2tM9&=YHNi6-?rh0-{+k@cQm zdp`g#YdQn%MDVg2GR>wZ`n2<0l4)9nx1Wfr&!Dvz=bPwU!h2S?ez6MVc5APE4-xLB zi&W9Q8k2@0w!C53g?iAIQ}~p*3O(@zja6KQ=M3zfW*_6o5SwR-)6VBh~m7{^-=MC-owYH5-u40a}a0liho3QZZ5L{bS_xM1)4}19)zTU$$MY zq3eZML1WC{K%YFd`Be0M-rkO^l?h{kM{$2oK1*A@HVJ57*yhDkUF!2WZ&oA4Y-sK( zCY69%#`mBCi6>6uw(x4gbFaP0+FD*JKJ-q!F1E?vLJ+d35!I5d7@^eU?(CS|C^tmI5?lv@s{{*|1F zFg|OzNpZ0hxljdjaW%45O0MOttRrd(Z?h{HYbB-KFUx&9GfFL3b8NwZ$zNu)WbBD` zYkj$^UB5%3Pj1MDr>S2Ejr9pUcgA!;ZG!@{uAy12)vG=*^9-|dNQBc8&`oxBlU~#y zs!anJX&T?57Jdr^sb>e+V`MVfY>Y0ESg7MG<7W0g&bR-ZYzzZ%2H&Etcp zcd6QeXO1D!5A#zM0lx*GH}`M)2~ZFLE;sP^RSB5wVMNfiZXPd(cmO>j=OSA3`o5r& zna(|^jGXbdN7PK)U8b7^zYtYkkeb%<%F~=OqB~kXMQkq}ii|skh@WSRt>5za;cjP0 zZ~nD%6)wzedqE}BMLt~qKwlvTr33))#uP~xyw#*Eaa|DbMQ_%mG0U8numf8)0DX`r zRoG2bM;#g|p-8gWnwRV5SCW0tLjLO&9Z?K>FImeIxlGUgo0Zk`9Qzhj1eco~7XZy+hXc@YF&ZQ=? zn*^1O56yK^x{y}q`j7}blGCx%dydV!c7)g~tJzmHhV=W~jbWRRR{1<^oDK+1clprm zz$eCy7y9+?{E|YgkW~}}iB#I4XoJ*xr8R?i_Hv$=Cof5bo-Nj~f`-DLebH}&0% zfQj9@WGd4;N~Y?mzQsHJTJq6!Qzl^-vwol(+fMt#Pl=Wh#lI5Vmu@QM0=_r+1wHt` z+8WZ~c2}KQQ+q)~2Ki77QvV&`xb|xVcTms99&cD$Zz4+-^R4kvUBxG8gDk7Y`K*)JZ^2rL(+ZWV~%W(@6 z)0bPArG#BROa_PHs~&WplQ_UIrpd)1N1QGPfv!J(Z9jNT#i%H?CE6|pPZb9hJ1JW4 z^q;ft#!HRNV0YgPojzIYT`8LuET2rUe-J|c!9l4`^*;4WtY@Ew@pL>wkjmMgGfN7 ze}}GtmU0@<_#08~I-Suk=^*9GLW=H4xhsml;vAV{%hy5Eegl@!6qKqbG024%n2HHw zCc@ivW_$@5ZoHP70(7D+(`PvgjW1Pd`wsiuv-aCukMrafwDm)B!xXVy*j2opohhoU zcJz%ADmj>i3`-3-$7nQKBQQuGY;2Qt&+(L~C>vSGFj5{Mlv?T_^dql;{zkpe4R1}R z%XfZyQ}wr*sr>jrKgm*PWLjuVc%6&&`Kbf1SuFpHPN&>W)$GmqC;pIoBC`=4-hPY8 zT*>%I2fP}vGW;R=^!1be?ta2UQd2>alOFFbVl;(SQJ4Jk#)4Z0^wpWEVvY4=vyDk@ zqlModi@iVPMC+{?rm=4(n+<;|lmUO@UKYA>EPTS~AndtK^Wy^%#3<;(dQdk3WaUkRtzSMC9}7x2||CNpF#(3T4C)@ z$~RWs`BNABKX|{cmBt>Q=&gkXl&x!!NK_%5hW0LS)Z4PB>%sV?F-{Wyj#s7W%$F{D zXdK^Fp3wvy+48+GP6F_|^PCRx=ddcTO3sG;B23A49~Qaw31SZ0Rc~`r4qqt%#OGW{ zCA_(LG5^N>yzUn&kAgVmxb=EA8s&tBXC}S1CZ(KoW)(%^JjLTPo^fs`Va;`=YlVPgmB$!yB}<(4ym6OeZ3xAJJ#;)2+B%p3P1Wt+d$eo`vz`T zXfUP2))kBDPoscH;Jc7I3NU<({|@wM$&GaDt`n7WLgIY3IA7A6-_R?z8N3mz|}*i z(zl5ot--Oq@f2-nv{X(ujT2T(k1vY_qh93pK@>H-qc%2Xta)IP0Q%zt%bqYgI`o!wv!0QerB`nCN^1n|@$sVOQ!V0teVG!I z_fD%JvfDeT1cK#-{o6Gv7}& zY0#NWin~kVaf$aufV&;63Hbs|`QVZWpDX6IMk1Hj2G}fiH9e-^6u2zf^FIr^BwD<6zjw63+{yUe8PUFvk8v{sJ=R{d#`O!sz`Q13~< zPT$JS(w=yQfU2`zPCNfSw=&zup@DXc(98afjhv@1w_f!m2Z>rMJ19AB&dB%P#Ls3b z=lK7OILM+SQ&VEd=1GN6o&>YVVtIzoZ%=Z_SdqJN2}E43{bE`>w+A;=y->@^k{oCC z$F*WTY&?34;kfyFV?b*Xb1Pq`Z=%OgwEg)Rz)tx=`f%5#w_INP=x&z5!jI;#;N$ma zhO)+MDm;SxOEVL15; zGq(v2pL3&P1Sl)8P*;G-fd{l1QJsv@e@d8)1PK4w2m*M%V3j-V~L^$i|&C@b?D?9tfwE{B^}Z$k8e5FmQ>v7Xz)sG32g9t}YBt zyR$+*_00RmPx+0mW+vVG4mxd(n$(eQf3-w>JPl2UJpafrPaL5@2j}%{VE-) zBI%6Qpj*dsdH<;g!S!avA~bv^0E+ zfyJbSjPb+j;J52U)<|cIcntQBI2T#>2;tOxu{%D?kML476AErF(qN9hPva5Nkc@BF zC-tLF@3ZFb%Kpj)M<{)x*l|*Ia@ECeXo2E4h2f!aV=cHAhi_E_mfUth(sM4^hJq7B zQsGWqdZUm9S%F`$nQ*_#NcuD`&)Ek%_s{&^78{9Hm ztri&rYLOxgFdG>O@+XHy z9#;|&vBCPXH5Mon^I`jSuR$&~ZWtyB67ujzFSj!51>#C}C17~TffQ{c-!QFQkTQ%! zIR^b1`zHx|*1GU?tbBx23weFLz5H?y_Q%N&t$}k?w+``2A=aotj0;2v$~AL z{scF-cL{wsdrmPvf#a9OHyYLcwQD4Kcm)`LLwMh4WT~p29f7M!iafJSU`IV}QY5Wa z(n44-9oA}?J{a+ah*@31WTs#&J#o1`H98#6IQf;Wv0N_!);f&9g7o-k(lW5rWnDUR zQBFIRG+X=6NnsI@mxnwm;tf5;_Uxg?jZ8m-m0}&6+DA!qam(p$mN5R})yA_7m$q@| zFEd|dpS595rxQr-n#GjI5i-AhnUE>Cr;jpCqSrD~EwK_DqI^7%3#p5)%T_od!t3SOmH9MyXeeGO2(UQL;ax|x?Ncixmeo1=$ z{-);Au{*tfzOG?KQ~K|ak8-HQ?`Pekhe2WM(8s{xv-p>Zmu_6{G!-oE$7$mY`MOJorI=+mMx?H;`pr!;fVYz?5~yXBACruWB`Ph zZM}90_<^OBxIhyZ9BW$`>6JvO;%VFpqVr8|7t3~AmxYak6?`Pp#c;**_SYmi`&z23 z`p6_~ePvH)C6x-G9$hgL=eVALq`-AiamN>!3~Lxw&{H(b{B(7xSRm6<3<{%{yXiH# zos5Rv1L+8fUKJLo%P>4I&$}y { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }); + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/List.test.ets b/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..e790e43 --- /dev/null +++ b/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import abilityTest from './XComponentAbility.test' + +export default function testsuite() { + abilityTest() +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/test/XComponentAbility.test.ets b/entry/src/ohosTest/ets/test/XComponentAbility.test.ets new file mode 100644 index 0000000..2333f3d --- /dev/null +++ b/entry/src/ohosTest/ets/test/XComponentAbility.test.ets @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import { describe, it, expect } from '@ohos/hypium'; +import { Driver, ON } from '@ohos.UiTest'; + +const TAG = '[Sample_NDK_XComponent]'; + +export default function abilityTest() { + + describe('ActsAbilityTest', () => { + /** + * 打开应用 + */ + it('StartAbility_001', 0, async (done: Function) => { + console.info(TAG, 'StartAbility_001 begin'); + let driver = Driver.create(); + let abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + try { + await abilityDelegator.startAbility({ + bundleName: 'com.sample.xcomponent', + abilityName: 'EntryAbility' + }); + } catch (exception) { + console.info(TAG, `StartAbility_001 exception = ${JSON.stringify(exception)}`); + expect().assertFail(); + } + await driver.delayMs(1000); + await driver.assertComponentExist(ON.text('Draw Star')); + done(); + console.info(TAG, 'StartAbility_001 end'); + }) + + /** + * 点击按钮,绘制图形,之后点击XComponent改变颜色 + */ + it('DrawShape_001', 2, async () => { + console.info(TAG, 'CreateFiles_001 begin'); + let driver = Driver.create(); + // 判断XComponent onLoad回调执行成功 + await driver.assertComponentExist(ON.text('index')); + // 判断是否有按键 + await driver.assertComponentExist(ON.text('Draw Star')); + let drawStarBtn = await driver.findComponent(ON.text('Draw Star')); + // 点击'Draw Star'按钮 + await drawStarBtn.click(); + await driver.delayMs(1000); + // 判断drawPattern方法已执行 + await driver.assertComponentExist(ON.text('draw star')); + + // 判断是否有XComponent组件 + let xcomponent = await driver.findComponent(ON.id('xcomponent')); + // 点击XComponent组件 + await xcomponent.click(); + await driver.delayMs(1000); + // 判断touch回调已执行 + await driver.assertComponentExist(ON.text('change color')); + console.info(TAG, 'DrawShape_001 end'); + }) + }) +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/testability/TestAbility.ets b/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000..41857ca --- /dev/null +++ b/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; +import Want from '@ohos.app.ability.Want'; +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? ''); + let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs = AbilityDelegatorRegistry.getArguments(); + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + windowStage.loadContent('testability/pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', + JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/entry/src/ohosTest/ets/testability/pages/Index.ets b/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000..5991a3e --- /dev/null +++ b/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(() => { + }) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/entry/src/ohosTest/module.json5 b/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000..a7685a5 --- /dev/null +++ b/entry/src/ohosTest/module.json5 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "default" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/entry/src/ohosTest/resources/base/element/color.json b/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/entry/src/ohosTest/resources/base/element/string.json b/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000..65d8fa5 --- /dev/null +++ b/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/entry/src/ohosTest/resources/base/media/icon.png b/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c GIT binary patch literal 6790 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.default.createHash(u);u.hash=(D,e)=>(0,u.createHash)(e).update(D).digest("hex");u.hashFile=(D,e)=>{if(i.default.existsSync(D))return(0,u.hash)(i.default.readFileSync(D,"utf-8"),e)}}(v);var g={},m={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.Unicode=void 0;class y{}R.Unicode=y,y.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,y.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,y.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(m,"__esModule",{value:!0}),m.JudgeUtil=void 0;const I=R;m.JudgeUtil=class{static isIgnoreChar(u){return"string"==typeof u&&("\t"===u||"\v"===u||"\f"===u||" "===u||" "===u||"\ufeff"===u||"\n"===u||"\r"===u||"\u2028"===u||"\u2029"===u)}static isSpaceSeparator(u){return"string"==typeof u&&I.Unicode.SPACE_SEPARATOR.test(u)}static isIdStartChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||"$"===u||"_"===u||I.Unicode.ID_START.test(u))}static isIdContinueChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||u>="0"&&u<="9"||"$"===u||"_"===u||"‌"===u||"‍"===u||I.Unicode.ID_CONTINUE.test(u))}static isDigitWithoutZero(u){return/[1-9]/.test(u)}static isDigit(u){return"string"==typeof u&&/[0-9]/.test(u)}static isHexDigit(u){return"string"==typeof u&&/[0-9A-Fa-f]/.test(u)}};var N=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(g,"__esModule",{value:!0}),g.parseJsonText=g.parseJsonFile=void 0;const b=N(e),S=N(D),w=N(u),H=m;var x;!function(u){u[u.Char=0]="Char",u[u.EOF=1]="EOF",u[u.Identifier=2]="Identifier"}(x||(x={}));let M,T,V,G,j,J,W="start",U=[],L=0,$=1,k=0,K=!1,z="default",q="'",Z=1;function X(u,D=!1){T=String(u),W="start",U=[],L=0,$=1,k=0,G=void 0,K=D;do{M=Q(),nu[W]()}while("eof"!==M.type);return G}function Q(){for(z="default",j="",q="'",Z=1;;){J=Y();const u=Du[z]();if(u)return u}}function Y(){if(T[L])return String.fromCodePoint(T.codePointAt(L))}function uu(){const u=Y();return"\n"===u?($++,k=0):u?k+=u.length:k++,u&&(L+=u.length),u}g.parseJsonFile=function(u,D=!1,e="utf-8"){const t=b.default.readFileSync(w.default.resolve(u),{encoding:e});try{return X(t,D)}catch(D){if(D instanceof SyntaxError){const e=D.message.split("at");if(2===e.length)throw new Error(`${e[0].trim()}${S.default.EOL}\t at ${u}:${e[1].trim()}`)}throw new Error(`${u} is not in valid JSON/JSON5 format.`)}},g.parseJsonText=X;const Du={default(){switch(J){case"/":return uu(),void(z="comment");case void 0:return uu(),eu("eof")}if(!H.JudgeUtil.isIgnoreChar(J)&&!H.JudgeUtil.isSpaceSeparator(J))return Du[W]();uu()},start(){z="value"},beforePropertyName(){switch(J){case"$":case"_":return j=uu(),void(z="identifierName");case"\\":return uu(),void(z="identifierNameStartEscape");case"}":return eu("punctuator",uu());case'"':case"'":return q=J,uu(),void(z="string")}if(H.JudgeUtil.isIdStartChar(J))return j+=uu(),void(z="identifierName");throw Eu(x.Char,uu())},afterPropertyName(){if(":"===J)return eu("punctuator",uu());throw Eu(x.Char,uu())},beforePropertyValue(){z="value"},afterPropertyValue(){switch(J){case",":case"}":return eu("punctuator",uu())}throw Eu(x.Char,uu())},beforeArrayValue(){if("]"===J)return eu("punctuator",uu());z="value"},afterArrayValue(){switch(J){case",":case"]":return eu("punctuator",uu())}throw Eu(x.Char,uu())},end(){throw Eu(x.Char,uu())},comment(){switch(J){case"*":return uu(),void(z="multiLineComment");case"/":return uu(),void(z="singleLineComment")}throw Eu(x.Char,uu())},multiLineComment(){switch(J){case"*":return uu(),void(z="multiLineCommentAsterisk");case void 0:throw Eu(x.Char,uu())}uu()},multiLineCommentAsterisk(){switch(J){case"*":return void uu();case"/":return uu(),void(z="default");case void 0:throw Eu(x.Char,uu())}uu(),z="multiLineComment"},singleLineComment(){switch(J){case"\n":case"\r":case"\u2028":case"\u2029":return uu(),void(z="default");case void 0:return uu(),eu("eof")}uu()},value(){switch(J){case"{":case"[":return eu("punctuator",uu());case"n":return uu(),tu("ull"),eu("null",null);case"t":return uu(),tu("rue"),eu("boolean",!0);case"f":return uu(),tu("alse"),eu("boolean",!1);case"-":case"+":return"-"===uu()&&(Z=-1),void(z="numerical");case".":case"0":case"I":case"N":return void(z="numerical");case'"':case"'":return q=J,uu(),j="",void(z="string")}if(void 0===J||!H.JudgeUtil.isDigitWithoutZero(J))throw Eu(x.Char,uu());z="numerical"},numerical(){switch(J){case".":return j=uu(),void(z="decimalPointLeading");case"0":return j=uu(),void(z="zero");case"I":return uu(),tu("nfinity"),eu("numeric",Z*(1/0));case"N":return uu(),tu("aN"),eu("numeric",NaN)}if(void 0!==J&&H.JudgeUtil.isDigitWithoutZero(J))return j=uu(),void(z="decimalInteger");throw Eu(x.Char,uu())},zero(){switch(J){case".":case"e":case"E":return void(z="decimal");case"x":case"X":return j+=uu(),void(z="hexadecimal")}return eu("numeric",0)},decimalInteger(){switch(J){case".":case"e":case"E":return void(z="decimal")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimal(){switch(J){case".":j+=uu(),z="decimalFraction";break;case"e":case"E":j+=uu(),z="decimalExponent"}},decimalPointLeading(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalFraction");throw Eu(x.Char,uu())},decimalFraction(){switch(J){case"e":case"E":return j+=uu(),void(z="decimalExponent")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimalExponent(){switch(J){case"+":case"-":return j+=uu(),void(z="decimalExponentSign")}if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentSign(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentInteger(){if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},hexadecimal(){if(H.JudgeUtil.isHexDigit(J))return j+=uu(),void(z="hexadecimalInteger");throw Eu(x.Char,uu())},hexadecimalInteger(){if(!H.JudgeUtil.isHexDigit(J))return eu("numeric",Z*Number(j));j+=uu()},identifierNameStartEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":break;default:if(!H.JudgeUtil.isIdStartChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},identifierName(){switch(J){case"$":case"_":case"‌":case"‍":return void(j+=uu());case"\\":return uu(),void(z="identifierNameEscape")}if(!H.JudgeUtil.isIdContinueChar(J))return eu("identifier",j);j+=uu()},identifierNameEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":case"‌":case"‍":break;default:if(!H.JudgeUtil.isIdContinueChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},string(){switch(J){case"\\":return uu(),void(j+=function(){const u=Y(),D=function(){switch(Y()){case"b":return uu(),"\b";case"f":return uu(),"\f";case"n":return uu(),"\n";case"r":return uu(),"\r";case"t":return uu(),"\t";case"v":return uu(),"\v"}return}();if(D)return D;switch(u){case"0":if(uu(),H.JudgeUtil.isDigit(Y()))throw Eu(x.Char,uu());return"\0";case"x":return uu(),function(){let u="",D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());if(u+=uu(),D=Y(),!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());return u+=uu(),String.fromCodePoint(parseInt(u,16))}();case"u":return uu(),ru();case"\n":case"\u2028":case"\u2029":return uu(),"";case"\r":return uu(),"\n"===Y()&&uu(),""}if(void 0===u||H.JudgeUtil.isDigitWithoutZero(u))throw Eu(x.Char,uu());return uu()}());case'"':case"'":if(J===q){const u=eu("string",j);return uu(),u}return void(j+=uu());case"\n":case"\r":case void 0:throw Eu(x.Char,uu());case"\u2028":case"\u2029":!function(u){console.warn(`JSON5: '${Fu(u)}' in strings is not valid ECMAScript; consider escaping.`)}(J)}j+=uu()}};function eu(u,D){return{type:u,value:D,line:$,column:k}}function tu(u){for(const D of u){if(Y()!==D)throw Eu(x.Char,uu());uu()}}function ru(){let u="",D=4;for(;D-- >0;){const D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());u+=uu()}return String.fromCodePoint(parseInt(u,16))}const nu={start(){if("eof"===M.type)throw Eu(x.EOF);iu()},beforePropertyName(){switch(M.type){case"identifier":case"string":return V=M.value,void(W="afterPropertyName");case"punctuator":return void Cu();case"eof":throw Eu(x.EOF)}},afterPropertyName(){if("eof"===M.type)throw Eu(x.EOF);W="beforePropertyValue"},beforePropertyValue(){if("eof"===M.type)throw Eu(x.EOF);iu()},afterPropertyValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforePropertyName");case"}":Cu()}},beforeArrayValue(){if("eof"===M.type)throw Eu(x.EOF);"punctuator"!==M.type||"]"!==M.value?iu():Cu()},afterArrayValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforeArrayValue");case"]":Cu()}},end(){}};function iu(){const u=function(){let u;switch(M.type){case"punctuator":switch(M.value){case"{":u={};break;case"[":u=[]}break;case"null":case"boolean":case"numeric":case"string":u=M.value}return u}();if(K&&"object"==typeof u&&(u._line=$,u._column=k),void 0===G)G=u;else{const D=U[U.length-1];Array.isArray(D)?K&&"object"!=typeof u?D.push({value:u,_line:$,_column:k}):D.push(u):D[V]=K&&"object"!=typeof u?{value:u,_line:$,_column:k}:u}!function(u){if(u&&"object"==typeof u)U.push(u),W=Array.isArray(u)?"beforeArrayValue":"beforePropertyName";else{const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}}(u)}function Cu(){U.pop();const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}function Fu(u){const D={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(D[u])return D[u];if(u<" "){const D=u.charCodeAt(0).toString(16);return`\\x${`00${D}`.substring(D.length)}`}return u}function Eu(u,D){let e="";switch(u){case x.Char:e=void 0===D?`JSON5: invalid end of input at ${$}:${k}`:`JSON5: invalid character '${Fu(D)}' at ${$}:${k}`;break;case x.EOF:e=`JSON5: invalid end of input at ${$}:${k}`;break;case x.Identifier:k-=5,e=`JSON5: invalid identifier character at ${$}:${k}`}const t=new Au(e);return t.lineNumber=$,t.columnNumber=k,t}class Au extends SyntaxError{}var ou={},au=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),cu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),su=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&au(D,u,e);return cu(D,u),D},lu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(ou,"__esModule",{value:!0}),ou.isFileExists=ou.offlinePluginConversion=ou.executeCommand=ou.getNpmPath=ou.hasNpmPackInPaths=void 0;const Bu=r,du=lu(e),fu=su(u),_u=i,pu=l;ou.hasNpmPackInPaths=function(u,D){try{return require.resolve(u,{paths:[...D]}),!0}catch(u){return!1}},ou.getNpmPath=function(){const u=process.execPath;return fu.join(fu.dirname(u),_u.NPM_TOOL)},ou.executeCommand=function(u,D,e){0!==(0,Bu.spawnSync)(u,D,e).status&&(0,pu.logErrorAndExit)(`Error: ${u} ${D} execute failed.See above for details.`)},ou.offlinePluginConversion=function(u,D){return D.startsWith("file:")||D.endsWith(".tgz")?fu.resolve(u,_u.HVIGOR,D.replace("file:","")):D},ou.isFileExists=function(u){return du.default.existsSync(u)&&du.default.statSync(u).isFile()};var Ou=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),hu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),Pu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&Ou(D,u,e);return hu(D,u),D},vu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(P,"__esModule",{value:!0});var gu=P.initProjectWorkSpace=void 0;const mu=Pu(e),Ru=vu(D),yu=Pu(u),Iu=v,Nu=i,bu=g,Su=l,wu=ou;let Hu,xu,Mu;function Tu(u,D,e){return void 0!==e.dependencies&&(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,D.dependencies[u])===yu.normalize(e.dependencies[u])}function Vu(){const u=yu.join(Mu,Nu.WORK_SPACE);if((0,Su.logInfoPrintConsole)("Hvigor cleaning..."),!mu.existsSync(u))return;const D=mu.readdirSync(u);if(!D||0===D.length)return;const e=yu.resolve(Mu,"node_modules","@ohos","hvigor","bin","hvigor.js");mu.existsSync(e)&&(0,wu.executeCommand)(process.argv[0],[e,"--stop-daemon"],{});try{D.forEach((D=>{mu.rmSync(yu.resolve(u,D),{recursive:!0})}))}catch(D){(0,Su.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${u}.`)}}gu=P.initProjectWorkSpace=function(){if(Hu=function(){const u=yu.resolve(Nu.HVIGOR_PROJECT_WRAPPER_HOME,Nu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);mu.existsSync(u)||(0,Su.logErrorAndExit)(`Error: Hvigor config file ${u} does not exist.`);return(0,bu.parseJsonFile)(u)}(),Mu=function(u){let D;D=function(u){let D=u.hvigorVersion;if(D.startsWith("file:")||D.endsWith(".tgz"))return!1;const e=u.dependencies,t=Object.getOwnPropertyNames(e);for(const u of t){const D=e[u];if(D.startsWith("file:")||D.endsWith(".tgz"))return!1}if(1===t.length&&"@ohos/hvigor-ohos-plugin"===t[0])return D>"2.5.0";return!1}(u)?function(u){let D=`${Nu.HVIGOR_ENGINE_PACKAGE_NAME}@${u.hvigorVersion}`;const e=u.dependencies;if(e){Object.getOwnPropertyNames(e).sort().forEach((u=>{D+=`,${u}@${e[u]}`}))}return(0,Iu.hash)(D)}(u):(0,Iu.hash)(process.cwd());return yu.resolve(Ru.default.homedir(),".hvigor","project_caches",D)}(Hu),xu=function(){const u=yu.resolve(Mu,Nu.WORK_SPACE,Nu.DEFAULT_PACKAGE_JSON);return mu.existsSync(u)?(0,bu.parseJsonFile)(u):{dependencies:{}}}(),!(0,wu.hasNpmPackInPaths)(Nu.HVIGOR_ENGINE_PACKAGE_NAME,[yu.join(Mu,Nu.WORK_SPACE)])||(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion)!==xu.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function u(u){const D=null==u?void 0:u.dependencies;return void 0===D?0:Object.getOwnPropertyNames(D).length}const D=u(Hu),e=u(xu);if(D+1!==e)return!1;for(const u in null==Hu?void 0:Hu.dependencies)if(!(0,wu.hasNpmPackInPaths)(u,[yu.join(Mu,Nu.WORK_SPACE)])||!Tu(u,Hu,xu))return!1;return!0}()){Vu();try{!function(){(0,Su.logInfoPrintConsole)("Hvigor installing...");for(const u in Hu.dependencies)Hu.dependencies[u]&&(Hu.dependencies[u]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.dependencies[u]));const u={dependencies:{...Hu.dependencies}};u.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion);const D=yu.join(Mu,Nu.WORK_SPACE);try{mu.mkdirSync(D,{recursive:!0});const e=yu.resolve(D,Nu.DEFAULT_PACKAGE_JSON);mu.writeFileSync(e,JSON.stringify(u))}catch(u){(0,Su.logErrorAndExit)(u)}(function(){const u=["config","set","store-dir",Nu.HVIGOR_PNPM_STORE_PATH],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)})(),function(){const u=["install"],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)}(),(0,Su.logInfoPrintConsole)("Hvigor install success.")}()}catch(u){Vu()}}return Mu};var Gu={};!function(t){var C=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),F=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),E=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&C(D,u,e);return F(D,u),D},A=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(t,"__esModule",{value:!0}),t.executeInstallPnpm=t.isPnpmInstalled=t.environmentHandler=t.checkNpmConifg=t.PNPM_VERSION=void 0;const o=r,a=E(e),c=A(D),s=E(u),B=i,d=l,f=ou;t.PNPM_VERSION="7.30.0",t.checkNpmConifg=function(){const u=s.resolve(B.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),D=s.resolve(c.default.homedir(),".npmrc");if((0,f.isFileExists)(u)||(0,f.isFileExists)(D))return;const e=(0,f.getNpmPath)(),t=(0,o.spawnSync)(e,["config","get","prefix"],{cwd:B.HVIGOR_PROJECT_ROOT_DIR});if(0!==t.status||!t.stdout)return void(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const r=s.resolve(`${t.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,f.isFileExists)(r)||(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},t.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},t.isPnpmInstalled=function(){return!!a.existsSync(B.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,f.hasNpmPackInPaths)("pnpm",[B.HVIGOR_WRAPPER_TOOLS_HOME])},t.executeInstallPnpm=function(){(0,d.logInfoPrintConsole)(`Installing pnpm@${t.PNPM_VERSION}...`);const u=(0,f.getNpmPath)();!function(){const u=s.resolve(B.HVIGOR_WRAPPER_TOOLS_HOME,B.DEFAULT_PACKAGE_JSON);try{a.existsSync(B.HVIGOR_WRAPPER_TOOLS_HOME)||a.mkdirSync(B.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const D={dependencies:{}};D.dependencies[B.PNPM]=t.PNPM_VERSION,a.writeFileSync(u,JSON.stringify(D))}catch(D){(0,d.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${u} failed.`)}}(),(0,f.executeCommand)(u,["install","pnpm"],{cwd:B.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,d.logInfoPrintConsole)("Pnpm install success.")}}(Gu),function(){Gu.checkNpmConifg(),Gu.environmentHandler(),Gu.isPnpmInstalled()||Gu.executeInstallPnpm();const D=gu();_(u.join(D,i.WORK_SPACE))}(); \ No newline at end of file diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..f3cb9f1 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/hvigorw b/hvigorw new file mode 100644 index 0000000..4f0aa94 --- /dev/null +++ b/hvigorw @@ -0,0 +1,54 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME="`pwd -P`" +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +#NODE_OPTS="--max-old-space-size=4096" + +fail() { + echo "$*" + exit 1 +} + +set_executable_node() { + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ -x "$EXECUTABLE_NODE" ]; then + return + fi + + EXECUTABLE_NODE="${NODE_HOME}/node" + if [ -x "$EXECUTABLE_NODE" ]; then + return + fi + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ]; then + set_executable_node +else + EXECUTABLE_NODE="node" + command -v ${EXECUTABLE_NODE} &> /dev/null || fail "ERROR: NODE_HOME not set and 'node' command not found" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ]; then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +if [ -z "${NODE_OPTS}" ]; then + NODE_OPTS="--" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" "${NODE_OPTS}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/hvigorw.bat b/hvigorw.bat new file mode 100644 index 0000000..b5eecf5 --- /dev/null +++ b/hvigorw.bat @@ -0,0 +1,54 @@ +@rem +@rem ---------------------------------------------------------------------------- +@rem Hvigor startup script for Windows, version 1.0.0 +@rem +@rem Required ENV vars: +@rem ------------------ +@rem NODE_HOME - location of a Node home dir +@rem or +@rem Add %NODE_HOME%/bin to the PATH environment variable +@rem ---------------------------------------------------------------------------- +@rem +@echo off + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe +@rem set NODE_OPTS="--max-old-space-size=4096" + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +if not defined NODE_OPTS set NODE_OPTS="--" + +@rem Find node.exe +if defined NODE_HOME ( + set NODE_HOME=%NODE_HOME:"=% + set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% +) + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" ( + "%NODE_EXE%" "%NODE_OPTS%" "%WRAPPER_MODULE_PATH%" %* +) else if exist "%NODE_EXE_PATH%" ( + "%NODE_EXE%" "%NODE_OPTS%" "%WRAPPER_MODULE_PATH%" %* +) else ( + echo. + echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. + echo. + echo Please set the NODE_HOME variable in your environment to match the + echo location of your NodeJs installation. +) + +if "%ERRORLEVEL%" == "0" ( + if "%OS%" == "Windows_NT" endlocal +) else ( + exit /b %ERRORLEVEL% +) \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..9d01538 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,14 @@ +{ + "name": "ndkxcomponentv2", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.16", + "@ohos/hamock": "1.0.0" + } +} diff --git a/ohosTest.md b/ohosTest.md new file mode 100644 index 0000000..5469eac --- /dev/null +++ b/ohosTest.md @@ -0,0 +1,9 @@ +# NdkXComponent 测试用例归档 + +## 用例表 + +|测试功能|预置条件|输入|预期输出|是否自动|测试结果| +|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------|--------------------------------| +| 拉起应用 | 设备正常运行 | |成功拉起应用|是| Pass | +| 绘制图形 | 位于首页 | 1、点击**Draw Star** | 1、页面显示出一个五角星 | 是 | Pass | +| 响应触摸事件 | 位于首页,且已经显示了五角星 | 1、点击XComponent区域(灰色背景部分) | 1、页面中的五角星改变颜色 | 是 | Pass | \ No newline at end of file diff --git a/screenshots/device/changeColor.png b/screenshots/device/changeColor.png new file mode 100644 index 0000000000000000000000000000000000000000..07586fceb2d038482dd984b921a7c80871824bb8 GIT binary patch literal 31936 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 M+y{mEjQ?)}03HCzkN^Mx literal 0 HcmV?d00001 diff --git a/screenshots/device/main.png b/screenshots/device/main.png new file mode 100644 index 0000000000000000000000000000000000000000..a51df8c9cf759098882721219d5c9f4b5acfb0c8 GIT binary patch literal 28584 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