1 /*
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <ani.h>
16 #include <array>
17 #include <iostream>
18
19 #include "base/log/log_wrapper.h"
20 #include "base/memory/referenced.h"
21 #include "core/components_ng/event/focus_hub.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 #include "frameworks/bridge/common/utils/engine_helper.h"
24 #include "frameworks/core/common/container.h"
25
26 namespace {
ANIUtils_ANIStringToStdString(ani_env * env,ani_string ani_str)27 std::string ANIUtils_ANIStringToStdString(ani_env* env, ani_string ani_str)
28 {
29 ani_size strSize;
30 env->String_GetUTF8Size(ani_str, &strSize);
31
32 std::vector<char> buffer(strSize + 1); // +1 for null terminator
33 char* utf8Buffer = buffer.data();
34
35 ani_size bytes_written = 0;
36 env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytes_written);
37
38 utf8Buffer[bytes_written] = '\0';
39 std::string content = std::string(utf8Buffer);
40 return content;
41 }
42 } // namespace
43
GetBooleanValue(ani_env * env,ani_object object,bool & value)44 static bool GetBooleanValue(ani_env* env, ani_object object, bool& value)
45 {
46 ani_boolean isUndefined;
47 env->Reference_IsUndefined(object, &isUndefined);
48 if (!isUndefined) {
49 return false;
50 }
51 ani_boolean aniValue;
52 if (ANI_OK != env->Object_CallMethodByName_Boolean(object, "unboxed", nullptr, &aniValue)) {
53 return false;
54 }
55 value = static_cast<bool>(aniValue);
56 return true;
57 }
58
clearFocus(ani_env * env,ani_object object)59 static void clearFocus(ani_env* env, [[maybe_unused]] ani_object object)
60 {
61 OHOS::Ace::NG::FocusHub::LostFocusToViewRoot();
62 }
63
requestFocus(ani_env * env,ani_string key)64 static void requestFocus([[maybe_unused]] ani_env* env, ani_string key)
65 {
66 auto keyStr = ANIUtils_ANIStringToStdString(env, key);
67 auto focusCallback = [env](OHOS::Ace::NG::RequestFocusResult result) {
68 switch (result) {
69 case OHOS::Ace::NG::RequestFocusResult::NON_FOCUSABLE:
70 LOGI("This component is not focusable");
71 break;
72 case OHOS::Ace::NG::RequestFocusResult::NON_FOCUSABLE_ANCESTOR:
73 LOGI("This component has unfocusable ancestor.");
74 break;
75 case OHOS::Ace::NG::RequestFocusResult::NON_EXIST:
76 LOGI("The component doesn't exist, is currently invisible, or has been disabled.");
77 break;
78 default:
79 LOGI("An internal error occurred.");
80 break;
81 }
82 };
83 LOGI("focuscontroller requestFocus key %{public}s", keyStr.c_str());
84 auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
85 CHECK_NULL_VOID(pipeline);
86 auto focusManager = pipeline->GetOrCreateFocusManager();
87 CHECK_NULL_VOID(focusManager);
88 focusManager->SetRequestFocusCallback(focusCallback);
89 pipeline->RequestFocus(keyStr, true);
90 focusManager->ResetRequestFocusCallback();
91 }
92
activate(ani_env * env,ani_object isActive,ani_object autoInactive)93 static void activate(ani_env* env, ani_object isActive, ani_object autoInactive)
94 {
95 bool isActiveValue = false;
96 bool autoInactiveValue = true;
97
98 GetBooleanValue(env, isActive, isActiveValue);
99 GetBooleanValue(env, autoInactive, autoInactiveValue);
100
101 auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
102 CHECK_NULL_VOID(pipeline);
103 pipeline->SetIsFocusActive(isActiveValue, OHOS::Ace::NG::FocusActiveReason::USE_API, autoInactiveValue);
104 }
105
setAutoFocusTransfer(ani_env * env,ani_object isAutoFocusTransfer)106 static void setAutoFocusTransfer(ani_env* env, ani_object isAutoFocusTransfer)
107 {
108 bool isAutoFocusTransferValue = true;
109 if (!GetBooleanValue(env, isAutoFocusTransfer, isAutoFocusTransferValue)) {
110 return;
111 }
112 auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
113 CHECK_NULL_VOID(pipeline);
114 auto focusManager = pipeline->GetOrCreateFocusManager();
115 CHECK_NULL_VOID(focusManager);
116 focusManager->SetIsAutoFocusTransfer(isAutoFocusTransferValue);
117 }
118
setKeyProcessingMode(ani_env * env,ani_enum_item mode)119 static void setKeyProcessingMode(ani_env* env, ani_enum_item mode)
120 {
121 ani_int keyProcessingMode;
122 if (ANI_OK != env->EnumItem_GetValue_Int(mode, &keyProcessingMode)) {
123 LOGE("focuscontroller setKeyProcessingMode get modeType failed");
124 return;
125 }
126 auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContext();
127 CHECK_NULL_VOID(pipeline);
128 auto focusManager = pipeline->GetOrCreateFocusManager();
129 CHECK_NULL_VOID(focusManager);
130 focusManager->SetKeyProcessingMode(static_cast<OHOS::Ace::NG::KeyProcessingMode>(keyProcessingMode));
131 }
132
ANI_Constructor(ani_vm * vm,uint32_t * result)133 ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result)
134 {
135 if (vm == nullptr) {
136 return ANI_ERROR;
137 }
138 ani_env* env;
139 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
140 return ANI_ERROR;
141 }
142
143 ani_namespace ns;
144 if (ANI_OK != env->FindNamespace("L@ohos/arkui/focusController/focusController;", &ns)) {
145 return ANI_ERROR;
146 }
147 std::array methods = {
148 ani_native_function { "clearFocus", nullptr, reinterpret_cast<void*>(clearFocus) },
149 ani_native_function { "requestFocus", nullptr, reinterpret_cast<void*>(requestFocus) },
150 ani_native_function { "activate", nullptr, reinterpret_cast<void*>(activate) },
151 ani_native_function { "setAutoFocusTransfer", nullptr, reinterpret_cast<void*>(setAutoFocusTransfer) },
152 ani_native_function { "setKeyProcessingMode", nullptr, reinterpret_cast<void*>(setKeyProcessingMode) },
153 };
154 if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
155 return ANI_ERROR;
156 }
157
158 *result = ANI_VERSION_1;
159 return ANI_OK;
160 }