diff --git a/README.md b/README.md index 4d944b4cabc2dd2c53b9e2e9a7a52ce5112a3f44..d1a811e4e890c18875446a5e9414aba466b0fbaf 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,21 @@ ### 介绍 -XComponent控件常用于相机预览流的显示和游戏画面的绘制,在HarmonyOS上,可以配合Native Window创建OpenGL开发环境,并最终将OpenGL绘制的图形显示到XComponent控件。本示例基于"Native C++"模板,调用OpenGL(OpenGL ES)图形库相关API绘制3D图形(三棱锥),并将结果渲染到页面的XComponent控件中进行展示。同时,可以点击自动旋转按钮可以自动旋转,点击阻尼旋转可以减速旋转直至停止旋转,还可以在屏幕上通过触摸滑动手势对三棱锥进行旋转,最终得到不同角度的图形并显示到页面。 +XComponent控件常用于相机预览流的显示和游戏画面的绘制,在HarmonyOS上,可以配合Native Window创建OpenGL开发环境,并最终将OpenGL绘制的图形显示到XComponent控件。本示例基于"Native C++"模板,调用OpenGL(OpenGL ES)图形库相关API绘制3D图形(三棱锥),并将结果渲染到页面的XComponent控件中进行展示。同时,点击自动旋转按钮可以自动旋转,点击阻尼旋转可以减速旋转直至停止旋转,还可以在屏幕上通过触摸滑动手势对三棱锥进行旋转,最终得到不同角度的图形并显示到页面。 ### 效果预览 -| 首页 | 滑动屏幕旋转变换 | -|:-----------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:| -| index | rotate | +| 手机 | 折叠屏 | 平板 | +|:-----:|:-------------------------------:|------------------------------------| +| ![](screenshots/device/图片1.png) | ![](screenshots/device/图片4.png) | ![](screenshots/device/tablet.png) | 使用说明 -应用界面中展示了XComponent相关控件的使用,及采用OpenGL (OpenGL ES)相关标准API绘制3D图形(三棱锥,3D渲染的光源用的是简单的线性光源)。此外,可在屏幕触摸滑动,以使三棱锥进行旋转,其中主要采用了napi接口来更新3D图形的旋转角度。 +应用界面中展示了XComponent相关控件的使用,及采用OpenGL (OpenGL ES)相关标准API绘制3D图形(三棱锥,3D渲染的光源用的是简单的线性光源)。 + +可在屏幕触摸滑动旋转三棱锥,或点击按钮使三棱锥进行自动或阻尼旋转,其中主要采用了napi接口来更新3D图形的旋转角度。 + +此外,增加了一多适配能力,OpenGL绘制三棱锥区域可自适应手机、折叠屏、平板设备,同时,支持竖屏旋转,且自动避让摄像头区域。 ### 工程目录 @@ -81,6 +85,6 @@ Native XComponent相关函数如下: ### 约束与限制 1. 本示例仅支持标准系统上运行,支持设备:华为手机。 -2. HarmonyOS系统:HarmonyOS 5.0.0 Release及以上。 -3. DevEco Studio版本:DevEco Studio 5.0.0 Release及以上。 -4. HarmonyOS SDK版本:HarmonyOS 5.0.0 Release SDK及以上。 +2. HarmonyOS系统:HarmonyOS 5.0.4 Release及以上。 +3. DevEco Studio版本:DevEco Studio 5.0.4 Release及以上。 +4. HarmonyOS SDK版本:HarmonyOS 5.0.4 Release SDK及以上。 diff --git a/build-profile.json5 b/build-profile.json5 index d8c1dc53c229c6a7adab02944d312b78b87f8b3e..2335b14a04e67d9e50675602ba09ea6ed77f9b02 100644 --- a/build-profile.json5 +++ b/build-profile.json5 @@ -4,7 +4,7 @@ { "name": "default", "signingConfig": "default", - "compatibleSdkVersion": "5.0.0(12)", + "compatibleSdkVersion": "5.0.4(16)", "runtimeOS": "HarmonyOS" } ], diff --git a/entry/src/main/cpp/app_napi.cpp b/entry/src/main/cpp/app_napi.cpp index 9d190b2ad3f5dfed5b91f742439d92d1e46e8df0..3bc9ae65e7bf15ef62e9b76ebdbd58ec0f5c8db1 100644 --- a/entry/src/main/cpp/app_napi.cpp +++ b/entry/src/main/cpp/app_napi.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -47,9 +47,9 @@ Tetrahedron *AppNapi::getTetrahedron(void) { return tetrahedron_; } -FrameHandle &AppNapi::getFrameHanlde(void) -{ - return frame_handle_; +FrameHandle &AppNapi::getFrameHanlde(void) +{ + return frame_handle_; } static int Normalize(int angle) @@ -61,7 +61,8 @@ static int Normalize(int angle) return ret; } -static void OnFrameCB(OH_NativeXComponent* component, uint64_t timestamp, uint64_t targetTimestamp) { +static void OnFrameCB(OH_NativeXComponent* component, uint64_t timestamp, uint64_t targetTimestamp) +{ int32_t ret; char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; @@ -197,15 +198,18 @@ void AppNapi::SetNativeXComponent(OH_NativeXComponent* component) mousecallback_ = mousecallback; } +// [Start get_surfaceSize] void AppNapi::OnSurfaceCreated(OH_NativeXComponent* component, void* window) { LOGE("AppNapi::OnSurfaceCreated"); OH_NativeXComponent_RegisterOnFrameCallback(component, OnFrameCB); + // Get surface size. int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_); - + // Draw the image to be displayed on the window and set the size of the drawing area. LOGE("Offset : x = %{public}f, y = %{public}f ", x_, y_); if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { tetrahedron_->Init(window, width_, height_); + tetrahedron_->reSizeWindow(width_, height_); tetrahedron_->Update(angleX_, angleY_); isCreated_++; xcHeight_ = height_; @@ -220,16 +224,20 @@ void AppNapi::OnSurfaceCreated(OH_NativeXComponent* component, void* window) void AppNapi::OnSurfaceChanged(OH_NativeXComponent* component, void* window) { LOGE("AppNapi::OnSurfaceChanged"); + // Retrieve surface size again int32_t ret = OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_); int32_t ret1; + + // Set the size of the drawing area. if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + tetrahedron_->reSizeWindow(width_, height_); xcHeight_ = height_; xcWidth_ = width_; LOGE("after width = %{public}d, height = %{public}d", xcWidth_, xcHeight_); ret1= OH_NativeXComponent_GetXComponentOffset(component, window, &x_, &y_); off_x = x_; off_y = y_; - + // [StartExclude get_surfaceSize] if (ret1 == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { LOGE("Offset : x = %{public}lf, y = %{public}lf ", off_x, off_y); } else { @@ -238,8 +246,10 @@ void AppNapi::OnSurfaceChanged(OH_NativeXComponent* component, void* window) LOGE("AppNapi::GetOffset "); LOGE("Offset : x = %{public}lf, y = %{public}lf ", off_x, off_y); + // [EndExclude get_surfaceSize] } } +// [End get_surfaceSize] void AppNapi::OnSurfaceDestroyed(OH_NativeXComponent* component, void* window) { @@ -347,8 +357,8 @@ napi_value AppNapi::UpdateAngle(napi_env env, napi_callback_info info) return ret; } - -napi_value AppNapi::SetRotate(napi_env env, napi_callback_info info) { +napi_value AppNapi::SetRotate(napi_env env, napi_callback_info info) +{ LOGE("SetRotate"); size_t argc = 1; napi_value args[1] = {nullptr}; diff --git a/entry/src/main/cpp/frame_handle.cpp b/entry/src/main/cpp/frame_handle.cpp index ed31c23dca029a815497df02ad6af6a75149a4d4..01b9372be685c3c14b2063993b01a84b9b0d7df3 100644 --- a/entry/src/main/cpp/frame_handle.cpp +++ b/entry/src/main/cpp/frame_handle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -15,11 +15,12 @@ #include "frame_handle.h" #include "app_napi.h" -#define DA_MAX (360) -#define OMIGA_MAX (30) -#define DAMPING_FACTING (10) +const int DA_MAX (360); +const int OMIGA_MAX (30); +const int DAMPING_FACTING (10); -static int Normalize(int angle) { +static int Normalize(int angle) +{ int ret = angle % CIRCUMFERENCE_DEGREE; if (ret < 0) { ret += CIRCUMFERENCE_DEGREE; @@ -27,20 +28,25 @@ static int Normalize(int angle) { return ret; } -FrameHandle::FrameHandle() - : app_napi_(nullptr), tetrahedron_(nullptr) { +FrameHandle::FrameHandle() + : app_napi_(nullptr), tetrahedron_(nullptr) +{ rotate_mode_ = STOP_ROTATE; -} -void FrameHandle::Init(AppNapi *app_napi, Tetrahedron *tetrahedron) { +} +void FrameHandle::Init(AppNapi *app_napi, Tetrahedron *tetrahedron) +{ app_napi_ = app_napi; tetrahedron_ = tetrahedron; } -void FrameHandle::SetRotate(RotateMode rotateMode) { +void FrameHandle::SetRotate(RotateMode rotateMode) +{ index_= 0; - rotate_mode_ = rotateMode; } + rotate_mode_ = rotateMode; +} -void FrameHandle::OnFrameHandle(uint64_t timestamp, uint64_t targetTimestamp) { +void FrameHandle::OnFrameHandle(uint64_t timestamp, uint64_t targetTimestamp) +{ index_++; if (rotate_mode_ == AUTO_ROTATE) { ConstantSpeedRotation(); @@ -50,11 +56,11 @@ void FrameHandle::OnFrameHandle(uint64_t timestamp, uint64_t targetTimestamp) { } } -void FrameHandle::DampingRotation() { +void FrameHandle::DampingRotation() +{ if (index_ >= DA_MAX) { rotate_mode_ = STOP_ROTATE; - } - else { + } else { float tetrahedron_angleX = tetrahedron_->GetAngleX(); float tetrahedron_angleY = tetrahedron_->GetAngleY(); const float const_e = 2.718281828459045; @@ -68,7 +74,8 @@ void FrameHandle::DampingRotation() { } } -void FrameHandle::ConstantSpeedRotation() { +void FrameHandle::ConstantSpeedRotation() +{ float tetrahedron_angleX = tetrahedron_->GetAngleX(); float tetrahedron_angleY = tetrahedron_->GetAngleY(); tetrahedron_angleX += 0; diff --git a/entry/src/main/cpp/include/app_napi.h b/entry/src/main/cpp/include/app_napi.h index cab550b9eef1acf4782b7950439d0c3352faf4d5..141890b9a0bfbc349599c0383ea385120995385e 100644 --- a/entry/src/main/cpp/include/app_napi.h +++ b/entry/src/main/cpp/include/app_napi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/include/frame_handle.h b/entry/src/main/cpp/include/frame_handle.h index e636bc2eb15e922809963349dc0a17d6bc166dfa..f6f4271760ccb5bad9607dd3bc7bebc90477244f 100644 --- a/entry/src/main/cpp/include/frame_handle.h +++ b/entry/src/main/cpp/include/frame_handle.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -40,4 +40,4 @@ private: uint64_t index_; }; -#endif //NDKOPENGL_FRAME_HANDLE_H +#endif // NDKOPENGL_FRAME_HANDLE_H diff --git a/entry/src/main/cpp/include/tetrahedron.h b/entry/src/main/cpp/include/tetrahedron.h index 64f1fdfbd0b9b95244618712d8e46a732c016452..1d21af2882c8c6432d4956c74ddc906f5a5e0d9c 100644 --- a/entry/src/main/cpp/include/tetrahedron.h +++ b/entry/src/main/cpp/include/tetrahedron.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -30,6 +30,7 @@ class Tetrahedron { public: explicit Tetrahedron(std::string& id) : id(id) {}; int32_t Init(void* windowHandle, int windowWidth, int windowHeight); + void reSizeWindow(int windowWidth, int windowHeight); void Update(float angleXOffset, float angleYOffset); float GetAngleX(void); float GetAngleY(void); @@ -51,6 +52,11 @@ private: GLuint mProgramHandle; float angleX = 30.0; /* default X angle */ float angleY = 45.0; /* default Y angle */ + + GLfloat m_widthPercent; + + int m_width; + int m_height; GLint mRotationLocation; GLint mTranslationLocation; diff --git a/entry/src/main/cpp/include/util/log.h b/entry/src/main/cpp/include/util/log.h index ebab2fd363fc2dc7ed9f76aea848609ddc849529..c17fed743aadbc11baea114b3e1d809355fcfa66 100644 --- a/entry/src/main/cpp/include/util/log.h +++ b/entry/src/main/cpp/include/util/log.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/include/util/napi_manager.h b/entry/src/main/cpp/include/util/napi_manager.h index 353c9fcf50d4362e905c1b5aa23f8b7872dc1b8a..238645c1b53d58fabfee71cfeb14a41f391a4c9e 100644 --- a/entry/src/main/cpp/include/util/napi_manager.h +++ b/entry/src/main/cpp/include/util/napi_manager.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/include/util/napi_util.h b/entry/src/main/cpp/include/util/napi_util.h index f0e2cfdb6237ff6030328ad066b59cc0068d2182..abdcd16bd0f20b8e33419ac5d8520b581eba8bb7 100644 --- a/entry/src/main/cpp/include/util/napi_util.h +++ b/entry/src/main/cpp/include/util/napi_util.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/include/util/native_common.h b/entry/src/main/cpp/include/util/native_common.h index 166b0e4a708481a4b2f651669ffe53903d69feab..ac52864f01548ff00c59a09ee3bac4680720a6aa 100644 --- a/entry/src/main/cpp/include/util/native_common.h +++ b/entry/src/main/cpp/include/util/native_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/module.cpp b/entry/src/main/cpp/module.cpp index 349f6fea9e824240b73aec94841400205caead4c..2aea890e303eed579e7846d5b2d7326ea51777e0 100644 --- a/entry/src/main/cpp/module.cpp +++ b/entry/src/main/cpp/module.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -25,6 +25,64 @@ extern "C" { * function for module exports */ EXTERN_C_START +// [Start ArkTS2Native] +// Responsible for transferring data from ArkTS to Native. +static napi_value objectPassingTs2Napi(napi_env env, napi_callback_info info) +{ + size_t argc = 1; + napi_value args[1]; + napi_get_cb_info(env, info, &argc, args, NULL, NULL); + + if (argc < 1) { + napi_throw_error(env, NULL, "Wrong number of arguments"); + return NULL; + } + + napi_value obj = args[0]; + napi_value keys; + napi_get_property_names(env, obj, &keys); // Get all attribute names. + + uint32_t length; + napi_get_array_length(env, keys, &length); // Obtain the number of attributes. + + for (uint32_t i = 0; i < length; ++i) { + napi_value key; + napi_get_element(env, keys, i, &key); // Get the i-th attribute name. + + // Convert attribute names to strings. + char keyStr[128]; + size_t keyLen; + napi_get_value_string_utf8(env, key, keyStr, sizeof(keyStr), &keyLen); + + // Get attribute values. + napi_value value; + napi_get_property(env, obj, key, &value); + + // Determine the type of attribute value and process it. + napi_valuetype type; + napi_typeof(env, value, &type); + + if (type == napi_string) { + char valueStr[4]; + size_t valueLen; + napi_get_value_string_utf8(env, value, valueStr, sizeof(valueStr), &valueLen); + } + if (type == napi_number) { + double num; + napi_get_value_double(env, value, &num); + } + } + return NULL; +} + +// Define data transfer function. +static napi_value objectPassing(napi_env env, napi_callback_info info) +{ + objectPassingTs2Napi(env, info); + return nullptr; +} +// [End ArkTS2Native] + static napi_value Init(napi_env env, napi_value exports) { LOGE("Init"); @@ -32,6 +90,7 @@ static napi_value Init(napi_env env, napi_value exports) DECLARE_NAPI_FUNCTION("getContext", NapiManager::GetContext), DECLARE_NAPI_FUNCTION("updateAngle", AppNapi::UpdateAngle), DECLARE_NAPI_FUNCTION("setRotate", AppNapi::SetRotate), + {"objectPassing", nullptr, objectPassing, nullptr, nullptr, nullptr, napi_default, nullptr} }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); diff --git a/entry/src/main/cpp/napi_manager.cpp b/entry/src/main/cpp/napi_manager.cpp index 37dd1200ea00bea7c0ce24c0321495a377fe5415..8f28c08dba0208fc0f484d152fdd612368d0b01c 100644 --- a/entry/src/main/cpp/napi_manager.cpp +++ b/entry/src/main/cpp/napi_manager.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 diff --git a/entry/src/main/cpp/napi_util.cpp b/entry/src/main/cpp/napi_util.cpp index ffcfa947fc30571627e553bfe51aeb15b3a09b63..2aed1d69a1737cf109ae3ce1ebfc43488ee5478c 100644 --- a/entry/src/main/cpp/napi_util.cpp +++ b/entry/src/main/cpp/napi_util.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -13,7 +13,7 @@ * limitations under the License. */ -#include +#include #include #include #include diff --git a/entry/src/main/cpp/tetrahedron.cpp b/entry/src/main/cpp/tetrahedron.cpp index 5df48b54745d3ed3f5ef735d2d93761cdf99823a..ecb3ccdcbfa2bfee7cbd95c3f404871e9010856e 100644 --- a/entry/src/main/cpp/tetrahedron.cpp +++ b/entry/src/main/cpp/tetrahedron.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Huawei Device Co., Ltd. + * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved. * 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 @@ -19,6 +19,8 @@ #include "log.h" #include "tetrahedron.h" +const float FIFTY_PERCENT = 0.5; + static char g_vertexShader[] = "attribute vec4 a_pos;\n" "attribute vec4 a_color;\n" @@ -191,6 +193,17 @@ GLuint Tetrahedron::CreateProgram(const char *vertexShader, const char *fragShad return program; } +void Tetrahedron::reSizeWindow(int32_t width, int32_t height) +{ + if ((0 >= width) || (0 >= height)) { + LOGE("Tetrahedron::Init: param error."); + return; + } + m_width = width; + m_height = height; + m_widthPercent = FIFTY_PERCENT * m_height / m_width; +} + int32_t Tetrahedron::Init(void *window, int32_t width, int32_t height) { LOGI("Init window = %{public}p, w = %{public}d, h = %{public}d.", window, width, height); @@ -202,7 +215,8 @@ int32_t Tetrahedron::Init(void *window, int32_t width, int32_t height) return -1; } - EGLint eglMajVers, eglMinVers; + EGLint eglMajVers; + EGLint eglMinVers; if (!eglInitialize(mEGLDisplay, &eglMajVers, &eglMinVers)) { mEGLDisplay = EGL_NO_DISPLAY; LOGE("unable to initialize display"); @@ -251,6 +265,7 @@ void Tetrahedron::Update(float angleXOffset, float angleYOffset) { const float pi = 3.141592; + glViewport(0, 0, m_width, m_height); glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(mProgramHandle); diff --git a/entry/src/main/cpp/type/libentry/tetrahedron_napi.d.ts b/entry/src/main/cpp/type/libentry/index.d.ts similarity index 87% rename from entry/src/main/cpp/type/libentry/tetrahedron_napi.d.ts rename to entry/src/main/cpp/type/libentry/index.d.ts index 179a6b535190e059addd376b332c07bbb2390607..890011de69f6ac9695aa61e4f9a8e51d5a57c2cc 100644 --- a/entry/src/main/cpp/type/libentry/tetrahedron_napi.d.ts +++ b/entry/src/main/cpp/type/libentry/index.d.ts @@ -14,4 +14,7 @@ */ export const updateAngle: (offsetX: number, offsetY: number) => Array; -export const setRotate: (mode: number) => void; \ No newline at end of file + +export const setRotate: (mode: number) => void; + +export const objectPassing: (input: object) => void; \ No newline at end of file diff --git a/entry/src/main/ets/entryability/EntryAbility.ets b/entry/src/main/ets/entryability/EntryAbility.ets index 34b629d31dc780e72cdde6284bec059b830a4bfa..fbc0b5814fe022b6655cc162af1d1de8dec73ce6 100644 --- a/entry/src/main/ets/entryability/EntryAbility.ets +++ b/entry/src/main/ets/entryability/EntryAbility.ets @@ -13,32 +13,118 @@ * limitations under the License. */ -import { UIAbility,AbilityConstant,Want } from '@kit.AbilityKit' -import { window } from '@kit.ArkUI' +import { UIAbility } from '@kit.AbilityKit'; +import { window } from '@kit.ArkUI'; +import { BusinessError } from '@kit.BasicServicesKit'; import { Logger } from '../utils/Logger'; +// [Start get_breakPoint] export default class EntryAbility extends UIAbility { - onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + public uiContext?: UIContext; + public onWindowSizeChange: (windowSize: window.Size) => void = (windowSize: window.Size) => { + let widthBp: WidthBreakpoint = this.uiContext!.getWindowWidthBreakpoint(); + AppStorage.setOrCreate('currentWidthBreakpoint', widthBp); + let heightBp: HeightBreakpoint = this.uiContext!.getWindowHeightBreakpoint(); + AppStorage.setOrCreate('currentHeightBreakpoint', heightBp); + }; + + // [StartExclude get_breakPoint] + // [Start onAvoidAreaChange] + public onAvoidAreaChange: (avoidArea: window.AvoidAreaOptions) => void = (avoidArea: window.AvoidAreaOptions) => { + if (avoidArea.type === window.AvoidAreaType.TYPE_CUTOUT) { + AppStorage.setOrCreate('cutout', avoidArea); + } + } + // [StartExclude onAvoidAreaChange] + onCreate() { Logger.info('Ability onCreate'); } onDestroy() { Logger.info('Ability onDestroy'); } + // [EndExclude get_breakPoint] + // [EndExclude onAvoidAreaChange] + // [Start immersive] onWindowStageCreate(windowStage: window.WindowStage) { + // [StartExclude onAvoidAreaChange] + // [StartExclude immersive] // Main window is created, set main page for this ability + // [StartExclude get_breakPoint] Logger.info('Ability onWindowStageCreate'); + // [EndExclude immersive] + // Set the main window to immersive and hide the navigation bar. + windowStage.getMainWindowSync().setWindowLayoutFullScreen(true); + windowStage.getMainWindowSync().setWindowSystemBarEnable([]); + + // [StartExclude immersive] + // Set the display direction of the main window. + let windowClass: window.Window | undefined = undefined; + // [EndExclude onAvoidAreaChange] + windowStage.getMainWindow((err: BusinessError, data) => { + // [StartExclude onAvoidAreaChange] + if (err.code) { + Logger.error('Failed to obtain the main window. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + windowClass = data; + // [Start set_rotate] + // Automatically rotate vertically following the sensor. + let orientation = window.Orientation.AUTO_ROTATION_PORTRAIT; + try { + windowClass.setPreferredOrientation(orientation, (err: BusinessError) => { + if (err.code) { + Logger.error('Failed to set window orientation. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + Logger.info('Succeeded in setting window orientation. Data: %{public}s'); + }) + } catch (exception) { + Logger.error('Failed to set window orientation. Cause: %{public}s', JSON.stringify(exception) ?? ''); + } + // [End set_rotate] + }) + // [EndExclude get_breakPoint] + // [EndExclude onAvoidAreaChange] windowStage.loadContent('pages/Index', (err, data) => { + // [StartExclude onAvoidAreaChange] + // [StartExclude get_breakPoint] if (err.code) { Logger.error('Failed to load the content. Cause: ' + JSON.stringify(err)); return; } Logger.info('Succeeded in loading the content. Data: ' + JSON.stringify(data)); + // [EndExclude get_breakPoint] + windowStage.getMainWindow((err: BusinessError, data) => { + if (err.code) { + Logger.error('Failed to obtain the main window. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + + // Determine the type of attribute value and process it + this.uiContext = data.getUIContext(); + let widthBp: WidthBreakpoint = this.uiContext.getWindowWidthBreakpoint(); + let heightBp: HeightBreakpoint = this.uiContext.getWindowHeightBreakpoint(); + AppStorage.setOrCreate('currentWidthBreakpoint', widthBp); + AppStorage.setOrCreate('currentHeightBreakpoint', heightBp); + data.on('windowSizeChange', this.onWindowSizeChange); + // [EndExclude onAvoidAreaChange] + // [StartExclude get_breakPoint] + // Monitor changes in the location of the cutout area. + let avoidArea: window.AvoidArea = data.getWindowAvoidArea(window.AvoidAreaType.TYPE_CUTOUT); + this.onAvoidAreaChange({ type: window.AvoidAreaType.TYPE_CUTOUT, area: avoidArea }); + data.on('avoidAreaChange', this.onAvoidAreaChange); + // [EndExclude get_breakPoint] + }) }); + // [EndExclude immersive] } + // [End immersive] + // [End onAvoidAreaChange] + // [StartExclude get_breakPoint] onWindowStageDestroy() { // Main window is destroyed, release UI related resources Logger.info('Ability onWindowStageDestroy'); @@ -53,4 +139,6 @@ export default class EntryAbility extends UIAbility { // Ability has back to background Logger.info('%{public}s', 'Ability onBackground'); } -}; \ No newline at end of file + // [EndExclude get_breakPoint] +}; +// [End get_breakPoint] \ No newline at end of file diff --git a/entry/src/main/ets/pages/Index.ets b/entry/src/main/ets/pages/Index.ets index 1f31fabef5c3ed96e9e0076d0d47f0f61fffbef7..76c8ec684ff6fb45e40993f773ab6bcf9cdb36b5 100644 --- a/entry/src/main/ets/pages/Index.ets +++ b/entry/src/main/ets/pages/Index.ets @@ -13,86 +13,230 @@ * limitations under the License. */ +import { LengthMetrics, window } from '@kit.ArkUI'; +import { hilog } from '@kit.PerformanceAnalysisKit'; import { Logger } from '../utils/Logger'; import tetrahedron_napi from 'libtetrahedron_napi.so'; import { RotationType } from '../utils/Constants'; +interface Areas { + top: number, + right: number, + bottom: number, + left: number, + heightBreakpoint: number, + widthBreakpoint: number +} + +// [Start get_safeAreaPixel] +function getTop(avoidArea: window.AvoidAreaOptions | undefined): number { + let result: number = 0; + if (avoidArea !== undefined) { + if (avoidArea.area.topRect.height) { + result = avoidArea.area.topRect.top + avoidArea.area.topRect.height; + } + } else { + hilog.error(0x0000, '3D', 'Can not get TopSafeAreaPixel, avoidArea visible false'); + } + return result; +} + +function getBottom(avoidArea: window.AvoidAreaOptions | undefined, windowHeight: number): number { + let result: number = 0; + if (avoidArea !== undefined) { + if (avoidArea.area.bottomRect.height) { + result = windowHeight - avoidArea.area.bottomRect.top; + } + } else { + hilog.error(0x0000, '3D', 'Can not get BottomSafeAreaPixel, avoidArea visible false'); + } + return result; +} + +function getLeft(avoidArea: window.AvoidAreaOptions | undefined): number { + let result: number = 0; + if (avoidArea !== undefined) { + if (avoidArea.area.leftRect.width) { + result = avoidArea.area.leftRect.left + avoidArea.area.leftRect.width; + } + } else { + hilog.error(0x0000, '3D', 'Can not get LeftSafeAreaPixel, avoidArea visible false'); + } + return result; +} + +function getRight(avoidArea: window.AvoidAreaOptions | undefined, windowWidth: number): number { + let result: number = 0; + if (avoidArea !== undefined) { + if (avoidArea.area.rightRect.width) { + result = windowWidth - avoidArea.area.rightRect.left; + } + } else { + hilog.error(0x0000, '3D', 'Can not get RightSafeAreaPixel, avoidArea visible false'); + } + return result; +} +// [End get_safeAreaPixel] + +// [Start breakPoint2Native] @Entry @Component struct Index { + // [StartExclude breakPoint2Native] @State angleArray: Array = new Array(); @State enableRotate: boolean = false; + // [EndExclude breakPoint2Native] + // Define the variables passed into the Native side. + @State cutoutAreas: Areas = { + top: 0, + right: 0, + bottom: 0, + left: 0, + heightBreakpoint: 0, + widthBreakpoint: 0 + }; + // [StartExclude breakPoint2Native] + // [Start padding] + @State localPadding: LocalizedPadding = { top: LengthMetrics.vp(0), start: LengthMetrics.vp(0) }; + // [StartExclude padding] + // [EndExclude breakPoint2Native] + // Watching the changes in horizontal and vertical breakpoint values. + @StorageLink('currentHeightBreakpoint') @Watch('breakPointChange') heightBreakpoint: HeightBreakpoint = + HeightBreakpoint.HEIGHT_SM; + @StorageLink('currentWidthBreakpoint') @Watch('breakPointChange') widthBreakpoint: WidthBreakpoint = + WidthBreakpoint.WIDTH_XS; + // [StartExclude breakPoint2Native] + // [EndExclude padding] + // [Start get_avoidAreas] + @StorageLink('cutout') @Watch('cutoutChange') avoidAreas: window.AvoidAreaOptions | undefined = undefined; + // [StartExclude padding] + @StorageLink('windowHeight') windowHeight: number = 0; + @StorageLink('windowWidth') windowWidth: number = 0; + // [StartExclude get_avoidAreas] private xComponentId = 'tetrahedron'; private panOption: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.All }); + // [EndExclude breakPoint2Native] + + // Breakpoint change, triggering value transfer. + breakPointChange() { + this.cutoutAreas.heightBreakpoint = this.heightBreakpoint; + this.cutoutAreas.widthBreakpoint = this.widthBreakpoint; + // Encapsulate the Native method and pass in a breakpoint. + tetrahedron_napi.objectPassing(this.cutoutAreas); + } + // [EndExclude padding] + // [EndExclude get_avoidAreas] + // [StartExclude breakPoint2Native] + cutoutChange() { + let topPX = getTop(this.avoidAreas); + let rightPX = getRight(this.avoidAreas, this.windowWidth); + let bottomPX = getBottom(this.avoidAreas, this.windowHeight); + let leftPX = getLeft(this.avoidAreas); + + // [StartExclude padding] + // [StartExclude get_avoidAreas] + this.cutoutAreas = { + top: topPX, + right: rightPX, + bottom: bottomPX, + left: leftPX, + heightBreakpoint: this.heightBreakpoint, + widthBreakpoint: this.widthBreakpoint + }; + // [EndExclude padding] + + this.localPadding = { + top: LengthMetrics.px(topPX), + end: LengthMetrics.px(rightPX), + bottom: LengthMetrics.px(bottomPX), + start: LengthMetrics.px(leftPX) + } + // ArkTS2Native + tetrahedron_napi.objectPassing(this.cutoutAreas); + // [EndExclude get_avoidAreas] + } + // [End get_avoidAreas] + // [StartExclude padding] async aboutToAppear() { Logger.info('aboutToAppear'); this.angleArray[0] = 30; this.angleArray[1] = 45; + + let topPX = getTop(this.avoidAreas); + let rightPX = getRight(this.avoidAreas, this.windowWidth); + let bottomPX = getBottom(this.avoidAreas, this.windowHeight); + let leftPX = getLeft(this.avoidAreas); + + this.cutoutAreas = { + top: topPX, + right: rightPX, + bottom: bottomPX, + left: leftPX, + heightBreakpoint: this.heightBreakpoint, + widthBreakpoint: this.widthBreakpoint + }; + + this.localPadding = { + top: LengthMetrics.px(topPX), + end: LengthMetrics.px(rightPX), + bottom: LengthMetrics.px(bottomPX), + start: LengthMetrics.px(leftPX) + } + + tetrahedron_napi.objectPassing(this.cutoutAreas); } + // [EndExclude padding] build() { - Column() { - Text($r('app.string.EntryAbility_desc')) - .fontSize($r('app.float.head_font_24')) - .lineHeight($r('app.float.wh_value_33')) - .fontFamily('HarmonyHeiTi-Bold') - .fontWeight(FontWeight.Bold) - .fontColor($r('app.color.font_color_182431')) - .textOverflow({ overflow: TextOverflow.Ellipsis }) - .textAlign(TextAlign.Start) - .margin({ top: $r('app.float.wh_value_13'), bottom: $r('app.float.wh_value_15') }) - - Text() { - Span($r('app.string.x_shaft_rotation')) - Span(this.angleArray[0].toString() + '°\n') - Span($r('app.string.y_shaft_rotation')) - Span(this.angleArray[1].toString() + '°') - } - .fontSize($r('app.float.head_font_24')) - .lineHeight($r('app.float.wh_value_33')) - .fontFamily('HarmonyHeiTi-Bold') - .fontWeight(FontWeight.Bold) - .fontColor($r('app.color.font_color_182431')) - .textOverflow({ overflow: TextOverflow.Ellipsis }) - .textAlign(TextAlign.Start) - .margin({ top: $r('app.float.wh_value_13'), bottom: $r('app.float.wh_value_15') }) - + // [StartExclude padding] + Stack({ alignContent: Alignment.Bottom }) { Column() { - XComponent({ - id: this.xComponentId, - type: XComponentType.SURFACE, - libraryname: 'tetrahedron_napi' - }) - .onLoad(() => { - Logger.info('onLoad'); - }) - .width($r('app.float.wh_value_360')) - .height($r('app.float.wh_value_360')) - .key('tetrahedron') - .onDestroy(() => { - Logger.info('onDestroy'); + Column() { + XComponent({ + id: this.xComponentId, + type: XComponentType.SURFACE, + libraryname: 'tetrahedron_napi' }) - .id('xComponent') - .backgroundColor(Color.White) + .onLoad(() => { + Logger.info('onLoad'); + }) + .key('tetrahedron') + .onDestroy(() => { + Logger.info('onDestroy'); + }) + .id('xComponent') + .backgroundColor(Color.White) + } + .justifyContent(FlexAlign.SpaceAround) + .alignItems(HorizontalAlign.Center) + .height('100%') + .width('100%') + .backgroundColor(Color.White) + .borderRadius(24) } - .justifyContent(FlexAlign.SpaceAround) - .alignItems(HorizontalAlign.Center) - .height('70%') - .width('100%') - .backgroundColor(Color.White) - .borderRadius(24) + .gesture( + PanGesture(this.panOption) + .onActionStart(() => { + Logger.info('Gesture onActionStart'); + }) + .onActionUpdate((event: GestureEvent) => { + tetrahedron_napi.setRotate(RotationType.STOP); + this.enableRotate = false; + this.angleArray = tetrahedron_napi.updateAngle(event.offsetX, event.offsetY); + Logger.info('Gesture onActionUpdate : offSet ' + event.offsetX + ',' + event.offsetY); + }) + .onActionEnd(() => { + Logger.info('Gesture onActionEnd'); + }) + ) + .height('100%') + .justifyContent(FlexAlign.SpaceBetween) Row({ space: 12 }) { Button(this.enableRotate ? $r('app.string.btn_stop_rotation') : $r('app.string.btn_auto_rotation'), { type: ButtonType.Capsule, stateEffect: true }) - .fontSize($r('app.float.head_font_24')) - .margin({ - top: $r('app.float.wh_value_13'), - bottom: $r('app.float.wh_value_15'), - left: $r('app.float.wh_value_13'), - right: $r('app.float.wh_value_13') - }) + .fontSize(16) .onClick(() => { if (this.enableRotate) { tetrahedron_napi.setRotate(RotationType.STOP); @@ -105,38 +249,24 @@ struct Index { .layoutWeight(1) Button($r('app.string.btn_damping_rotation'), { type: ButtonType.Capsule, stateEffect: true }) - .fontSize($r('app.float.head_font_24')) - .margin({ - top: $r('app.float.wh_value_13'), - bottom: $r('app.float.wh_value_15'), - left: $r('app.float.wh_value_13'), - right: $r('app.float.wh_value_13') - }) + .fontSize(16) .onClick(() => { this.enableRotate = false; tetrahedron_napi.setRotate(RotationType.DAMPING); }) .layoutWeight(1) } + .hitTestBehavior(HitTestMode.Transparent) + .padding({ + left: this.widthBreakpoint === WidthBreakpoint.WIDTH_SM ? 12 : 24, + right: this.widthBreakpoint === WidthBreakpoint.WIDTH_SM ? 12 : 24, + bottom: 50 + }) } - .gesture( - PanGesture(this.panOption) - .onActionStart((event: GestureEvent) => { - Logger.info('Gesture onActionStart'); - }) - .onActionUpdate((event: GestureEvent) => { - tetrahedron_napi.setRotate(RotationType.STOP); - this.enableRotate = false; - this.angleArray = tetrahedron_napi.updateAngle(event.offsetX, event.offsetY); - Logger.info('Gesture onActionUpdate : offSet ' + event.offsetX + ',' + event.offsetY); - }) - .onActionEnd(() => { - Logger.info('Gesture onActionEnd'); - }) - ) - .padding(12) - .backgroundColor($r('sys.color.comp_background_focus')) - .height('100%') - .justifyContent(FlexAlign.SpaceBetween) + // [EndExclude padding] + .padding(this.localPadding) } + // [End padding] + // [EndExclude breakPoint2Native] } +// [End breakPoint2Native] diff --git a/entry/src/main/ets/utils/Logger.ets b/entry/src/main/ets/utils/Logger.ets index c5d59679b494196d5a9c6a65f6e9d9da14afaf89..8ef4e550105c89b93a55c04464a94f9aabc567ff 100644 --- a/entry/src/main/ets/utils/Logger.ets +++ b/entry/src/main/ets/utils/Logger.ets @@ -13,33 +13,33 @@ * limitations under the License. */ -import { hilog } from '@kit.PerformanceAnalysisKit' +import { hilog } from '@kit.PerformanceAnalysisKit'; class LoggerModel { - private domain: number - private prefix: string - private format: string = '%{public}s' + private domain: number; + private prefix: string; + private format: string = '%{public}s'; constructor(prefix: string) { - this.prefix = prefix - this.domain = 0xFF00 + this.prefix = prefix; + this.domain = 0xFF00; } debug(...args: string[]) { - hilog.debug(this.domain, this.prefix, this.format, args) + hilog.debug(this.domain, this.prefix, this.format, args); } info(...args: string[]) { - hilog.info(this.domain, this.prefix, this.format, args) + hilog.info(this.domain, this.prefix, this.format, args); } warn(...args: string[]) { - hilog.warn(this.domain, this.prefix, this.format, args) + hilog.warn(this.domain, this.prefix, this.format, args); } error(...args: string[]) { - hilog.error(this.domain, this.prefix, this.format, args) + hilog.error(this.domain, this.prefix, this.format, args); } } -export let Logger = new LoggerModel('[Sample_OpenGL]') \ No newline at end of file +export let Logger = new LoggerModel('[Sample_OpenGL]'); \ No newline at end of file diff --git a/entry/src/main/module.json5 b/entry/src/main/module.json5 index eb02efbf8be966d5609f6a818bc6c75273fc24a0..cc3a198956886aaf2e02f2f228c8d6391ff52241 100644 --- a/entry/src/main/module.json5 +++ b/entry/src/main/module.json5 @@ -20,7 +20,8 @@ "description": "$string:module_desc", "mainElement": "EntryAbility", "deviceTypes": [ - "phone" + "phone", + "tablet" ], "deliveryWithInstall": true, "installationFree": false, diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 index f70ecd4112d94f9aa555adf898d53f18bf58f3e9..c76bd97133ae529a19be728265f4c43ece19e4ba 100644 --- a/hvigor/hvigor-config.json5 +++ b/hvigor/hvigor-config.json5 @@ -1,5 +1,5 @@ { - "modelVersion": "5.0.0", + "modelVersion": "5.0.4", "dependencies": { } } \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 index 533586d39e03acfa2f0dc36d915445a01a93a008..1c36108f7b706945168bfd59be6424aaa37fc596 100644 --- a/oh-package.json5 +++ b/oh-package.json5 @@ -1,5 +1,5 @@ { - "modelVersion": "5.0.0", + "modelVersion": "5.0.4", "license": "", "devDependencies": { "@ohos/hypium": "1.0.15" diff --git a/screenshots/device/tablet.png b/screenshots/device/tablet.png new file mode 100644 index 0000000000000000000000000000000000000000..1b992b9bf3c964e654418d155238c7a43749b429 Binary files /dev/null and b/screenshots/device/tablet.png differ diff --git "a/screenshots/device/\345\233\276\347\211\2071.png" "b/screenshots/device/\345\233\276\347\211\2071.png" new file mode 100644 index 0000000000000000000000000000000000000000..0f6eda1efafc22d70cd0e5730ea131b635100a22 Binary files /dev/null and "b/screenshots/device/\345\233\276\347\211\2071.png" differ diff --git "a/screenshots/device/\345\233\276\347\211\2074.png" "b/screenshots/device/\345\233\276\347\211\2074.png" new file mode 100644 index 0000000000000000000000000000000000000000..01b3d3cf12fc9cd162d0ad9d6ecc32546564806d Binary files /dev/null and "b/screenshots/device/\345\233\276\347\211\2074.png" differ