diff --git a/ArkUIKit/ArkTSXComponent/.gitignore b/ArkUIKit/ArkTSXComponent/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fbabf771011fe78f9919db0b1195ab6cadffc2b0 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/.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/ArkUIKit/ArkTSXComponent/AppScope/app.json5 b/ArkUIKit/ArkTSXComponent/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..efa5c58de2e89c10d805722de050a27f97ce2850 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/AppScope/resources/base/element/string.json b/ArkUIKit/ArkTSXComponent/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..23797971100a06869721b629195f6256412b41a5 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ArkTSXComponent" + } + ] +} diff --git a/ArkUIKit/ArkTSXComponent/AppScope/resources/base/media/app_icon.png b/ArkUIKit/ArkTSXComponent/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/AppScope/resources/base/media/app_icon.png differ diff --git a/ArkUIKit/ArkTSXComponent/README_zh.md b/ArkUIKit/ArkTSXComponent/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..cff0d355a5b019f8dae87bf9544548ad9bd1b296 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/build-profile.json5 b/ArkUIKit/ArkTSXComponent/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f7f2bfddf1cfd2205e6917ed106c4c5953e64e7f --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * 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)", + "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/ArkUIKit/ArkTSXComponent/entry/.gitignore b/ArkUIKit/ArkTSXComponent/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/ArkUIKit/ArkTSXComponent/entry/build-profile.json5 b/ArkUIKit/ArkTSXComponent/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..177757fad3d380f8b55ccbb1bd00ebf112561d7e --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/build-profile.json5 @@ -0,0 +1,35 @@ +/* + * 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"] + } + }, + "targets": [ + { + "name": "default", + "runtimeOS": "HarmonyOS" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/ArkTSXComponent/entry/hvigorfile.ts b/ArkUIKit/ArkTSXComponent/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/oh-package.json5 b/ArkUIKit/ArkTSXComponent/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..811a0b10e04f84946f75b69e8b72f3bf7ec35000 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/CMakeLists.txt b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9364964d1d61f571e57148f854e6fb6dddfb9630 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/common/common.h b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/common/common.h new file mode 100644 index 0000000000000000000000000000000000000000..c7760109da75f11204bd359ac3a65900b6749872 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/manager/plugin_manager.cpp b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/manager/plugin_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8942dc87ce7e5bd2eba0a9e5545998fa436f3d41 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/manager/plugin_manager.h b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/manager/plugin_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..3734c98239e70c0671f52a57816b51ab93ffc965 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/napi_init.cpp b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a96bc999f1c9231cda4c20c2a23f5faf302e683 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/egl_core.cpp b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/egl_core.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7112fd06276e9e1829d515394e2f4abf3bb9af33 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/egl_core.h b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/egl_core.h new file mode 100644 index 0000000000000000000000000000000000000000..0f25ca0ccfea46959dba9161f1c6abf064904209 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/plugin_render.cpp b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/plugin_render.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c19c5f4b990666b8eda3ddd2771ddd4fb510cd9d --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/plugin_render.h b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/render/plugin_render.h new file mode 100644 index 0000000000000000000000000000000000000000..16cffcd7a35264f127b1544bd94f5db9d0dcfa23 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/types/libnativerender/Index.d.ts b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/types/libnativerender/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5bed9410ed867df3bc3131af1db29c969e6b652 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/types/libnativerender/oh-package.json5 b/ArkUIKit/ArkTSXComponent/entry/src/main/cpp/types/libnativerender/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f66d915ed293f4db1248f1c46aaf10ff0a7c7b34 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/ets/entryability/EntryAbility.ets b/ArkUIKit/ArkTSXComponent/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..30e8936fcd5927a4e8934503305318b68c2049ae --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/ets/pages/Index.ets b/ArkUIKit/ArkTSXComponent/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..9336b63caa78480bf773b11bc3853ccce67f1633 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/module.json5 b/ArkUIKit/ArkTSXComponent/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..643a0085e7cffb4afde1a2c8284ff9ce832a0a74 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/element/color.json b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/element/string.json b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/media/icon.png b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/media/icon.png differ diff --git a/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/profile/main_pages.json b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/ArkUIKit/ArkTSXComponent/entry/src/main/resources/en_US/element/string.json b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/en_US/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/ArkUIKit/ArkTSXComponent/entry/src/main/resources/zh_CN/element/string.json b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..597ecf95e61d7e30367c22fe2f8638008361b044 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..585a000d65aaf0cc128e3e9edde5d7bbd9cb7d88 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/TestRunner/OpenHarmonyTestRunner.ts @@ -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 hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +let abilityDelegator = undefined; +let abilityDelegatorArguments = undefined; + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments(); + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator(); + let testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'; + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback); + let cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName; + let debug = abilityDelegatorArguments.parameters['-D']; + if (debug == 'true') { + cmd += ' -D'; + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + 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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/test/List.test.ets b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..e790e436bfe740d121e5b33d150ef2ab22da6158 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/test/XComponentAbility.test.ets b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/test/XComponentAbility.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..2333f3d3d3e135201d1345c2f6defdd21e36f6c6 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/testability/TestAbility.ets b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..41857cadc2b07849a89d6cda4fdccd0c6ee23589 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/testability/pages/Index.ets b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..5991a3ef4b41ec78e4977a7d65d84a589ee1eb20 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/module.json5 b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..a7685a59ea27672ae8074c984c4699bd73f36a5b --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/element/color.json b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/element/string.json b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/media/icon.png b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/profile/test_pages.json b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..b7e7343cacb32ce982a45e76daad86e435e054fe --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "testability/pages/Index" + ] +} diff --git a/ArkUIKit/ArkTSXComponent/hvigor/hvigor-config.json5 b/ArkUIKit/ArkTSXComponent/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9b4f0fca99368e3cd711b6cb4b45c083cc6b3e06 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/hvigor/hvigor-config.json5 @@ -0,0 +1,36 @@ +/* + * 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. + */ + +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "default", /* Define the build analyze mode. Value: [ "default" | "verbose" | false ]. Default: "default" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 4096 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process */ + } +} \ No newline at end of file diff --git a/ArkUIKit/ArkTSXComponent/hvigorfile.ts b/ArkUIKit/ArkTSXComponent/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3cb9f1a87a81687554a76283af8df27d8bda775 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/oh-package.json5 b/ArkUIKit/ArkTSXComponent/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..699504ce5f1b6eabe030897b5c38f3ad77c4c5a3 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/oh-package.json5 @@ -0,0 +1,15 @@ +{ + "modelVersion": "5.0.0", + "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" + } +} \ No newline at end of file diff --git a/ArkUIKit/ArkTSXComponent/ohosTest.md b/ArkUIKit/ArkTSXComponent/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..5469eacbec87545aec4cfaa42c141af8859124e1 --- /dev/null +++ b/ArkUIKit/ArkTSXComponent/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/ArkUIKit/ArkTSXComponent/screenshots/device/changeColor.png b/ArkUIKit/ArkTSXComponent/screenshots/device/changeColor.png new file mode 100644 index 0000000000000000000000000000000000000000..07586fceb2d038482dd984b921a7c80871824bb8 Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/screenshots/device/changeColor.png differ diff --git a/ArkUIKit/ArkTSXComponent/screenshots/device/drawStar.png b/ArkUIKit/ArkTSXComponent/screenshots/device/drawStar.png new file mode 100644 index 0000000000000000000000000000000000000000..e8be339ee8a16dae2c1ce350747f8e1f09782692 Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/screenshots/device/drawStar.png differ diff --git a/ArkUIKit/ArkTSXComponent/screenshots/device/main.png b/ArkUIKit/ArkTSXComponent/screenshots/device/main.png new file mode 100644 index 0000000000000000000000000000000000000000..a51df8c9cf759098882721219d5c9f4b5acfb0c8 Binary files /dev/null and b/ArkUIKit/ArkTSXComponent/screenshots/device/main.png differ