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 "gesture_observer.h"
17
18 #include "gesture_observer_listener.h"
19 #include "interfaces/napi/kits/observer/ui_observer_listener.h"
20
21 #include "bridge/common/utils/engine_helper.h"
22
23 namespace OHOS::Ace::Napi {
24
25 constexpr napi_status NAPI_ERR = napi_status::napi_generic_failure;
26 constexpr size_t ARG_COUNT_ONE = 1;
27 constexpr size_t ARG_COUNT_TWO = 2;
28 constexpr size_t ARG_COUNT_THREE = 3;
29
30 constexpr uint32_t WILL_START = 0;
31 constexpr uint32_t WILL_END = 1;
32
33 static constexpr size_t PARAM_INDEX_ZERO = 0;
34 static constexpr size_t PARAM_INDEX_ONE = 1;
35 static constexpr size_t PARAM_INDEX_TWO = 2;
36
37 std::unordered_map<NG::GestureListenerType,
38 std::unordered_map<NG::GestureActionPhase, std::list<std::shared_ptr<UIObserverListener>>>>
39 GestureObserver::GestureObserverListeners_;
40 std::mutex GestureObserver::mutex_;
41
42 bool GestureObserver::isGestureHandleFuncSetted_ = false;
43
ParseGestureListenerType(napi_env env,napi_value value,NG::GestureListenerType & outType)44 bool ParseGestureListenerType(napi_env env, napi_value value, NG::GestureListenerType& outType)
45 {
46 if (!GestureObserverListener::MatchValueType(env, value, napi_number)) {
47 return false;
48 }
49
50 uint32_t typeValue;
51 napi_get_value_uint32(env, value, &typeValue);
52
53 switch (typeValue) {
54 case static_cast<uint32_t>(NG::GestureListenerType::TAP):
55 case static_cast<uint32_t>(NG::GestureListenerType::LONG_PRESS):
56 case static_cast<uint32_t>(NG::GestureListenerType::PAN):
57 case static_cast<uint32_t>(NG::GestureListenerType::PINCH):
58 case static_cast<uint32_t>(NG::GestureListenerType::SWIPE):
59 case static_cast<uint32_t>(NG::GestureListenerType::ROTATION):
60 outType = static_cast<NG::GestureListenerType>(typeValue);
61 return true;
62 default:
63 return false;
64 }
65 }
66
ParseGestureActionPhase(napi_env env,napi_value value,NG::GestureActionPhase & outPhase,NG::GestureListenerType gestureListenerType)67 bool ParseGestureActionPhase(
68 napi_env env, napi_value value, NG::GestureActionPhase& outPhase, NG::GestureListenerType gestureListenerType)
69 {
70 if (!GestureObserverListener::MatchValueType(env, value, napi_number)) {
71 return false;
72 }
73
74 uint32_t phaseValue;
75 napi_get_value_uint32(env, value, &phaseValue);
76 switch (phaseValue) {
77 case static_cast<uint32_t>(NG::GestureActionPhase::WILL_START):
78 outPhase = static_cast<NG::GestureActionPhase>(phaseValue);
79 return true;
80 case static_cast<uint32_t>(NG::GestureActionPhase::WILL_END):
81 if (gestureListenerType == NG::GestureListenerType::TAP ||
82 gestureListenerType == NG::GestureListenerType::SWIPE) {
83 return false;
84 }
85 outPhase = static_cast<NG::GestureActionPhase>(phaseValue);
86 return true;
87 default:
88 return false;
89 }
90 }
91
ParseActionPhasesArray(napi_env env,napi_value arrayValue,std::unordered_set<NG::GestureActionPhase> & outPhases,NG::GestureListenerType gestureListenerType)92 bool ParseActionPhasesArray(napi_env env, napi_value arrayValue, std::unordered_set<NG::GestureActionPhase>& outPhases,
93 NG::GestureListenerType gestureListenerType)
94 {
95 bool isArray;
96 napi_is_array(env, arrayValue, &isArray);
97 if (!isArray) {
98 return false;
99 }
100
101 uint32_t arrayLength;
102 napi_get_array_length(env, arrayValue, &arrayLength);
103 for (uint32_t i = 0; i < arrayLength; ++i) {
104 napi_value element;
105 napi_get_element(env, arrayValue, i, &element);
106
107 NG::GestureActionPhase phase;
108 if (!ParseGestureActionPhase(env, element, phase, gestureListenerType)) {
109 continue;
110 }
111 outPhases.insert(phase);
112 }
113 return !outPhases.empty();
114 }
115
ParseGestureObserverConfigs(napi_env env,napi_value configValue,std::unordered_set<NG::GestureActionPhase> & outPhase,NG::GestureListenerType gestureListenerType)116 bool ParseGestureObserverConfigs(napi_env env, napi_value configValue,
117 std::unordered_set<NG::GestureActionPhase>& outPhase, NG::GestureListenerType gestureListenerType)
118 {
119 napi_valuetype valueType;
120 napi_typeof(env, configValue, &valueType);
121 if (valueType != napi_object) {
122 return false;
123 }
124
125 napi_value actionPhasesValue;
126 napi_status status = napi_get_named_property(env, configValue, "actionPhases", &actionPhasesValue);
127 if (status != napi_ok) {
128 return false;
129 }
130
131 if (!ParseActionPhasesArray(env, actionPhasesValue, outPhase, gestureListenerType)) {
132 return false;
133 }
134 return true;
135 }
136
AddGestureListenerType(napi_env env)137 napi_value AddGestureListenerType(napi_env env)
138 {
139 napi_value gestureListenerType = nullptr;
140 napi_value prop = nullptr;
141 NAPI_CALL(env, napi_create_object(env, &gestureListenerType));
142 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::TAP), &prop));
143 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "TAP", prop));
144 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::LONG_PRESS), &prop));
145 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "LONG_PRESS", prop));
146 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::PAN), &prop));
147 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "PAN", prop));
148 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::PINCH), &prop));
149 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "PINCH", prop));
150 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::SWIPE), &prop));
151 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "SWIPE", prop));
152 NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(NG::GestureListenerType::ROTATION), &prop));
153 NAPI_CALL(env, napi_set_named_property(env, gestureListenerType, "ROTATION", prop));
154 return gestureListenerType;
155 }
156
AddGestureActionPhase(napi_env env)157 napi_value AddGestureActionPhase(napi_env env)
158 {
159 napi_value gestureActionPhase = nullptr;
160 napi_value prop = nullptr;
161 NAPI_CALL(env, napi_create_object(env, &gestureActionPhase));
162 NAPI_CALL(env, napi_create_uint32(env, WILL_START, &prop));
163 NAPI_CALL(env, napi_set_named_property(env, gestureActionPhase, "WILL_START", prop));
164 NAPI_CALL(env, napi_create_uint32(env, WILL_END, &prop));
165 NAPI_CALL(env, napi_set_named_property(env, gestureActionPhase, "WILL_END", prop));
166 return gestureActionPhase;
167 }
168
DefineGestureObserver(napi_env env,napi_value exports)169 napi_status GestureObserver::DefineGestureObserver(napi_env env, napi_value exports)
170 {
171 napi_property_descriptor gestureObserverDesc[] = {
172 DECLARE_NAPI_FUNCTION("addGlobalGestureListener", AddGlobalGestureListener),
173 DECLARE_NAPI_FUNCTION("removeGlobalGestureListener", RemoveGlobalGestureListener),
174 DECLARE_NAPI_PROPERTY("GestureListenerType", AddGestureListenerType(env)),
175 DECLARE_NAPI_PROPERTY("GestureActionPhase", AddGestureActionPhase(env)),
176 };
177 NAPI_CALL_BASE(env,
178 napi_define_properties(
179 env, exports, sizeof(gestureObserverDesc) / sizeof(gestureObserverDesc[0]), gestureObserverDesc),
180 NAPI_ERR);
181 return napi_ok;
182 }
183
AddGlobalGestureListener(napi_env env,napi_callback_info info)184 napi_value GestureObserver::AddGlobalGestureListener(napi_env env, napi_callback_info info)
185 {
186 napi_handle_scope scope = nullptr;
187 NAPI_CALL(env, napi_open_handle_scope(env, &scope));
188 size_t argc = ARG_COUNT_THREE;
189 napi_value argv[ARG_COUNT_THREE] = { nullptr };
190 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
191 if (argc != ARG_COUNT_THREE) {
192 napi_close_handle_scope(env, scope);
193 LOGE("Invalid arguments");
194 return nullptr;
195 }
196 NG::GestureListenerType gestureListenerType;
197 if (!ParseGestureListenerType(env, argv[PARAM_INDEX_ZERO], gestureListenerType)) {
198 napi_close_handle_scope(env, scope);
199 return nullptr;
200 }
201 std::unordered_set<NG::GestureActionPhase> outPhases;
202 if (!ParseGestureObserverConfigs(env, argv[PARAM_INDEX_ONE], outPhases, gestureListenerType)) {
203 napi_close_handle_scope(env, scope);
204 return nullptr;
205 }
206
207 if (!isGestureHandleFuncSetted_) {
208 NG::UIObserverHandler::GetInstance().SetHandleGestureHandleFunc(&HandleGestureAccept);
209 isGestureHandleFuncSetted_ = true;
210 }
211
212 auto listener = std::make_shared<UIObserverListener>(env, argv[PARAM_INDEX_TWO]);
213 RegisterGlobalGestureListener(gestureListenerType, outPhases, listener);
214 napi_close_handle_scope(env, scope);
215 return nullptr;
216 }
217
RemoveGlobalGestureListener(napi_env env,napi_callback_info info)218 napi_value GestureObserver::RemoveGlobalGestureListener(napi_env env, napi_callback_info info)
219 {
220 napi_handle_scope scope = nullptr;
221 NAPI_CALL(env, napi_open_handle_scope(env, &scope));
222 size_t argc = ARG_COUNT_TWO;
223 napi_value argv[ARG_COUNT_TWO] = { nullptr };
224 napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
225 if (argc != ARG_COUNT_ONE && argc != ARG_COUNT_TWO) {
226 napi_close_handle_scope(env, scope);
227 LOGE("Invalid arguments");
228 return nullptr;
229 }
230 NG::GestureListenerType gestureListenerType;
231 if (!ParseGestureListenerType(env, argv[PARAM_INDEX_ZERO], gestureListenerType)) {
232 napi_close_handle_scope(env, scope);
233 return nullptr;
234 }
235 if (argc == ARG_COUNT_ONE) {
236 UnRegisterGlobalGestureListener(gestureListenerType, nullptr);
237 }
238 if (argc == ARG_COUNT_TWO && GestureObserverListener::MatchValueType(env, argv[PARAM_INDEX_ONE], napi_function)) {
239 UnRegisterGlobalGestureListener(gestureListenerType, argv[PARAM_INDEX_ONE]);
240 }
241 napi_close_handle_scope(env, scope);
242 return nullptr;
243 }
244
RegisterGlobalGestureListener(NG::GestureListenerType gestureListenerType,const std::unordered_set<NG::GestureActionPhase> & outPhase,const std::shared_ptr<UIObserverListener> & listener)245 void GestureObserver::RegisterGlobalGestureListener(NG::GestureListenerType gestureListenerType,
246 const std::unordered_set<NG::GestureActionPhase>& outPhase, const std::shared_ptr<UIObserverListener>& listener)
247 {
248 std::lock_guard<std::mutex> lock(mutex_);
249 auto iter = GestureObserverListeners_.find(gestureListenerType);
250 if (iter == GestureObserverListeners_.end()) {
251 std::unordered_map<NG::GestureActionPhase, std::list<std::shared_ptr<UIObserverListener>>> phaseMap;
252 for (const auto& phase : outPhase) {
253 phaseMap.emplace(phase, std::list<std::shared_ptr<UIObserverListener>>({ listener }));
254 }
255 GestureObserverListeners_.emplace(gestureListenerType, std::move(phaseMap));
256 return;
257 }
258 auto& holder = iter->second;
259 for (const auto& phase : outPhase) {
260 auto phaseIter = holder.find(phase);
261 if (phaseIter == holder.end()) {
262 holder.emplace(phase, std::list<std::shared_ptr<UIObserverListener>> { listener });
263 } else {
264 auto& listenerList = phaseIter->second;
265 if (std::find(listenerList.begin(), listenerList.end(), listener) != listenerList.end()) {
266 continue;
267 }
268 listenerList.emplace_back(listener);
269 }
270 }
271 }
272
UnRegisterGlobalGestureListener(NG::GestureListenerType gestureListenerType,napi_value callback)273 void GestureObserver::UnRegisterGlobalGestureListener(NG::GestureListenerType gestureListenerType, napi_value callback)
274 {
275 std::lock_guard<std::mutex> lock(mutex_);
276 auto iter = GestureObserverListeners_.find(gestureListenerType);
277 if (iter == GestureObserverListeners_.end()) {
278 return;
279 }
280 auto& phaseMap = iter->second;
281 if (callback == nullptr) {
282 phaseMap.clear();
283 return;
284 }
285
286 for (auto phaseIter = phaseMap.begin(); phaseIter != phaseMap.end();) {
287 auto& listenerList = phaseIter->second;
288 listenerList.erase(
289 std::remove_if(
290 listenerList.begin(),
291 listenerList.end(),
292 [callback](const std::shared_ptr<UIObserverListener>& registeredListener) {
293 return registeredListener->NapiEqual(callback);
294 }),
295 listenerList.end()
296 );
297 if (listenerList.empty()) {
298 phaseIter = phaseMap.erase(phaseIter);
299 } else {
300 ++phaseIter;
301 }
302 }
303
304 if (phaseMap.empty()) {
305 GestureObserverListeners_.erase(iter);
306 }
307 }
308
HandleGestureAccept(NG::GestureListenerType gestureListenerType,const GestureEvent & gestureEventInfo,const RefPtr<NG::NGGestureRecognizer> & current,const RefPtr<NG::FrameNode> & frameNode,NG::GestureActionPhase phase)309 void GestureObserver::HandleGestureAccept(NG::GestureListenerType gestureListenerType,
310 const GestureEvent& gestureEventInfo, const RefPtr<NG::NGGestureRecognizer>& current,
311 const RefPtr<NG::FrameNode>& frameNode, NG::GestureActionPhase phase)
312 {
313 std::lock_guard<std::mutex> lock(mutex_);
314 if (!current) {
315 TAG_LOGW(AceLogTag::ACE_OBSERVER, "Handle gesture accept failed, recognizer is null");
316 return;
317 }
318 auto env = GetCurrentNapiEnv();
319 napi_handle_scope scope = nullptr;
320 auto status = napi_open_handle_scope(env, &scope);
321 if (status != napi_ok) {
322 return;
323 }
324
325 auto iter = GestureObserverListeners_.find(gestureListenerType);
326 if (iter == GestureObserverListeners_.end()) {
327 napi_close_handle_scope(env, scope);
328 return;
329 }
330 auto& holder = iter->second;
331 auto phaseIter = holder.find(phase);
332 if (phaseIter == holder.end()) {
333 napi_close_handle_scope(env, scope);
334 return;
335 }
336 for (const auto& listener : phaseIter->second) {
337 listener->OnGestureStateChange(gestureListenerType, gestureEventInfo, current, frameNode, phase);
338 }
339 napi_close_handle_scope(env, scope);
340 }
341
GetCurrentNapiEnv()342 napi_env GestureObserver::GetCurrentNapiEnv()
343 {
344 auto engine = EngineHelper::GetCurrentEngine();
345 CHECK_NULL_RETURN(engine, nullptr);
346 NativeEngine* nativeEngine = engine->GetNativeEngine();
347 CHECK_NULL_RETURN(nativeEngine, nullptr);
348 return reinterpret_cast<napi_env>(nativeEngine);
349 }
350 } // namespace OHOS::Ace::Napi
351