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
16 #include <ani.h>
17 #include <string>
18 #include <unistd.h>
19
20 #include "base/log/log.h"
21 #include "base/memory/ace_type.h"
22 #include "core/components_ng/base/observer_handler.h"
23 #include "core/pipeline_ng/pipeline_context.h"
24
25 namespace {
26 // constexpr const char DENSITY_CHNAGE[] = "densityUpdate";
27 const char ANI_OBSERVER_NS[] = "L@ohos/arkui/observer/uiObserver;";
28 const char ANI_OBSERVER_CLS[] = "L@ohos/arkui/observer/uiObserver/UIObserverImpl;";
29 } // namespace
30 namespace OHOS::Ace {
31 class UiObserver {
32 public:
UiObserver(int32_t instanceId)33 explicit UiObserver(int32_t instanceId) : id_(instanceId) {}
34
CallJsFunction(ani_env * env,std::list<ani_ref> & cbList)35 void CallJsFunction(ani_env* env, std::list<ani_ref>& cbList)
36 {
37 std::vector<ani_ref> vec;
38 ani_ref fnReturnVal;
39 for (auto& cb : cbList) {
40 env->FunctionalObject_Call(reinterpret_cast<ani_fn_object>(cb), vec.size(), vec.data(), &fnReturnVal);
41 }
42 }
43
44 // UIObserver.on(type: "densityUpdate", uiContext | null, callback)
45 // register a listener on current page
RegisterDensityCallback(int32_t uiContextInstanceId,ani_ref & cb)46 void RegisterDensityCallback(int32_t uiContextInstanceId, ani_ref& cb)
47 {
48 id_ = uiContextInstanceId;
49 if (uiContextInstanceId == 0) {
50 uiContextInstanceId = Container::CurrentId();
51 }
52 auto iter = densityCbMap_.find(uiContextInstanceId);
53 if (iter == densityCbMap_.end()) {
54 densityCbMap_.emplace(uiContextInstanceId, std::list<ani_ref>({ cb }));
55 return;
56 }
57 auto& holder = iter->second;
58 if (std::find(holder.begin(), holder.end(), cb) != holder.end()) {
59 return;
60 }
61 holder.emplace_back(cb);
62 }
63 // UIObserver.off(type: "densityUpdate", uiContext | null, callback)
UnRegisterDensityCallback(ani_env * env,int32_t uiContextInstanceId,ani_ref & cb)64 void UnRegisterDensityCallback(ani_env* env, int32_t uiContextInstanceId, ani_ref& cb)
65 {
66 if (uiContextInstanceId == 0) {
67 uiContextInstanceId = Container::CurrentId();
68 }
69 auto iter = densityCbMap_.find(uiContextInstanceId);
70 if (iter == densityCbMap_.end()) {
71 return;
72 }
73 auto& holder = iter->second;
74 if (cb == nullptr) {
75 holder.clear();
76 return;
77 }
78 holder.erase(std::remove_if(
79 holder.begin(), holder.end(), [env, cb, this](ani_ref cb1) { return AniEqual(env, cb, cb1); }),
80 holder.end());
81 }
82
HandleDensityChange(ani_env * env,double density)83 void HandleDensityChange(ani_env* env, double density)
84 {
85 auto currentId = Container::CurrentId();
86 auto iter = densityCbMap_.find(currentId);
87 if (iter == densityCbMap_.end()) {
88 return;
89 }
90 auto& holder = iter->second;
91 std::vector<ani_ref> callbackParams;
92 ani_ref fnReturnVal;
93 ani_object res;
94 CreateDensityInfo(env, density, res);
95 callbackParams.emplace_back(res);
96 for (auto& cb : holder) {
97 env->FunctionalObject_Call(reinterpret_cast<ani_fn_object>(cb),
98 callbackParams.size(),
99 callbackParams.data(),
100 &fnReturnVal);
101 }
102 }
103
AniEqual(ani_env * env,ani_ref cb,ani_ref cb1)104 ani_boolean AniEqual(ani_env* env, ani_ref cb, ani_ref cb1)
105 {
106 ani_boolean isEquals = false;
107 env->Reference_StrictEquals(cb, cb1, &isEquals);
108 return isEquals;
109 }
110
CreateDensityInfo(ani_env * env,double density,ani_object & obj)111 void CreateDensityInfo(ani_env* env, double density, ani_object& obj)
112 {
113 static const char* className = "L@ohos/arkui/observer/uiObserver/DensityInfo;";
114 ani_class cls;
115 env->FindClass(className, &cls);
116 ani_method ctor;
117 env->Class_FindMethod(cls, "<ctor>", ":V", &ctor);
118 env->Object_New(cls, ctor, &obj);
119 env->Object_SetPropertyByName_Double(obj, "density", ani_double(density));
120 }
121
122 private:
123 int32_t id_;
124 std::unordered_map<int32_t, std::list<ani_ref>> densityCbMap_;
125 };
126
Unwrapp(ani_env * env,ani_object object)127 static UiObserver* Unwrapp(ani_env* env, ani_object object)
128 {
129 ani_long nativeAddr;
130 if (ANI_OK != env->Object_GetFieldByName_Long(object, "nativeObserverAddr", &nativeAddr)) {
131 return nullptr;
132 }
133 return reinterpret_cast<UiObserver*>(nativeAddr);
134 }
135
ANIUtils_ANIStringToStdString(ani_env * env,ani_string ani_str)136 std::string ANIUtils_ANIStringToStdString(ani_env* env, ani_string ani_str)
137 {
138 ani_size strSize;
139 env->String_GetUTF8Size(ani_str, &strSize);
140
141 std::vector<char> buffer(strSize + 1);
142 char* utf8Buffer = buffer.data();
143
144 ani_size bytes_written = 0;
145 env->String_GetUTF8(ani_str, utf8Buffer, strSize + 1, &bytes_written);
146
147 utf8Buffer[bytes_written] = '\0';
148 std::string content = std::string(utf8Buffer);
149 return content;
150 }
151
On(ani_env * env,ani_object object,ani_string type,ani_fn_object fnObj)152 static void On([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, ani_string type, ani_fn_object fnObj)
153 {
154 if (fnObj == nullptr) {
155 LOGE("observer-ani callback is undefined.");
156 return;
157 }
158 std::string typeStr = ANIUtils_ANIStringToStdString(env, type);
159 auto* observer = Unwrapp(env, object);
160 if (observer == nullptr) {
161 LOGE("observer-ani context is null.");
162 return;
163 }
164 ani_ref fnObjGlobalRef = nullptr;
165 env->GlobalReference_Create(reinterpret_cast<ani_ref>(fnObj), &fnObjGlobalRef);
166 observer->RegisterDensityCallback(100000, fnObjGlobalRef);
167 }
168
Off(ani_env * env,ani_object object,ani_string type,ani_fn_object fnObj)169 static void Off([[maybe_unused]] ani_env* env, [[maybe_unused]] ani_object object, ani_string type, ani_fn_object fnObj)
170 {
171 LOGE("lzr in off");
172 auto* observer = Unwrapp(env, object);
173 if (observer == nullptr) {
174 LOGE("observer-ani context is null.");
175 return;
176 }
177 ani_ref fnObjGlobalRef = nullptr;
178 env->GlobalReference_Create(reinterpret_cast<ani_ref>(fnObj), &fnObjGlobalRef);
179 observer->UnRegisterDensityCallback(env, 100000, fnObjGlobalRef);
180 }
181
CreateObserver(ani_env * env,ani_int id)182 static ani_object CreateObserver([[maybe_unused]] ani_env* env, ani_int id)
183 {
184 ani_class cls;
185 if (ANI_OK != env->FindClass(ANI_OBSERVER_CLS, &cls)) {
186 LOGE("observer-ani not found class");
187 return nullptr;
188 }
189
190 ani_method ctor;
191 if (ANI_OK != env->Class_FindMethod(cls, "<ctor>", nullptr, &ctor)) {
192 LOGE("observer-ani can not get construct method.");
193 return nullptr;
194 }
195
196 auto* observer = new UiObserver(id);
197 auto densityChangeCallback = [observer, env](NG::AbilityContextInfo& info, double density) {
198 observer->HandleDensityChange(env, density);
199 };
200 NG::UIObserverHandler::GetInstance().SetHandleDensityChangeFuncForAni(densityChangeCallback);
201 ani_object context_object;
202 if (ANI_OK != env->Object_New(cls, ctor, &context_object, reinterpret_cast<ani_long>(observer))) {
203 LOGE("observer-ani Can not new object.");
204 delete observer;
205 return nullptr;
206 }
207 return context_object;
208 }
209 } // namespace OHOS::Ace
ANI_ConstructorForAni(ani_env * env)210 bool ANI_ConstructorForAni(ani_env* env)
211 {
212 ani_namespace ns;
213 if (ANI_OK != env->FindNamespace(ANI_OBSERVER_NS, &ns)) {
214 LOGE("observer-ani Not found ns");
215 return false;
216 }
217 std::array methods = {
218 ani_native_function { "createUIObserver", nullptr, reinterpret_cast<void*>(OHOS::Ace::CreateObserver) },
219 };
220
221 if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
222 LOGE("observer-ani Namespace_BindNativeFunctions error");
223 return false;
224 }
225
226 ani_class clsObserver;
227 if (ANI_OK != env->FindClass(ANI_OBSERVER_CLS, &clsObserver)) {
228 LOGE("observer-ani not found class");
229 return false;
230 }
231
232 std::array methodsObserver = {
233 ani_native_function { "on", nullptr, reinterpret_cast<void*>(OHOS::Ace::On) },
234 ani_native_function { "off", nullptr, reinterpret_cast<void*>(OHOS::Ace::Off) },
235 };
236 if (ANI_OK != env->Class_BindNativeMethods(clsObserver, methodsObserver.data(), methodsObserver.size())) {
237 return false;
238 }
239 return true;
240 }
241
ANI_Constructor(ani_vm * vm,uint32_t * result)242 ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result)
243 {
244 ani_env* env;
245 if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
246 LOGE("observer-ani Unsupported ANI_VERSION_1");
247 return ANI_ERROR;
248 }
249 if (ANI_ConstructorForAni(env)) {
250 *result = ANI_VERSION_1;
251 return ANI_OK;
252 }
253 return ANI_ERROR;
254 }
255