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控件中进行展示。同时,点击自动旋转按钮可以自动旋转,点击阻尼旋转可以减速旋转直至停止旋转,还可以在屏幕上通过触摸滑动手势对三棱锥进行旋转,最终得到不同角度的图形并显示到页面。
### 效果预览
-| 首页 | 滑动屏幕旋转变换 |
-|:-----------------------------------------------------------------------------------:|:-------------------------------------------------------------------------------------:|
-|
|
|
+| 手机 | 折叠屏 | 平板 |
+|:-----:|:-------------------------------:|------------------------------------|
+|  |  |  |
使用说明
-应用界面中展示了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