• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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