diff --git a/ArkUIKit/NdkKeyEvent/.gitignore b/ArkUIKit/NdkKeyEvent/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/AppScope/app.json5 b/ArkUIKit/NdkKeyEvent/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..41737beb8789543a52f54841c9a0414327c7fd4d --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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.sample.ndkkeyevent", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/ArkUIKit/NdkKeyEvent/AppScope/resources/base/element/string.json b/ArkUIKit/NdkKeyEvent/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..d71e5ead1ce20b4ac3384d18e6d104f1ea4d83b4 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "MyApplication" + } + ] +} diff --git a/ArkUIKit/NdkKeyEvent/AppScope/resources/base/media/app_icon.png b/ArkUIKit/NdkKeyEvent/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/ArkUIKit/NdkKeyEvent/AppScope/resources/base/media/app_icon.png differ diff --git a/ArkUIKit/NdkKeyEvent/README_zh.md b/ArkUIKit/NdkKeyEvent/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..f67f2b32e2cde72c2e43d83276642d5c33825f3b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/README_zh.md @@ -0,0 +1,68 @@ +# ArkUI指南文档示例 + +### 介绍 + +本示例通过使用[ArkUI指南文档](https://gitee.com/openharmony/docs/tree/master/zh-cn/application-dev/reference)中各场景的开发示例,展示在工程中,帮助开发者更好地理解ArkUI提供的组件及组件属性并合理使用。该工程中展示的代码详细描述可查如下链接: + +1. [按键事件](https://gitcode.com/openharmony/docs/blob/master/en/application-dev/reference/apis-arkui/native__key_event_8h.md)。 +### 效果预览 + +| 首页 | +|------------------------------------| +| ![](screenshots/device/image1.jpg) | + +### 使用说明 + +1. 在首页可以查看通过按键事件CAPI接口实现监听按键事件示例。 + +2. 通过自动测试框架可进行测试及维护。 + +### 工程目录 +``` +entry/src/main/ets/ +|---cpp +| |---types +| |---CMakeLists.txt +| |---container.cpp +| |---container.h +| |---init.cpp +| |---manager.cpp // native创建与对接 +| |---manager.h // manager头文件 +| |---key_event_handler.cpp // 通过按键事件CAPI接口实现相关示例代码 +| |---key_event_handler.h +| |---napi_init.cpp +|---ets +| |---pages +| | |---Index.ets // 应用主页面 +entry/src/ohosTest/ +|---ets +| |---index.test.ets // 示例代码测试代码 +``` + +### 相关权限 + +不涉及。 + +### 依赖 + +不涉及。 + +### 约束与限制 + +1.本示例仅支持标准系统上运行, 支持设备:RK3568。 + +2.本示例为Stage模型,支持API20版本SDK,版本号:6.0.0.41,镜像版本号:OpenHarmony_6.0.0.41。 + +3.本示例需要使用DevEco Studio 5.0.5 Release (Build Version: 5.0.13.200, built on May 13, 2025)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +```` +git init +git config core.sparsecheckout true +echo code/DocsSample/ArkUISample/NdkKeyEvent > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +```` \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/build-profile.json5 b/ArkUIKit/NdkKeyEvent/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ba221bc20f11c76a63dc7acb8128ef1bbe905134 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/build-profile.json5 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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", + "compileSdkVersion": 20, + "compatibleSdkVersion": 20, + "runtimeOS": "OpenHarmony", + "buildOption": { + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "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/NdkKeyEvent/code-linter.json5 b/ArkUIKit/NdkKeyEvent/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..28586467ee7a761c737d8654a73aed6fddbc3c71 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/code-linter.json5 @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/.gitignore b/ArkUIKit/NdkKeyEvent/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/build-profile.json5 b/ArkUIKit/NdkKeyEvent/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..21cff554210e7ead0baebebb58a5da1a84dd5fd8 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/build-profile.json5 @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 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", "armeabi-v7a"] + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/hvigorfile.ts b/ArkUIKit/NdkKeyEvent/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/NdkKeyEvent/entry/obfuscation-rules.txt b/ArkUIKit/NdkKeyEvent/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/oh-package.json5 b/ArkUIKit/NdkKeyEvent/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5d993e5251fd56950970aa593aefef1b8d71e976 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/CMakeLists.txt b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2f4c2d326ddc8784490d522dbfd0791b0831794e --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(MyApplication) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(nativeNode SHARED init.cpp manager.cpp container.cpp key_event_handler.cpp) +target_link_libraries(nativeNode PUBLIC libace_napi.z.so) +target_link_libraries(nativeNode PUBLIC libace_napi.z.so) +target_link_libraries(nativeNode PUBLIC libace_ndk.z.so) +target_link_libraries(nativeNode PUBLIC libnative_drawing.so) +target_link_libraries(nativeNode PUBLIC libhilog_ndk.z.so) +target_link_libraries(nativeNode PUBLIC libhilog_ndk.z.so libpixelmap.so) diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.cpp b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7f4b15dc54c259a62b0ec1ce7411ff21c4e7bbc --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2025 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 "container.h" + +#include +#include +#include +#include +#include + + +namespace NativeXComponentSample { +namespace { +void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB"); + if ((component == nullptr) || (window == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: component or window is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: Unable to get XComponent id"); + return; + } +} + +void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChangedCB"); + if ((component == nullptr) || (window == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChangedCB: component or window is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChangedCB: Unable to get XComponent id"); + return; + } + std::string id(idStr); + auto container = Container::GetInstance(id); + if (container != nullptr) { + container->OnSurfaceChanged(component, window); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "surface changed"); + } +} + +void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB"); + if ((component == nullptr) || (window == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB: component or window is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + Container::Release(id); +} + +void DispatchTouchEventCB(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB"); + if ((component == nullptr) || (window == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB: component or window is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + Container* render = Container::GetInstance(id); + if (render != nullptr) { + render->OnTouchEvent(component, window); + } +} + +void DispatchMouseEventCB(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchMouseEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto render = Container::GetInstance(id); + if (render != nullptr) { + render->OnMouseEvent(component, window); + } +} + +void DispatchHoverEventCB(OH_NativeXComponent* component, bool isHover) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchHoverEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto container = Container::GetInstance(id); + if (container != nullptr) { + container->OnHoverEvent(component, isHover); + } +} +} // namespace + +std::unordered_map Container::instance_; + +Container::Container(const std::string& id) +{ + this->id_ = id; +} + +Container* Container::GetInstance(const std::string& id) +{ + if (instance_.find(id) == instance_.end()) { + Container* instance = new Container(id); + instance_[id] = instance; + return instance; + } else { + return instance_[id]; + } +} + +void Container::Release(const std::string& id) +{ + if (instance_.find(id) != instance_.end()) { + instance_[id] = nullptr; + } +} + +void Container::OnSurfaceChanged(OH_NativeXComponent* component, void* window) +{ + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChanged: Unable to get XComponent id"); + return; + } + double offsetX; + double offsetY; + OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset", + "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY); + uint64_t width; + uint64_t height; + OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); +} + +void Container::OnTouchEvent(OH_NativeXComponent* component, void* window) +{ + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "DispatchTouchEventCB: Unable to get XComponent id"); + return; + } + OH_NativeXComponent_TouchEvent touchEvent; + OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); + std::string id(idStr); + Container* container = Container::GetInstance(id); + float tiltX = 0.0f; + float tiltY = 0.0f; + OH_NativeXComponent_TouchPointToolType toolType = + OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN; + OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); + OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); + OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", + "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY); +} + +void Container::RegisterCallback(OH_NativeXComponent* nativeXComponent) +{ + containerCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; + containerCallback_.OnSurfaceChanged = OnSurfaceChangedCB; + containerCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; + containerCallback_.DispatchTouchEvent = DispatchTouchEventCB; + OH_NativeXComponent_RegisterCallback(nativeXComponent, &containerCallback_); + + mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; + mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; + OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &mouseCallback_); + OH_NativeXComponent_RegisterOnTouchInterceptCallback( + nativeXComponent, [](OH_NativeXComponent*, ArkUI_UIInputEvent*) -> HitTestMode { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "callback", + "OH_NativeXComponent_RegisterOnTouchInterceptCallback"); + return HitTestMode::HTM_TRANSPARENT; + }); +} + +void Container::OnMouseEvent(OH_NativeXComponent* component, void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Container", "OnMouseEvent"); + OH_NativeXComponent_MouseEvent mouseEvent; + int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); + if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Container", + "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d", mouseEvent.x, + mouseEvent.y, mouseEvent.action, mouseEvent.button); + } else { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Container", "GetMouseEvent error"); + } +} + +void Container::OnHoverEvent(OH_NativeXComponent* component, bool isHover) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Container", "OnHoverEvent isHover_ = %{public}d", isHover); +} +} // namespace NativeXComponentSample diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.h b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.h new file mode 100644 index 0000000000000000000000000000000000000000..8290fa3d85fe4c58fe153e981337456dfdf03c54 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/container.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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 MY_APPLICATION_CONTAINER_H +#define MY_APPLICATION_CONTAINER_H + +#include +#include +#include +#include + + +const unsigned int LOG_PRINT_DOMAIN = 0xFF00; + +namespace NativeXComponentSample { + +class Container { +public: + explicit Container(const std::string& id); + ~Container() = default; + static Container* GetInstance(const std::string& id); + static void Release(const std::string& id); + void Export(napi_env env, napi_value exports); + void OnSurfaceChanged(OH_NativeXComponent* component, void* window); + void OnTouchEvent(OH_NativeXComponent* component, void* window); + void OnMouseEvent(OH_NativeXComponent* component, void* window); + void OnHoverEvent(OH_NativeXComponent* component, bool isHover); + void OnKeyEvent(OH_NativeXComponent* component, void* window); + void RegisterCallback(OH_NativeXComponent* nativeXComponent); + +public: + static std::unordered_map instance_; + std::string id_; + +private: + OH_NativeXComponent_Callback containerCallback_; + OH_NativeXComponent_MouseEvent_Callback mouseCallback_; +}; + +} // namespace NativeXComponentSample +#endif // MY_APPLICATION_CONTAINER_H diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/init.cpp b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3c9615ef6a7c3a19cb21ca0e1eac5d324247c116 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/init.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025 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 "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[] = { + { "getContext", nullptr, Manager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "createNativeNode", nullptr, Manager::CreateNativeNode, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "upDateNativeNode", nullptr, Manager::UpdateNativeNode, 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; + } + + Manager::GetInstance()->Export(env, exports); + return exports; +} +EXTERN_C_END + +static napi_module nativeNodeModule = { .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "nativeNode", + .nm_priv = ((void*)0), + .reserved = { 0 } }; + +extern "C" __attribute__((constructor)) void RegisterModule(void) +{ + napi_module_register(&nativeNodeModule); +} +} // namespace NativeXComponentSample diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.cpp b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ffdb5fb670eb9f855f113130f9f5d3ab6d6502a --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.cpp @@ -0,0 +1,995 @@ +/* + * Copyright (c) 2025 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 "key_event_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "container.h" + +namespace KeyEventSample { +const int32_t NUMBER_ZERO = 0; +const int32_t NUMBER_ONE = 1; +const int32_t NUMBER_TWO = 2; +const int32_t NUMBER_THREE = 3; +const int32_t NUMBER_FOUR = 4; +const int32_t NUMBER_FIVE = 5; +const int32_t NUMBER_TEN = 10; + +static ArkUI_NativeNodeAPI_1* g_nodeAPI = nullptr; + +KeyEventHandler* KeyEventHandler::instance_ = nullptr; + +KeyEventHandler* KeyEventHandler::GetInstance() +{ + if (instance_ == nullptr) { + instance_ = new KeyEventHandler(); + } + return instance_; +} + +ArkUI_NativeNodeAPI_1* OH_ArkUI_NodeAPI_GetAPI() +{ + if (g_nodeAPI) { + return g_nodeAPI; + } else { + OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, g_nodeAPI); + return g_nodeAPI; + } +} + +// 通用UI属性设置工具函数 +namespace UIUtils { +void SetNodeSize(ArkUI_NodeHandle node, float width, float height) +{ + ArkUI_NumberValue widthValue = { .f32 = width }; + ArkUI_AttributeItem widthItem = { .value = &widthValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_WIDTH, &widthItem); + + ArkUI_NumberValue heightValue = { .f32 = height }; + ArkUI_AttributeItem heightItem = { .value = &heightValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_HEIGHT, &heightItem); +} + +void SetNodeBackgroundColor(ArkUI_NodeHandle node, uint32_t color) +{ + ArkUI_NumberValue bgColorValue = { .u32 = color }; + ArkUI_AttributeItem bgColorItem = { .value = &bgColorValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_BACKGROUND_COLOR, &bgColorItem); +} + +void SetNodeBorderRadius(ArkUI_NodeHandle node, float radius) +{ + ArkUI_NumberValue borderRadius = { .f32 = radius }; + ArkUI_AttributeItem radiusItem = { .value = &borderRadius, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_BORDER_RADIUS, &radiusItem); +} + +void SetNodePadding(ArkUI_NodeHandle node, float padding) +{ + ArkUI_NumberValue paddingValue = { .f32 = padding }; + ArkUI_AttributeItem paddingItem = { .value = &paddingValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_PADDING, &paddingItem); +} + +void SetNodeBorder(ArkUI_NodeHandle node, float width, uint32_t color) +{ + ArkUI_NumberValue borderWidth = { .f32 = width }; + ArkUI_AttributeItem borderWidthItem = { .value = &borderWidth, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_BORDER_WIDTH, &borderWidthItem); + + ArkUI_NumberValue borderColorValue = { .u32 = color }; + ArkUI_AttributeItem borderColorItem = { .value = &borderColorValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_BORDER_COLOR, &borderColorItem); +} + +void SetNodeFocusable(ArkUI_NodeHandle node, bool focusable) +{ + ArkUI_NumberValue focusableValue = { .i32 = focusable ? 1 : 0 }; + ArkUI_AttributeItem focusableItem = { .value = &focusableValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(node, NODE_FOCUSABLE, &focusableItem); +} + +ArkUI_NodeHandle CreateSpacer(float height) +{ + ArkUI_NodeHandle spacer = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_STACK); + if (spacer) { + ArkUI_NumberValue spacerHeight = { .f32 = height }; + ArkUI_AttributeItem spacerHeightItem = { .value = &spacerHeight, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(spacer, NODE_HEIGHT, &spacerHeightItem); + } + return spacer; +} + +ArkUI_NodeHandle CreateTextNode(const std::string& text, float fontSize, uint32_t color) +{ + ArkUI_NodeHandle textNode = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_TEXT); + if (textNode) { + ArkUI_AttributeItem textItem = { .string = text.c_str() }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(textNode, NODE_TEXT_CONTENT, &textItem); + + ArkUI_NumberValue fontSizeValue = { .f32 = fontSize }; + ArkUI_AttributeItem fontSizeItem = { .value = &fontSizeValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(textNode, NODE_FONT_SIZE, &fontSizeItem); + + ArkUI_NumberValue colorValue = { .u32 = color }; + ArkUI_AttributeItem colorItem = { .value = &colorValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(textNode, NODE_FONT_COLOR, &colorItem); + } + return textNode; +} +} // namespace UIUtils + +ArkUI_NodeHandle CreateTitleText() +{ + return UIUtils::CreateTextNode("Key Event Sample", 22.0f, 0xFF2E5BBA); +} + +void SetRootColumnAttribute(ArkUI_NodeHandle& columnNode) +{ + // 设置Column的基本样式 + UIUtils::SetNodeSize(columnNode, 350.0f, 500.0f); + UIUtils::SetNodeBackgroundColor(columnNode, 0xFFF8F9FA); + UIUtils::SetNodeBorderRadius(columnNode, 16.0f); + UIUtils::SetNodePadding(columnNode, 20.0f); + + // 创建并添加标题文本 + ArkUI_NodeHandle titleNode = CreateTitleText(); + if (titleNode) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(columnNode, titleNode); + } + + // 添加间距 + ArkUI_NodeHandle spacer = UIUtils::CreateSpacer(20.0f); + if (spacer) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(columnNode, spacer); + } +} + +ArkUI_NodeHandle CreateOuterColumnTitle() +{ + return UIUtils::CreateTextNode("Event Propagation Demo", 16.0f, 0xFF2E7D32); +} + +void SetOuterColumnAttribute(ArkUI_NodeHandle& outerColumn) +{ + // 设置外层Column基本样式 + UIUtils::SetNodeSize(outerColumn, 300.0f, 120.0f); + UIUtils::SetNodeBackgroundColor(outerColumn, 0xFFE8F5E8); + UIUtils::SetNodeBorderRadius(outerColumn, 8.0f); + UIUtils::SetNodeBorder(outerColumn, 2.0f, 0xFF4CAF50); + UIUtils::SetNodePadding(outerColumn, 10.0f); + UIUtils::SetNodeFocusable(outerColumn, true); + + // 设置ID + ArkUI_AttributeItem idItem = { .string = "parentColumn" }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(outerColumn, NODE_ID, &idItem); + + // 创建并添加标题文本 + ArkUI_NodeHandle titleNode = CreateOuterColumnTitle(); + if (titleNode) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(outerColumn, titleNode); + } +} + +void KeyEventHandler::Initialize() +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Initialize begins"); + + // 创建显示节点 + displayNode_ = UIUtils::CreateTextNode("Click buttons below to test key events...", 14.0f, 0xFF666666); +} + +ArkUI_NodeHandle CreateButtonsContainer() +{ + ArkUI_NodeHandle columnNode = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_COLUMN); + if (!columnNode) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Failed to create column node"); + return nullptr; + } + + SetRootColumnAttribute(columnNode); + return columnNode; +} + +void AddButtonWithSpacer(ArkUI_NodeHandle container, ArkUI_NodeHandle button, const std::string& buttonId) +{ + if (button) { + ArkUI_AttributeItem idItem = { .string = buttonId.c_str() }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(button, NODE_ID, &idItem); + OH_ArkUI_NodeAPI_GetAPI()->addChild(container, button); + + // 添加间距 + ArkUI_NodeHandle spacer = UIUtils::CreateSpacer(12.0f); + if (spacer) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(container, spacer); + } + } +} + +ArkUI_NodeHandle KeyEventHandler::CreateKeyEventNode() +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Creating key event node"); + + // 创建主容器 + ArkUI_NodeHandle columnNode = CreateButtonsContainer(); + if (!columnNode) { + return nullptr; + } + + // 创建三个Button,分别对应三个事件类型 + ArkUI_NodeHandle keyEventButton = CreateKeyEventButton("Key Event (Click to Focus)", NODE_ON_KEY_EVENT); + AddButtonWithSpacer(columnNode, keyEventButton, "Button1"); + + ArkUI_NodeHandle preIMEButton = CreateKeyEventButton("Pre-IME Event (Click to Focus)", NODE_ON_KEY_PRE_IME); + AddButtonWithSpacer(columnNode, preIMEButton, "Button2"); + + ArkUI_NodeHandle dispatchButton = CreateKeyEventButton("Dispatch Event (Click to Focus)", NODE_DISPATCH_KEY_EVENT); + AddButtonWithSpacer(columnNode, dispatchButton, "Button3"); + + // 添加事件冒泡和消费演示场景 + ArkUI_NodeHandle propagationDemo = CreateEventPropagationDemo(); + if (propagationDemo) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(columnNode, propagationDemo); + } + + // 添加最终间距和显示节点 + ArkUI_NodeHandle finalSpacer = UIUtils::CreateSpacer(20.0f); + if (finalSpacer) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(columnNode, finalSpacer); + } + + if (displayNode_) { + OH_ArkUI_NodeAPI_GetAPI()->addChild(columnNode, displayNode_); + } + + return columnNode; +} + +void SetButtonStyle(ArkUI_NodeHandle buttonNode, const std::string& label) +{ + UIUtils::SetNodeSize(buttonNode, 300.0f, 48.0f); + UIUtils::SetNodeBackgroundColor(buttonNode, 0xFF4A90E2); + UIUtils::SetNodeBorderRadius(buttonNode, 8.0f); + UIUtils::SetNodeFocusable(buttonNode, true); + + // 设置Button文本 + ArkUI_AttributeItem labelItem = { .string = label.c_str() }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(buttonNode, NODE_BUTTON_LABEL, &labelItem); +} + +KeyEventCallbackData* CreateCallbackData( + ArkUI_NodeHandle buttonNode, const std::string& label, ArkUI_NodeEventType eventType) +{ + KeyEventCallbackData* callbackData = new KeyEventCallbackData(); + callbackData->buttonNode = buttonNode; + callbackData->originalLabel = label; + callbackData->currentLabel = label; + callbackData->eventCount = 0; + callbackData->eventType = eventType; + return callbackData; +} + +void RegisterButtonEvents( + ArkUI_NodeHandle buttonNode, KeyEventCallbackData* callbackData, ArkUI_NodeEventType eventType) +{ + // 注册全局事件接收器(只需要注册一次) + static bool receiverRegistered = false; + if (!receiverRegistered) { + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEventReceiver(KeyEventHandler::GlobalEventReceiver); + receiverRegistered = true; + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Global event receiver registered"); + } + + // 注册各种事件 + int32_t result1 = OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(buttonNode, eventType, 0, callbackData); + int32_t result2 = OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(buttonNode, NODE_ON_CLICK, 1, callbackData); + int32_t result3 = OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(buttonNode, NODE_ON_FOCUS, 2, callbackData); + int32_t result4 = OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(buttonNode, NODE_ON_BLUR, 3, callbackData); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Event registration results - Key:%{public}d, Click:%{public}d, Focus:%{public}d, Blur:%{public}d", result1, + result2, result3, result4); +} + +ArkUI_NodeHandle KeyEventHandler::CreateKeyEventButton(const std::string& label, ArkUI_NodeEventType eventType) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Creating button with label: %{public}s", + label.c_str()); + + // 创建Button节点 + ArkUI_NodeHandle buttonNode = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_BUTTON); + if (!buttonNode) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Failed to create button node"); + return nullptr; + } + + // 设置按钮样式 + SetButtonStyle(buttonNode, label); + + // 创建回调数据 + KeyEventCallbackData* callbackData = CreateCallbackData(buttonNode, label, eventType); + + // 注册事件 + RegisterButtonEvents(buttonNode, callbackData, eventType); + + return buttonNode; +} + +ArkUI_NodeHandle CreateChildButton() +{ + ArkUI_NodeHandle childButton = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_BUTTON); + if (childButton) { + UIUtils::SetNodeSize(childButton, 250.0f, 40.0f); + UIUtils::SetNodeBackgroundColor(childButton, 0xFFFF9800); + UIUtils::SetNodeBorderRadius(childButton, 6.0f); + UIUtils::SetNodeFocusable(childButton, true); + + ArkUI_AttributeItem btnLabelItem = { .string = "Child Button (ESC=Consume, F1=StopProp)" }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(childButton, NODE_BUTTON_LABEL, &btnLabelItem); + + ArkUI_AttributeItem idItem = { .string = "Button4" }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(childButton, NODE_ID, &idItem); + } + return childButton; +} + +void RegisterChildButtonEvents(ArkUI_NodeHandle childButton) +{ + KeyEventCallbackData* childCallbackData = + CreateCallbackData(childButton, "Child Button (ESC=Consume, F1=StopProp)", NODE_ON_KEY_EVENT); + + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(childButton, NODE_ON_KEY_EVENT, NUMBER_ZERO, childCallbackData); + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(childButton, NODE_ON_CLICK, NUMBER_ONE, childCallbackData); + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(childButton, NODE_ON_FOCUS, NUMBER_TWO, childCallbackData); + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(childButton, NODE_ON_BLUR, NUMBER_THREE, childCallbackData); +} + +void RegisterParentContainerEvents(ArkUI_NodeHandle outerColumn) +{ + KeyEventCallbackData* parentCallbackData = + CreateCallbackData(outerColumn, "Parent Column Container", NODE_ON_KEY_EVENT); + + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(outerColumn, NODE_ON_KEY_EVENT, NUMBER_ZERO, parentCallbackData); + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(outerColumn, NODE_ON_FOCUS, NUMBER_TWO, parentCallbackData); + OH_ArkUI_NodeAPI_GetAPI()->registerNodeEvent(outerColumn, NODE_ON_BLUR, NUMBER_THREE, parentCallbackData); +} + +ArkUI_NodeHandle KeyEventHandler::CreateEventPropagationDemo() +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Creating event propagation demo"); + + // 创建外层Column容器 + ArkUI_NodeHandle outerColumn = OH_ArkUI_NodeAPI_GetAPI()->createNode(ARKUI_NODE_COLUMN); + if (!outerColumn) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Failed to create outer column node"); + return nullptr; + } + + SetOuterColumnAttribute(outerColumn); + + // 创建并配置子Button + ArkUI_NodeHandle childButton = CreateChildButton(); + if (childButton) { + RegisterChildButtonEvents(childButton); + OH_ArkUI_NodeAPI_GetAPI()->addChild(outerColumn, childButton); + } + + // 注册父容器事件 + RegisterParentContainerEvents(outerColumn); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Event propagation demo created successfully"); + + return outerColumn; +} + +void KeyEventHandler::GlobalEventReceiver(ArkUI_NodeEvent* event) +{ + if (!event) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "GlobalEventReceiver: Event is null"); + return; + } + + ArkUI_NodeEventType eventType = OH_ArkUI_NodeEvent_GetEventType(event); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "GlobalEventReceiver: eventType=%{public}d, targetId=%{public}d", eventType, + OH_ArkUI_NodeEvent_GetTargetId(event)); + + void* userData = OH_ArkUI_NodeEvent_GetUserData(event); + if (!userData) { + OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_PRINT_DOMAIN, "KeyEventHandler", "No user data found"); + return; + } + + KeyEventCallbackData* callbackData = static_cast(userData); + + // 根据事件类型分发处理 + switch (eventType) { + case NODE_ON_CLICK: + OnButtonClick(event); + break; + case NODE_ON_FOCUS: + UpdateButtonFocusState(callbackData->buttonNode, true); + break; + case NODE_ON_BLUR: + UpdateButtonFocusState(callbackData->buttonNode, false); + break; + default: + // 处理按键事件 + if (eventType == callbackData->eventType) { + HandleKeyEventWithUserData(event); + } + break; + } +} + +bool ProcessChildButtonKeyEvent(KeyEventCallbackData* callbackData, const ArkUI_UIInputEvent* inputEvent) +{ + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(inputEvent); + if (keyCode == ArkUI_KeyCode::ARKUI_KEYCODE_ESCAPE) { // ESC键 - 演示SetConsumed + KeyEventHandler::DemoKeyEventSetConsumed(inputEvent); + OH_LOG_Print( + LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Child Button: ESC key pressed - Event CONSUMED"); + return true; // 事件已消费,停止后续处理 + } else if (keyCode == ArkUI_KeyCode::ARKUI_KEYCODE_F1) { // F1键 - 演示StopPropagation + KeyEventHandler::DemoKeyEventStopPropagation(inputEvent); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Child Button: F1 key pressed - Event propagation STOPPED"); + return true; // 事件传播已停止,停止后续处理 + } else { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Child Button: Normal key pressed - Event will propagate to parent"); + return false; // 继续正常处理 + } +} + +void ProcessParentContainerKeyEvent(KeyEventCallbackData* callbackData) +{ + // 父容器的按键事件处理 - 更改颜色以显示收到事件 + KeyEventHandler::UpdateParentContainerEventFeedback(callbackData->buttonNode, true); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Parent Container received key event - child did not consume/stop it"); + + // 延迟恢复原始颜色 + static int resetCounter = 0; + resetCounter++; + if (resetCounter > NUMBER_TEN) { + resetCounter = 0; + } +} + +std::string GetEventTypeDisplayName(ArkUI_NodeEventType eventType) +{ + switch (eventType) { + case NODE_ON_KEY_EVENT: + return "KEY EVENT"; + case NODE_ON_KEY_PRE_IME: + return "PRE-IME EVENT"; + case NODE_DISPATCH_KEY_EVENT: + return "DISPATCH EVENT"; + default: + return "UNKNOWN EVENT"; + } +} + +std::string CreateEventInfo(KeyEventCallbackData* callbackData, const ArkUI_UIInputEvent* inputEvent) +{ + KeyEventHandler* instance = KeyEventHandler::GetInstance(); + if (!instance) { + return "Instance not available"; + } + + std::string eventTypeName = GetEventTypeDisplayName(callbackData->eventType); + std::string info = instance->GetKeyEventInfo(inputEvent); + + // 如果是事件冒泡演示,添加额外信息 + if (callbackData->originalLabel.find("Child Button") != std::string::npos || + callbackData->originalLabel.find("Parent Column") != std::string::npos) { + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(inputEvent); + std::string componentType = + callbackData->originalLabel.find("Child Button") != std::string::npos ? "Child" : "Parent"; + + if (keyCode == ArkUI_KeyCode::ARKUI_KEYCODE_ESCAPE) { + info += " ESC键被" + componentType + "处理-SetConsumed(true)"; + } else if (keyCode == ArkUI_KeyCode::ARKUI_KEYCODE_F1) { + info += " F1键被" + componentType + "处理-StopPropagation(true)"; + } else { + info += " 普通按键被" + componentType + "处理-正常传播"; + } + } + + return eventTypeName + ": " + info + " API调用完成-查看日志详情!"; +} + +void KeyEventHandler::HandleKeyEventWithUserData(ArkUI_NodeEvent* event) +{ + if (!event) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "HandleKeyEventWithUserData: Event is null"); + return; + } + + void* userData = OH_ArkUI_NodeEvent_GetUserData(event); + if (!userData) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "User data is null"); + return; + } + + KeyEventCallbackData* callbackData = static_cast(userData); + const ArkUI_UIInputEvent* inputEvent = OH_ArkUI_NodeEvent_GetInputEvent(event); + + if (inputEvent) { + // 检查是否是事件冒泡演示组件 + if (callbackData->originalLabel.find("Child Button") != std::string::npos) { + bool shouldStop = ProcessChildButtonKeyEvent(callbackData, inputEvent); + // 无论事件是否被消费/阻止传播,都要更新Child Button的标签和颜色以显示收到事件 + UpdateButtonLabel(callbackData, inputEvent); + if (shouldStop) { + // 如果事件被消费或阻止传播,在更新标签后返回,不传播到父容器 + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Child button key event consumed/stopped - updated button but stopping propagation"); + return; + } + } else if (callbackData->originalLabel.find("Parent Column") != std::string::npos) { + ProcessParentContainerKeyEvent(callbackData); + // 更新Button标签 + UpdateButtonLabel(callbackData, inputEvent); + } else { + // 普通按钮的处理 + UpdateButtonLabel(callbackData, inputEvent); + } + + // 同时更新全局显示 + KeyEventHandler* instance = GetInstance(); + if (instance) { + std::string displayText = CreateEventInfo(callbackData, inputEvent); + instance->UpdateKeyEventDisplay(displayText); + } + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Key event handled with user data and API demos completed"); +} + +std::string CreateButtonLabelText(KeyEventCallbackData* callbackData, const ArkUI_UIInputEvent* inputEvent) +{ + std::ostringstream oss; + oss << callbackData->originalLabel; + oss << " [" << callbackData->eventCount << "]"; + + KeyEventHandler* instance = KeyEventHandler::GetInstance(); + if (instance) { + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(inputEvent); + ArkUI_KeyEventType eventType = OH_ArkUI_KeyEvent_GetType(inputEvent); + + std::string keyName = instance->GetKeyCodeName(keyCode); + std::string typeName = instance->GetKeyEventTypeName(static_cast(eventType)); + oss << " " << keyName << "(" << typeName << ")"; + } + + return oss.str(); +} + +void UpdateButtonVisualFeedback(ArkUI_NodeHandle buttonNode, const std::string& originalLabel) +{ + // 所有按钮收到按键事件时都变色,以便用户清楚看到事件传播现象 + if (originalLabel.find("Child Button") != std::string::npos) { + // Child Button + UIUtils::SetNodeBackgroundColor(buttonNode, 0xFFFFEBEE); // 浅红色背景 + UIUtils::SetNodeBorder(buttonNode, 3.0f, 0xFFE57373); // 红色边框 + } else if (originalLabel.find("Parent Column") != std::string::npos) { + // Parent Column 通过专门的函数处理,这里不处理 + // UpdateParentContainerEventFeedback 函数会处理父容器的变色 + } else { + // 普通按钮变为浅红色 + UIUtils::SetNodeBackgroundColor(buttonNode, 0xFFFFEBEE); // 浅红色背景 + UIUtils::SetNodeBorder(buttonNode, 3.0f, 0xFFE57373); // 红色边框 + } +} + +void KeyEventHandler::UpdateButtonLabel(KeyEventCallbackData* callbackData, const ArkUI_UIInputEvent* inputEvent) +{ + if (!callbackData || !inputEvent) { + return; + } + + // 增加事件计数 + callbackData->eventCount++; + + // 创建新的标签文本 + callbackData->currentLabel = CreateButtonLabelText(callbackData, inputEvent); + + // 更新Button的标签 + ArkUI_AttributeItem labelItem = { .string = callbackData->currentLabel.c_str() }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(callbackData->buttonNode, NODE_BUTTON_LABEL, &labelItem); + + // 更新Button颜色 + UpdateButtonVisualFeedback(callbackData->buttonNode, callbackData->originalLabel); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Button label updated: %{public}s", + callbackData->currentLabel.c_str()); +} + +std::string GetBasicKeyEventInfo(const ArkUI_UIInputEvent* event) +{ + std::ostringstream oss; + + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(event); + ArkUI_KeyEventType eventType = OH_ArkUI_KeyEvent_GetType(event); + + KeyEventHandler* instance = KeyEventHandler::GetInstance(); + if (instance) { + std::string keyName = instance->GetKeyCodeName(keyCode); + std::string typeName = instance->GetKeyEventTypeName(static_cast(eventType)); + + oss << "Key:" << keyName << "(" << keyCode << ") "; + oss << "\nType:" << typeName << " "; + } + + return oss.str(); +} + +std::string GetKeySourceInfo(const ArkUI_UIInputEvent* event) +{ + ArkUI_KeySourceType sourceType = OH_ArkUI_KeyEvent_GetKeySource(event); + std::string sourceName; + switch (sourceType) { + case ARKUI_KEY_SOURCE_TYPE_KEYBOARD: + sourceName = "KEYBOARD"; + break; + case ARKUI_KEY_SOURCE_TYPE_MOUSE: + sourceName = "MOUSE"; + break; + case ARKUI_KEY_SOURCE_TYPE_JOYSTICK: + sourceName = "JOYSTICK"; + break; + default: + sourceName = "UNK"; + break; + } + return "/Source:" + sourceName + "\n"; +} + +std::string GetKeyTextAndUnicodeInfo(const ArkUI_UIInputEvent* event) +{ + std::ostringstream oss; + + // 获取按键文本 + const char* keyText = OH_ArkUI_KeyEvent_GetKeyText(event); + if (keyText && strlen(keyText) > 0) { + oss << "KeyText:" << keyText << " "; + } + oss << "\n"; + + // 获取Unicode值 + uint32_t unicode = OH_ArkUI_KeyEvent_GetUnicode(event); + oss << "Unicode:" << std::hex << unicode << std::dec << " "; + oss << "\n"; + + return oss.str(); +} + +std::string GetKeyIntentionInfo(const ArkUI_UIInputEvent* event) +{ + std::ostringstream oss; + + ArkUI_KeyIntension intention = OH_ArkUI_KeyEvent_GetKeyIntensionCode(event); + if (intention != ARKUI_KEY_INTENSION_UNKNOWN) { + oss << "意图:" << static_cast(intention) << " "; + } + oss << "\n"; + + return oss.str(); +} + +std::string GetLockKeysInfo(const ArkUI_UIInputEvent* event) +{ + std::ostringstream oss; + + bool numLock = false; + bool capsLock = false; + bool scrollLock = false; + ArkUI_ErrorCode result1 = OH_ArkUI_KeyEvent_IsNumLockOn(event, &numLock); + ArkUI_ErrorCode result2 = OH_ArkUI_KeyEvent_IsCapsLockOn(event, &capsLock); + ArkUI_ErrorCode result3 = OH_ArkUI_KeyEvent_IsScrollLockOn(event, &scrollLock); + + oss << "Locks: "; + if (result1 == ARKUI_ERROR_CODE_NO_ERROR) { + oss << "Num" << (numLock ? "✓" : "✗") << " "; + } + if (result2 == ARKUI_ERROR_CODE_NO_ERROR) { + oss << "Caps" << (capsLock ? "✓" : "✗") << " "; + } + if (result3 == ARKUI_ERROR_CODE_NO_ERROR) { + oss << "Scroll" << (scrollLock ? "✓" : "✗"); + } + oss << "\n"; + + return oss.str(); +} + +std::string KeyEventHandler::GetKeyEventInfo(const ArkUI_UIInputEvent* event) +{ + if (!event) { + return "Invalid event"; + } + + std::ostringstream oss; + + // 使用Native Key Event API获取按键信息 + try { + oss << GetBasicKeyEventInfo(event); + oss << GetKeySourceInfo(event); + oss << GetKeyTextAndUnicodeInfo(event); + oss << GetKeyIntentionInfo(event); + oss << GetLockKeysInfo(event); + } catch (...) { + // 如果API调用失败,返回基本信息 + oss << "Key Event Detected\n"; + } + + return oss.str(); +} + +std::string KeyEventHandler::GetKeyCodeName(int32_t keyCode) +{ + // 简化版本的按键码名称映射 + switch (keyCode) { + case ArkUI_KeyCode::ARKUI_KEYCODE_0: + return "KEY_0"; + case ArkUI_KeyCode::ARKUI_KEYCODE_1: + return "KEY_1"; + case ArkUI_KeyCode::ARKUI_KEYCODE_2: + return "KEY_2"; + case ArkUI_KeyCode::ARKUI_KEYCODE_3: + return "KEY_3"; + case ArkUI_KeyCode::ARKUI_KEYCODE_4: + return "KEY_4"; + case ArkUI_KeyCode::ARKUI_KEYCODE_5: + return "KEY_5"; + case ArkUI_KeyCode::ARKUI_KEYCODE_6: + return "KEY_6"; + case ArkUI_KeyCode::ARKUI_KEYCODE_7: + return "KEY_7"; + case ArkUI_KeyCode::ARKUI_KEYCODE_8: + return "KEY_8"; + case ArkUI_KeyCode::ARKUI_KEYCODE_9: + return "KEY_9"; + case ArkUI_KeyCode::ARKUI_KEYCODE_A: + return "KEY_A"; + case ArkUI_KeyCode::ARKUI_KEYCODE_B: + return "KEY_B"; + case ArkUI_KeyCode::ARKUI_KEYCODE_C: + return "KEY_C"; + case ArkUI_KeyCode::ARKUI_KEYCODE_D: + return "KEY_D"; + case ArkUI_KeyCode::ARKUI_KEYCODE_SPACE: + return "SPACE"; + case ArkUI_KeyCode::ARKUI_KEYCODE_ENTER: + return "ENTER"; + case ArkUI_KeyCode::ARKUI_KEYCODE_DEL: + return "BACKSPACE"; + case ArkUI_KeyCode::ARKUI_KEYCODE_ESCAPE: + return "ESCAPE"; + default: + return "OTHER(" + std::to_string(keyCode) + ")"; + } +} + +std::string KeyEventHandler::GetKeyEventTypeName(int32_t eventType) +{ + switch (eventType) { + case ArkUI_KeyEventType::ARKUI_KEY_EVENT_DOWN: + return "KEY_DOWN"; + case ArkUI_KeyEventType::ARKUI_KEY_EVENT_UP: + return "KEY_UP"; + case ArkUI_KeyEventType::ARKUI_KEY_EVENT_LONG_PRESS: + return "KEY_LONG_PRESS"; + case ArkUI_KeyEventType::ARKUI_KEY_EVENT_CLICK: + return "KEY_CLICK"; + default: + return "UNKNOWN_TYPE"; + } +} + +void KeyEventHandler::UpdateKeyEventDisplay(const std::string& info) +{ + if (!displayNode_) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Display node is null"); + return; + } + + lastKeyEventInfo_ = info; + + // 更新文本内容 + ArkUI_AttributeItem item = { .string = info.c_str() }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(displayNode_, NODE_TEXT_CONTENT, &item); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Display updated: %{public}s", info.c_str()); +} + +std::string GetClickDisplayText(KeyEventCallbackData* callbackData) +{ + if (callbackData->originalLabel.find("Child Button") != std::string::npos) { + return "Event Propagation Demo Focused!\n\n" + "按键事件测试:\n" + "• 按ESC键:调用SetConsumed(true)消费事件\n" + "• 按F1键:调用StopPropagation(true)阻止冒泡\n" + "• 按其他键:正常事件传播\n" + "查看日志了解详细API调用信息"; + } else if (callbackData->originalLabel.find("Parent Column") != std::string::npos) { + return "Parent Container Focused!\n\n" + "父容器接收到按键事件时会显示:\n" + "• 事件是否被子组件消费\n" + "• 事件是否被阻止冒泡\n" + "• 完整的按键信息"; + } else { + // 普通事件按钮的显示信息 + std::string eventTypeName = GetEventTypeDisplayName(callbackData->eventType); + return eventTypeName + " Button Focused!\n\nPress any key to test the event..."; + } +} + +void KeyEventHandler::OnButtonClick(ArkUI_NodeEvent* event) +{ + if (!event) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "OnButtonClick: Event is null"); + return; + } + + void* userData = OH_ArkUI_NodeEvent_GetUserData(event); + if (!userData) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "OnButtonClick: User data is null"); + return; + } + + KeyEventCallbackData* callbackData = static_cast(userData); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Button clicked, requesting focus"); + + // 主动请求焦点 + ArkUI_NumberValue focusValue = { .i32 = 1 }; + ArkUI_AttributeItem focusItem = { .value = &focusValue, .size = 1 }; + OH_ArkUI_NodeAPI_GetAPI()->setAttribute(callbackData->buttonNode, NODE_FOCUS_STATUS, &focusItem); + + // 更新显示信息 + KeyEventHandler* instance = GetInstance(); + if (instance) { + std::string displayText = GetClickDisplayText(callbackData); + instance->UpdateKeyEventDisplay(displayText); + } +} + +void KeyEventHandler::UpdateButtonFocusState(ArkUI_NodeHandle buttonNode, bool hasFocus) +{ + if (!buttonNode) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "UpdateButtonFocusState: Button node is null"); + return; + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Button focus state changed: %{public}s", + hasFocus ? "focused" : "blurred"); + + // 根据焦点状态更新Button的视觉效果 + if (hasFocus) { + // 获得焦点时的样式 - 更亮的蓝色和边框 + UIUtils::SetNodeBackgroundColor(buttonNode, 0xFF2E86DE); + UIUtils::SetNodeBorder(buttonNode, 2.0f, 0xFF1B4F9C); + } else { + // 失去焦点时的样式 - 恢复原始颜色 + UIUtils::SetNodeBackgroundColor(buttonNode, 0xFF4A90E2); + UIUtils::SetNodeBorder(buttonNode, 0.0f, 0x00000000); // 移除边框 + } +} + +void KeyEventHandler::DemoKeyEventSetConsumed(const ArkUI_UIInputEvent* event) +{ + if (!event) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "DemoKeyEventSetConsumed: Event is null"); + return; + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Demo: OH_ArkUI_KeyEvent_SetConsumed"); + + // 演示OH_ArkUI_KeyEvent_SetConsumed API的使用 + try { + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(event); + ArkUI_KeyEventType eventType = OH_ArkUI_KeyEvent_GetType(event); + + // 设置事件为已消费状态 - ESC键触发此演示 + OH_ArkUI_KeyEvent_SetConsumed(event, true); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Key event consumed - KeyCode: %{public}d (ESC)", keyCode); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Event marked as consumed, preventing further propagation"); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Event consumption status set for Type: %{public}d", eventType); + } catch (...) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Failed to set event consumed status"); + } +} + +void KeyEventHandler::DemoKeyEventStopPropagation(const ArkUI_UIInputEvent* event) +{ + if (!event) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "DemoKeyEventStopPropagation: Event is null"); + return; + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Demo: OH_ArkUI_KeyEvent_StopPropagation"); + + // 演示OH_ArkUI_KeyEvent_StopPropagation API的使用 + try { + int32_t keyCode = OH_ArkUI_KeyEvent_GetKeyCode(event); + ArkUI_KeyEventType eventType = OH_ArkUI_KeyEvent_GetType(event); + + // 阻止事件冒泡 - F1键触发此演示 + OH_ArkUI_KeyEvent_StopPropagation(event, true); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Event propagation stopped for F1 Key - KeyCode: %{public}d", keyCode); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "F1 key event blocked from bubbling up to parent components"); + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Propagation control applied for event Type: %{public}d", eventType); + } catch (...) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", "Failed to control event propagation"); + } +} + +void KeyEventHandler::UpdateParentContainerEventFeedback(ArkUI_NodeHandle containerNode, bool eventReceived) +{ + if (!containerNode) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "KeyEventHandler", + "UpdateParentContainerEventFeedback: Container node is null"); + return; + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Updating parent container visual feedback: %{public}s", eventReceived ? "event received" : "reset"); + + if (eventReceived) { + // 父容器收到按键事件时 - 变为红色表示事件传播到了父级 + UIUtils::SetNodeBackgroundColor(containerNode, 0xFFFFEBEE); // 浅红色背景 + UIUtils::SetNodeBorder(containerNode, 3.0f, 0xFFE57373); // 红色边框 + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", + "Parent container highlighted - key event propagated!"); + } else { + // 恢复原始样式 - 浅绿色表示正常状态 + UIUtils::SetNodeBackgroundColor(containerNode, 0xFFE8F5E8); + UIUtils::SetNodeBorder(containerNode, 2.0f, 0xFF4CAF50); // 绿色边框 + + OH_LOG_Print( + LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "KeyEventHandler", "Parent container restored to normal state"); + } +} + +} // namespace KeyEventSample diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.h b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.h new file mode 100644 index 0000000000000000000000000000000000000000..f505b8937484d29881482501875042455ee66db8 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/key_event_handler.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025 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 KEY_EVENT_HANDLER_H +#define KEY_EVENT_HANDLER_H + +#include +#include +#include +#include +#include + + +namespace KeyEventSample { + +// 按键事件回调数据结构 +struct KeyEventCallbackData { + ArkUI_NodeHandle buttonNode; // Button节点引用 + std::string originalLabel; // 原始标签文本 + std::string currentLabel; // 当前标签文本 + int eventCount; // 事件计数 + ArkUI_NodeEventType eventType; // 事件类型 +}; + +class KeyEventHandler { +public: + static KeyEventHandler* GetInstance(); + + // 初始化按键事件处理器 + void Initialize(); + + // 创建带有按键事件的UI组件 + ArkUI_NodeHandle CreateKeyEventNode(); + + // 创建Button组件并设置按键事件 + ArkUI_NodeHandle CreateKeyEventButton(const std::string& label, ArkUI_NodeEventType eventType); + + // 创建事件冒泡和消费演示场景 + ArkUI_NodeHandle CreateEventPropagationDemo(); + + // 全局事件接收器 + static void GlobalEventReceiver(ArkUI_NodeEvent* event); + + // 按键事件处理函数(通过userdata获取回调信息) + static void HandleKeyEventWithUserData(ArkUI_NodeEvent* event); + + // 更新Button标签 + static void UpdateButtonLabel(KeyEventCallbackData* callbackData, const ArkUI_UIInputEvent* inputEvent); + + // 处理Button点击事件(请求焦点) + static void OnButtonClick(ArkUI_NodeEvent* event); + + // 更新Button的焦点状态显示 + static void UpdateButtonFocusState(ArkUI_NodeHandle buttonNode, bool hasFocus); + + // API示例函数 + static void DemoKeyEventSetConsumed(const ArkUI_UIInputEvent* event); + static void DemoKeyEventStopPropagation(const ArkUI_UIInputEvent* event); + + // 更新父容器事件反馈 + static void UpdateParentContainerEventFeedback(ArkUI_NodeHandle containerNode, bool eventReceived); + + // 获取按键信息的字符串表示 + std::string GetKeyEventInfo(const ArkUI_UIInputEvent* event); + std::string GetKeyCodeName(int32_t keyCode); + std::string GetKeyEventTypeName(int32_t eventType); + + // 更新UI显示 + void UpdateKeyEventDisplay(const std::string& info); + + // 获取显示节点 + ArkUI_NodeHandle GetDisplayNode() + { + return displayNode_; + } + +private: + KeyEventHandler() = default; + ~KeyEventHandler() = default; + + static KeyEventHandler* instance_; + ArkUI_NodeHandle displayNode_; + std::string lastKeyEventInfo_; +}; + +} // namespace KeyEventSample + +#endif // KEY_EVENT_HANDLER_H diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.cpp b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..42d0e55662210072c6be2fd3cee3be58618fb3e5 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2025 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 "manager.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "napi/native_api.h" + +namespace NativeXComponentSample { +Manager Manager::manager_; + +Manager::~Manager() +{ + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "~Manager"); + for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) { + if (iter->second != nullptr) { + iter->second = nullptr; + } + } + nativeXComponentMap_.clear(); + + for (auto iter = containerMap_.begin(); iter != containerMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + containerMap_.clear(); +} + +static ArkUI_NativeNodeAPI_1* nodeAPI = nullptr; + +void CreateKeyEventSample(napi_env env, napi_value arg, OH_NativeXComponent* component) +{ + // 初始化按键事件处理器 + KeyEventSample::KeyEventHandler* keyHandler = KeyEventSample::KeyEventHandler::GetInstance(); + keyHandler->Initialize(); + + // 创建按键事件的UI组件 + ArkUI_NodeHandle keyEventNode = keyHandler->CreateKeyEventNode(); + if (keyEventNode) { + OH_NativeXComponent_AttachNativeRootNode(component, keyEventNode); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Manager", "Key event sample UI created successfully"); + } else { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Failed to create key event sample UI"); + } +} + +napi_value Manager::CreateNativeNode(napi_env env, napi_callback_info info) +{ + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode env or info is null"); + return nullptr; + } + + size_t argCnt = 2; + napi_value args[2] = { nullptr }; + if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed"); + } + + if (argCnt < 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + if (napi_typeof(env, args[0], &valuetype) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_typeof failed"); + return nullptr; + } + + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong type of arguments"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + size_t length; + if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_get_value_int64 failed"); + return nullptr; + } + + auto manager = Manager::GetInstance(); + if (manager == nullptr) { + return nullptr; + } + + OH_NativeXComponent* component = manager->GetNativeXComponent(idStr); + if (component == nullptr) { + return nullptr; + } + + OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_NODE, ArkUI_NativeNodeAPI_1, nodeAPI); + + if (nodeAPI != nullptr) { + if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) { + CreateKeyEventSample(env, args[1], component); + } + } + return nullptr; +} + +napi_value Manager::UpdateNativeNode(napi_env env, napi_callback_info info) +{ + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode env or info is null"); + return nullptr; + } + + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode"); + + size_t argCnt = 1; + napi_value args[1] = { nullptr }; + if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "UpdateNativeNode napi_get_cb_info failed"); + } + + if (argCnt != 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + if (napi_typeof(env, args[0], &valuetype) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_typeof failed"); + return nullptr; + } + + if (valuetype != napi_string) { + napi_throw_type_error(env, NULL, "Wrong type of arguments"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + size_t length; + if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_get_value_int64 failed"); + return nullptr; + } + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "UpdateNativeNode %{public}s", idStr); + + auto manager = Manager::GetInstance(); + if (manager == nullptr) { + return nullptr; + } + + OH_NativeXComponent* component = manager->GetNativeXComponent(idStr); + if (component == nullptr) { + return nullptr; + } + + return nullptr; +} + +napi_value Manager::GetContext(napi_env env, napi_callback_info info) +{ + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext env or info is null"); + return nullptr; + } + + size_t argCnt = 1; + napi_value args[1] = { nullptr }; + if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "GetContext napi_get_cb_info failed"); + } + + if (argCnt != 1) { + napi_throw_type_error(env, NULL, "Wrong number of arguments"); + return nullptr; + } + + napi_valuetype valuetype; + if (napi_typeof(env, args[0], &valuetype) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_typeof failed"); + return nullptr; + } + + if (valuetype != napi_number) { + napi_throw_type_error(env, NULL, "Wrong type of arguments"); + return nullptr; + } + + int64_t value; + if (napi_get_value_int64(env, args[0], &value) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_get_value_int64 failed"); + return nullptr; + } + + napi_value exports; + if (napi_create_object(env, &exports) != napi_ok) { + napi_throw_type_error(env, NULL, "napi_create_object failed"); + return nullptr; + } + + return exports; +} + +void Manager::Export(napi_env env, napi_value exports) +{ + if ((env == nullptr) || (exports == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: env or exports is null"); + return; + } + + napi_value exportInstance = nullptr; + if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_get_named_property fail"); + return; + } + + OH_NativeXComponent* nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: napi_unwrap fail"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "Export: OH_NativeXComponent_GetXComponentId fail"); + return; + } + + std::string id(idStr); + auto manager = Manager::GetInstance(); + if ((manager != nullptr) && (nativeXComponent != nullptr)) { + manager->SetNativeXComponent(id, nativeXComponent); + } +} + +void Manager::SetNativeXComponent(std::string& id, OH_NativeXComponent* nativeXComponent) +{ + if (nativeXComponent == nullptr) { + return; + } + + if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) { + nativeXComponentMap_[id] = nativeXComponent; + return; + } + + if (nativeXComponentMap_[id] != nativeXComponent) { + OH_NativeXComponent* tmp = nativeXComponentMap_[id]; + tmp = nullptr; + nativeXComponentMap_[id] = nativeXComponent; + } +} + +OH_NativeXComponent* Manager::GetNativeXComponent(const std::string& id) +{ + return nativeXComponentMap_[id]; +} +} // namespace NativeXComponentSample diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.h b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.h new file mode 100644 index 0000000000000000000000000000000000000000..41f74bafab8683d0c7c9cd5d4e9e6ad831b444cb --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/manager.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2025 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 +#include +#include +#include + +#include "container.h" +#include "key_event_handler.h" + +namespace NativeXComponentSample { +const int MAX_SIZE = 11; +class Manager { +public: + ~Manager(); + + static Manager* GetInstance() + { + return &Manager::manager_; + } + + static napi_value GetContext(napi_env env, napi_callback_info info); + static napi_value CreateNativeNode(napi_env env, napi_callback_info info); + static napi_value UpdateNativeNode(napi_env env, napi_callback_info info); + + void SetNativeXComponent(std::string& id, OH_NativeXComponent* nativeXComponent); + OH_NativeXComponent* GetNativeXComponent(const std::string& id); + + void Export(napi_env env, napi_value exports); + +private: + static Manager manager_; + + std::unordered_map nativeXComponentMap_; + std::unordered_map containerMap_; + ArkUI_NodeHandle button_; + ArkUI_NodeHandle textShow_; + ArkUI_NodeHandle textShow2_; + ArkUI_GestureRecognizer* currentRecognizer_ = nullptr; + ArkUI_GestureRecognizer* childRecognizer_ = nullptr; + ArkUI_GestureRecognizerType list_[3] = { TAP_GESTURE, LONG_PRESS_GESTURE, PAN_GESTURE }; + int32_t index_ = 0; + float lastOffset_ = 0.0f; + int customData_ = 2; +}; +} // namespace NativeXComponentSample +#endif // NATIVE_XCOMPONENT_PLUGIN_MANAGER_H diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/napi_init.cpp b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ebee157faa8ccf9c15a7cd9ab28e356f824a7a6 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 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 "napi/native_api.h" + +static napi_value Add(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + napi_value args[2] = { nullptr }; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_valuetype valuetype0; + napi_typeof(env, args[0], &valuetype0); + + napi_valuetype valuetype1; + napi_typeof(env, args[1], &valuetype1); + + double value0; + napi_get_value_double(env, args[0], &value0); + + double value1; + napi_get_value_double(env, args[1], &value1); + + napi_value sum; + napi_create_double(env, value0 + value1, &sum); + return sum; +} + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + napi_property_descriptor desc[] = { { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr } }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) +{ + napi_module_register(&demoModule); +} diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/Index.d.ts b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c14affc63e0dc8a96335ff0c4f6256d392724e2 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/Index.d.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ + +export const add: (a: number, b: number) => number; \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/oh-package.json5 b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..846e4c7e13ead48abe6019bd40f3a13bf8f9c083 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/ets/entryability/EntryAbility.ets b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..252b7dad746191e35c874f2fc83685e3c8ae115b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 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/NdkKeyEvent/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..1504a74f09dfdcfae408be979f99369a2c5affab --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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 '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/ets/pages/Index.ets b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..c761abdc7538364c9957bb9066482f979715f02b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2025 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 nativeNode from 'libnativeNode.so'; + +@Entry +@Component +struct KeyEventSample { + @State eventInfo: string = '🎹 按任意键查看按键事件信息...'; + @State lastKeyEvent: string = ''; + @State keyEventCount: number = 0; + + build() { + Column() { + // 标题区域 + Row() { + Text('🎹 按键事件示例') + .fontSize(24) + .fontWeight(FontWeight.Bold) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + } + .width('100%') + .height(80) + .backgroundColor('#4A90E2') + .justifyContent(FlexAlign.Center) + .alignItems(VerticalAlign.Center) + + Scroll() { + Column({ space: 20 }) { + // 说明区域 + Column({ space: 10 }) { + Text('本示例展示了Native API (CAPI) 中的按键事件处理功能,包括:') + .fontSize(14) + .fontColor('#666666') + .textAlign(TextAlign.Start) + + Column({ space: 5 }) { + Text('• NODE_ON_KEY_EVENT - 标准按键事件处理') + .fontSize(12) + .fontColor('#888888') + .textAlign(TextAlign.Start) + Text('• NODE_ON_KEY_PRE_IME - 输入法前置按键事件') + .fontSize(12) + .fontColor('#888888') + .textAlign(TextAlign.Start) + Text('• NODE_DISPATCH_KEY_EVENT - 按键事件分发处理') + .fontSize(12) + .fontColor('#888888') + .textAlign(TextAlign.Start) + Text('• 事件冒泡与消费机制演示 (Column+Button层级结构)') + .fontSize(12) + .fontColor('#FF6B6B') + .textAlign(TextAlign.Start) + .fontWeight(FontWeight.Medium) + } + .alignItems(HorizontalAlign.Start) + .width('100%') + } + .width('95%') + .padding(15) + .backgroundColor('#F8F9FA') + .borderRadius(12) + .alignItems(HorizontalAlign.Start) + + // 统计信息 + Row() { + Column() { + Text(this.keyEventCount.toString()) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .fontColor('#4A90E2') + Text('按键次数') + .fontSize(12) + .fontColor('#666666') + } + .justifyContent(FlexAlign.Center) + + Divider() + .vertical(true) + .height(40) + .color('#E0E0E0') + + Column() { + Text(this.lastKeyEvent || 'None') + .fontSize(16) + .fontWeight(FontWeight.Medium) + .fontColor('#FF6B6B') + .maxLines(1) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + Text('最后按键') + .fontSize(12) + .fontColor('#666666') + } + .justifyContent(FlexAlign.Center) + .layoutWeight(1) + } + .width('95%') + .height(70) + .padding(15) + .backgroundColor(Color.White) + .borderRadius(12) + .shadow({ radius: 5, color: '#E0E0E0', offsetX: 2, offsetY: 2 }) + .justifyContent(FlexAlign.SpaceEvenly) + + // Native组件区域 + Column() { + Text('🎯 Native 按键事件组件') + .fontSize(16) + .fontWeight(FontWeight.Medium) + .fontColor('#333333') + .margin({ bottom: 10 }) + + XComponent({ + id: 'keyEventSample', + type: XComponentType.NODE, + libraryname: 'nativeNode' + }) + .onAppear(() => { + nativeNode.createNativeNode("keyEventSample", this.getUIContext()); + }) + .width('100%') + .height(600) + .backgroundColor('#FFFFFF') + .borderRadius(12) + .border({ + width: 2, + color: '#4A90E2', + style: BorderStyle.Dashed + }) + .focusable(true) + .onKeyEvent((event) => { + this.handleKeyEvent(event); + }) + } + .id('testKeyEvent') + .width('95%') + .padding(15) + .backgroundColor('#F0F8FF') + .borderRadius(12) + .alignItems(HorizontalAlign.Start) + + // 使用说明 + Column({ space: 8 }) { + Text('💡 使用说明') + .fontSize(16) + .fontWeight(FontWeight.Medium) + .fontColor('#333333') + + Text('📌 基础功能测试:') + .fontSize(13) + .fontColor('#4A90E2') + .fontWeight(FontWeight.Medium) + Text('1. 点击上方Native组件区域使其获得焦点') + .fontSize(12) + .fontColor('#666666') + Text('2. 点击三个按钮测试不同的按键事件处理类型') + .fontSize(12) + .fontColor('#666666') + Text('3. 使用键盘输入按键,观察事件信息的实时更新') + .fontSize(12) + .fontColor('#666666') + + Text('🔄 事件冒泡与消费测试:') + .fontSize(13) + .fontColor('#FF6B6B') + .fontWeight(FontWeight.Medium) + Text('4. 在事件冒泡演示区域的子按钮上:') + .fontSize(12) + .fontColor('#666666') + Text(' • 按 ESC 键 - 演示事件消费 (SetConsumed)') + .fontSize(12) + .fontColor('#666666') + Text(' • 按 F1 键 - 演示阻止事件冒泡 (StopPropagation)') + .fontSize(12) + .fontColor('#666666') + Text(' • 按其他键 - 事件正常冒泡到父容器') + .fontSize(12) + .fontColor('#666666') + Text('5. 支持常用按键:字母、数字、方向键、功能键等') + .fontSize(12) + .fontColor('#666666') + } + .width('95%') + .padding(15) + .backgroundColor('#FFF9E6') + .borderRadius(12) + .alignItems(HorizontalAlign.Start) + + } + } + .id('testScroll') + .layoutWeight(1) + .width('100%') + .padding({ top: 20, bottom: 20 }) + + } + .width('100%') + .height('100%') + .backgroundColor('#F5F5F5') + } + + private handleKeyEvent(event: KeyEvent) { + this.keyEventCount++; + + // 获取按键名称 + let keyName = this.getKeyName(event.keyCode); + this.lastKeyEvent = keyName; + + // 更新事件信息 + let eventType = this.getEventTypeName(event.type); + let timestamp = new Date().toLocaleTimeString(); + + this.eventInfo = `🔍 ETS 按键事件详情:\n` + + `🔑 按键: ${keyName} (${event.keyCode})\n` + + `📝 事件类型: ${eventType}\n` + + `📈 总计次数: ${this.keyEventCount}`; + } + + private getKeyName(keyCode: number): string { + const keyMap: Record = { + 2000: 'KEY_0', 2001: 'KEY_1', 2002: 'KEY_2', 2003: 'KEY_3', 2004: 'KEY_4', + 2005: 'KEY_5', 2006: 'KEY_6', 2007: 'KEY_7', 2008: 'KEY_8', 2009: 'KEY_9', + 2017: 'KEY_A', 2018: 'KEY_B', 2019: 'KEY_C', 2020: 'KEY_D', 2021: 'KEY_E', + 2022: 'KEY_F', 2023: 'KEY_G', 2024: 'KEY_H', 2025: 'KEY_I', 2026: 'KEY_J', + 2027: 'KEY_K', 2028: 'KEY_L', 2029: 'KEY_M', 2030: 'KEY_N', 2031: 'KEY_O', + 2032: 'KEY_P', 2033: 'KEY_Q', 2034: 'KEY_R', 2035: 'KEY_S', 2036: 'KEY_T', + 2037: 'KEY_U', 2038: 'KEY_V', 2039: 'KEY_W', 2040: 'KEY_X', 2041: 'KEY_Y', + 2042: 'KEY_Z', 2050: 'SPACE', 2054: 'ENTER', 2055: 'BACKSPACE', 2070: 'ESCAPE', + 2012: 'DPAD_UP', 2013: 'DPAD_DOWN', 2014: 'DPAD_LEFT', 2015: 'DPAD_RIGHT', + 2090: 'F1', 2091: 'F2', 2092: 'F3', 2093: 'F4', 2094: 'F5', 2095: 'F6', + 2096: 'F7', 2097: 'F8', 2098: 'F9', 2099: 'F10', 2100: 'F11', 2101: 'F12' + }; + + return keyMap[keyCode] || `UNKNOWN(${keyCode})`; + } + + private getEventTypeName(type: KeyType): string { + switch (type) { + case KeyType.Down: + return 'KEY_DOWN'; + case KeyType.Up: + return 'KEY_UP'; + default: + return 'UNKNOWN_TYPE'; + } + } +} diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/module.json5 b/ArkUIKit/NdkKeyEvent/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1d783f389c78a73c75e6c782d07d3433877a671f --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/module.json5 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 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" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/element/color.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..d66f9a7d4ac61fb8d215239ab3620b7bcd77bf33 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/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/NdkKeyEvent/entry/src/main/resources/base/element/string.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5e22b758e6fccae8588590862649655f49372295 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/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": "NdkKeyEvent" + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/background.png b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/background.png differ diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/foreground.png b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/foreground.png differ diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/layered_image.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/startIcon.png b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/media/startIcon.png differ diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/backup_config.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..d742c2f96e7dd0f406f499941f3147345e998f95 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/main_pages.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/en_US/element/string.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..5e22b758e6fccae8588590862649655f49372295 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/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": "NdkKeyEvent" + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/main/resources/zh_CN/element/string.json b/ArkUIKit/NdkKeyEvent/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..75ee7bc52256e716f142bbd79eefe8b6fcb29fa8 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/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": "NdkKeyEvent" + } + ] +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/mock/Libentry.mock.ets b/ArkUIKit/NdkKeyEvent/entry/src/mock/Libentry.mock.ets new file mode 100644 index 0000000000000000000000000000000000000000..eebf1ed910f6a8f2a9e7e565aa71b179b7b8b537 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/mock/Libentry.mock.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 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. + */ + +const NativeMock: Record = { + 'add': (a: number, b: number) => { + return a + b; + }, +}; + +export default NativeMock; \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/mock/mock-config.json5 b/ArkUIKit/NdkKeyEvent/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2c7d2ba82b796a2850ced0a277d261d7d7355416 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/mock/mock-config.json5 @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "libentry.so": { + "source": "src/mock/Libentry.mock.ets" + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/Ability.test.ets b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f8ce9a2c012f8fe36114cef65216ef0b6254f41 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 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 '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/KeyEventTest.test.ets b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/KeyEventTest.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a37bcde611aafd00456ce865f389bed028097d6b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/KeyEventTest.test.ets @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2025 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 '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; +import { abilityDelegatorRegistry, BY, Driver, ON } from '@kit.TestKit'; +import { UIAbility, Want } from '@kit.AbilityKit'; + +const delegator: abilityDelegatorRegistry.AbilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); +const bundleName = abilityDelegatorRegistry.getArguments().bundleName; +let want: Want; + +export default function KeyEventTest() { + describe('KeyEventTest', () => { + + beforeAll(async () => { + want = { + bundleName: bundleName, + abilityName: 'EntryAbility' + }; + await delegator.startAbility(want); + let driver = Driver.create(); + await driver.delayMs(1000); + const ability: UIAbility = await delegator.getCurrentTopAbility(); + console.info('get top ability'); + expect(ability.context.abilityInfo.name).assertEqual('EntryAbility'); + }) + + beforeEach(async () => { + let driver = Driver.create(); + await driver.delayMs(1000); + }) + + afterEach(() => { + hilog.info(0x0000, 'KeyEventTest', 'Key event test case completed'); + }) + + afterAll(() => { + hilog.info(0x0000, 'KeyEventTest', 'All key event tests completed'); + }) + + /** + * @tc.number KeyEvent_001 + * @tc.name testButton1KeyEvent + * @tc.desc 测试Button1按键事件 - 滑动到Button1,点击获焦,按键A,验证颜色变化 + */ + it('testButton1KeyEvent', 0, async (done: Function) => { + hilog.info(0x0000, 'KeyEventTest', 'testButton1KeyEvent begin'); + let driver = Driver.create(); + await driver.delayMs(1000); + + try { + // 滑动到Button1的位置 + const scroll = await driver.findComponent(ON.id('testScroll')); + await scroll.scrollSearch(ON.id('testKeyEvent')); + await driver.delayMs(500); + + // 点击Button1获取焦点 + const button1 = await driver.findComponent(ON.id('Button1')); + await button1.click(); + await driver.delayMs(1000); + + // 验证Button1获得焦点后的颜色变化(焦点状态下的颜色) + let strJson = getInspectorByKey('Button1'); + let obj: ESObject = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button1 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + // 焦点状态下应该是更亮的蓝色 0xFF2E86DE + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + + // 按键A,验证按键事件处理 + await driver.triggerKey(2017); // KEY_A + await driver.delayMs(1000); + + // 验证按键事件处理后Button1变浅红色 + strJson = getInspectorByKey('Button1'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button1 after key A backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FFFFEBEE'); + + } catch (error) { + hilog.error(0x0000, 'KeyEventTest', 'testButton1KeyEvent failed: %{public}s', error.message); + expect().assertFail(); + } + + hilog.info(0x0000, 'KeyEventTest', 'testButton1KeyEvent end'); + done(); + }) + + /** + * @tc.number KeyEvent_002 + * @tc.name testButton2PreIME + * @tc.desc 测试Button2预输入法按键事件 - Button2获焦,测试PreIme事件类型 + */ + it('testButton2PreIME', 0, async (done: Function) => { + hilog.info(0x0000, 'KeyEventTest', 'testButton2PreIME begin'); + let driver = Driver.create(); + await driver.delayMs(1000); + + try { + // 滑动到测试区域 + const scroll = await driver.findComponent(ON.id('testScroll')); + await scroll.scrollSearch(ON.id('testKeyEvent')); + await driver.delayMs(1500); + + // 点击Button2获取焦点 + const button2 = await driver.findComponent(ON.id('Button2')); + await button2.click(); + await driver.delayMs(1000); + + // 验证Button2获得焦点后的颜色变化 + let strJson = getInspectorByKey('Button2'); + let obj: ESObject = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button2 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + + // 按键B测试PreIME事件 + await driver.triggerKey(2018); // KEY_B + await driver.delayMs(1000); + + // 验证PreIME事件处理后Button2变浅红色 + strJson = getInspectorByKey('Button2'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button2 after key B backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FFFFEBEE'); + + } catch (error) { + hilog.error(0x0000, 'KeyEventTest', 'testButton2PreIME failed: %{public}s', error.message); + expect().assertFail(); + } + + hilog.info(0x0000, 'KeyEventTest', 'testButton2PreIME end'); + done(); + }) + + /** + * @tc.number KeyEvent_003 + * @tc.name testButton3Dispatch + * @tc.desc 测试Button3分发按键事件 - Button3获焦,测试Dispatch事件类型 + */ + it('testButton3Dispatch', 0, async (done: Function) => { + hilog.info(0x0000, 'KeyEventTest', 'testButton3Dispatch begin'); + let driver = Driver.create(); + await driver.delayMs(1000); + + try { + // 滑动到测试区域 + const scroll = await driver.findComponent(ON.id('testScroll')); + await scroll.scrollSearch(ON.id('testKeyEvent')); + await driver.delayMs(500); + + // 点击Button3获取焦点 + const button3 = await driver.findComponent(ON.id('Button3')); + await button3.click(); + await driver.delayMs(1000); + + // 验证Button3获得焦点后的颜色变化 + let strJson = getInspectorByKey('Button3'); + let obj: ESObject = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button3 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + + // 按键C测试Dispatch事件 + await driver.triggerKey(2019); // KEY_C + await driver.delayMs(1000); + + // 验证Dispatch事件处理后Button3变浅红色 + strJson = getInspectorByKey('Button3'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button3 after key C backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FFFFEBEE'); + + } catch (error) { + hilog.error(0x0000, 'KeyEventTest', 'testButton3Dispatch failed: %{public}s', error.message); + expect().assertFail(); + } + + hilog.info(0x0000, 'KeyEventTest', 'testButton3Dispatch end'); + done(); + }) + + /** + * @tc.number KeyEvent_004 + * @tc.name testButton4EventControl + * @tc.desc 测试Button4事件控制 - Button4获焦,测试ESC、F1和A键,验证父组件颜色变化 + */ + it('testButton4EventControl', 0, async (done: Function) => { + hilog.info(0x0000, 'KeyEventTest', 'testButton4EventControl begin'); + let driver = Driver.create(); + await driver.delayMs(1000); + + try { + // 滑动到测试区域 + const scroll = await driver.findComponent(ON.id('testScroll')); + await scroll.scrollSearch(ON.id('testKeyEvent')); + await driver.delayMs(500); + + // 点击Button4获取焦点 + const button4 = await driver.findComponent(ON.id('Button4')); + await button4.click(); + await driver.delayMs(1000); + + // 验证Button4获得焦点后的颜色变化 + let strJson = getInspectorByKey('Button4'); + let obj: ESObject = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button4 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + + // 测试ESC键 - 应该被消费,父组件不变色 + await driver.triggerKey(2070); // ESC键 + await driver.delayMs(1000); + + // 父组件背景色应该保持正常状态 0xFFE8F5E8 + strJson = getInspectorByKey('parentColumn'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button4 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + hilog.info(0x0000, 'KeyEventTest', 'After ESC key - parent should not change color'); + + // 测试F1键 - 应该阻止传播,父组件不变色 + await driver.triggerKey(2090); // F1键 + await driver.delayMs(1000); + // 父组件背景色应该保持正常状态 0xFFE8F5E8 + strJson = getInspectorByKey('parentColumn'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button4 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FF2E86DE'); + + hilog.info(0x0000, 'KeyEventTest', 'After F1 key - parent should not change color'); + + // 测试A键 - 应该正常传播,父组件变色 + await driver.triggerKey(2017); // KEY_A + await driver.delayMs(1000); + + strJson = getInspectorByKey('parentColumn'); + obj = JSON.parse(strJson); + hilog.info(0x0000, 'KeyEventTest', 'Button4 focus backgroundColor: %{public}s', obj.$attrs.backgroundColor); + expect(obj.$attrs.backgroundColor).assertEqual('#FFFFEBEE'); + + hilog.info(0x0000, 'KeyEventTest', 'After A key - parent should change color'); + } catch (error) { + hilog.error(0x0000, 'KeyEventTest', 'testButton4EventControl failed: %{public}s', error.message); + expect().assertFail(); + } + + hilog.info(0x0000, 'KeyEventTest', 'testButton4EventControl end'); + done(); + }) + + }) +} diff --git a/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/List.test.ets b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0d78b68acd1bc68f8352687c19845d4e1617eedc --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 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 './Ability.test'; +import KeyEventTest from './KeyEventTest.test'; + +export default function testsuite() { + abilityTest(); + KeyEventTest(); +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/module.json5 b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9983b2ba4e55e31a172f0328c82c9a75bfa00ded --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 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", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/ArkUIKit/NdkKeyEvent/entry/src/test/List.test.ets b/ArkUIKit/NdkKeyEvent/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1389499715f1bfeece9c0807dc40d216534623b --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/test/List.test.ets @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 KeyEventTest from '../ohosTest/ets/test/KeyEventTest.test'; +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/entry/src/test/LocalUnit.test.ets b/ArkUIKit/NdkKeyEvent/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 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 { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/hvigor/hvigor-config.json5 b/ArkUIKit/NdkKeyEvent/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..06cd7b9ed2566b9c4676235fbf6252399ecbe651 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 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": "6.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "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": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/hvigorfile.ts b/ArkUIKit/NdkKeyEvent/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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 { 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/NdkKeyEvent/oh-package-lock.json5 b/ArkUIKit/NdkKeyEvent/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..567c6d3a3c3bab23db56374ef33ac4aa4f58f45f --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/oh-package-lock.json5 @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 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. + */ + +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", + "@ohos/hypium@1.0.19": "@ohos/hypium@1.0.19" + }, + "packages": { + "@ohos/hamock@1.0.0": { + "name": "", + "version": "1.0.0", + "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hamock/-/hamock-1.0.0.har", + "registryType": "ohpm" + }, + "@ohos/hypium@1.0.19": { + "name": "", + "version": "1.0.19", + "integrity": "sha512-cEjDgLFCm3cWZDeRXk7agBUkPqjWxUo6AQeiu0gEkb3J8ESqlduQLSIXeo3cCsm8U/asL7iKjF85ZyOuufAGSQ==", + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.19.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/oh-package.json5 b/ArkUIKit/NdkKeyEvent/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f53e8a0a7a1ece7beea30f4f258eb44a2ebcbdc5 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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": "6.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/ohosTest.md b/ArkUIKit/NdkKeyEvent/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..b9d3d9c0c2839403b28cd1630c78f8d459d63335 --- /dev/null +++ b/ArkUIKit/NdkKeyEvent/ohosTest.md @@ -0,0 +1,10 @@ +# NdkKeyEvent 测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|------------------------------------------| -------------- |-------------------------------|-------------------------------------------------------| :------- | -------- | +| NODE_ON_KEY_EVENT示例验证 | 设备正常运行 | 进入首页,点击第一个Button后按下A | Button变成浅红色 | 是 | Pass | +| NODE_ON_KEY_PRE_IME示例代码验证 | 设备正常运行 | 进入首页,点击第二个Button后按下A | Button变成浅红色 | 是 | Pass | +| NODE_DISPATCH_KEY_EVENT示例代码验证 | 设备正常运行 | 进入首页,点击第三个Button后按下A | Button变成浅红色 | 是 | Pass | +| OH_ArkUI_KeyEvent_SetConsumed和OH_ArkUI_KeyEvent_StopPropagation事件冒泡示例代码验证 | 设备正常运行 | 进入首页,点击第四个Button后分别按下ESC、F1、A | Button均变浅红色,ESC和F1拦截事件冒泡,外层Column不变色,按下A后外层Column变浅红色 | 是 | Pass | \ No newline at end of file diff --git a/ArkUIKit/NdkKeyEvent/screenshots/device/image1.jpg b/ArkUIKit/NdkKeyEvent/screenshots/device/image1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2092635211e3ebf38e3637ba709ad59413522fc5 Binary files /dev/null and b/ArkUIKit/NdkKeyEvent/screenshots/device/image1.jpg differ