• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2023 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 "core/components_ng/gestures/recognizers/click_recognizer.h"
17 
18 #include "base/geometry/offset.h"
19 #include "base/log/log.h"
20 #include "base/ressched/ressched_report.h"
21 #include "base/utils/utils.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components_ng/gestures/base_gesture_event.h"
24 #include "core/components_ng/gestures/gesture_referee.h"
25 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
26 #include "core/components_ng/gestures/recognizers/multi_fingers_recognizer.h"
27 #include "core/pipeline_ng/pipeline_context.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 
32 int32_t MULTI_FINGER_TIMEOUT = 300;
33 constexpr int32_t MULTI_FINGER_TIMEOUT_TOUCH = 300;
34 constexpr int32_t MULTI_FINGER_TIMEOUT_MOUSE = 300;
35 int32_t MULTI_TAP_TIMEOUT = 300;
36 constexpr int32_t MULTI_TAP_TIMEOUT_TOUCH = 300;
37 constexpr int32_t MULTI_TAP_TIMEOUT_MOUSE = 300;
38 constexpr int32_t MAX_THRESHOLD_MANYTAP = 60;
39 constexpr int32_t MAX_TAP_FINGERS = 10;
40 constexpr double MAX_THRESHOLD = 20.0;
41 constexpr int32_t DEFAULT_TAP_FINGERS = 1;
42 constexpr int32_t DEFAULT_LONGPRESS_DURATION = 800000000;
43 
44 } // namespace
45 
ForceCleanRecognizer()46 void ClickRecognizer::ForceCleanRecognizer()
47 {
48     MultiFingersRecognizer::ForceCleanRecognizer();
49     OnResetStatus();
50 }
51 
IsPointInRegion(const TouchEvent & event)52 bool ClickRecognizer::IsPointInRegion(const TouchEvent& event)
53 {
54     if (distanceThreshold_ < std::numeric_limits<double>::infinity()) {
55         Offset offset = event.GetScreenOffset() - touchPoints_[event.id].GetScreenOffset();
56         if (offset.GetDistance() > distanceThreshold_) {
57             TAG_LOGI(AceLogTag::ACE_GESTURE, "Click move distance is larger than distanceThreshold_, "
58             "distanceThreshold_ is %{public}f", distanceThreshold_);
59             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
60             return false;
61         } else {
62             return true;
63         }
64     }
65     PointF localPoint(event.x, event.y);
66     auto frameNode = GetAttachedNode();
67     if (!frameNode.Invalid()) {
68         auto host = frameNode.Upgrade();
69         CHECK_NULL_RETURN(host, false);
70         NGGestureRecognizer::Transform(localPoint, frameNode, false, isPostEventResult_, event.postEventNodeId);
71         auto renderContext = host->GetRenderContext();
72         CHECK_NULL_RETURN(renderContext, false);
73         auto paintRect = renderContext->GetPaintRectWithoutTransform();
74         localPoint = localPoint + paintRect.GetOffset();
75         if (!host->InResponseRegionList(localPoint, responseRegionBuffer_)) {
76             TAG_LOGI(AceLogTag::ACE_GESTURE,
77                 "InputTracking id:%{public}d, this MOVE/UP event is out of region, try to reject click gesture",
78                 event.touchEventId);
79             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
80             return false;
81         }
82     }
83     return true;
84 }
85 
ClickRecognizer(int32_t fingers,int32_t count,double distanceThreshold)86 ClickRecognizer::ClickRecognizer(int32_t fingers, int32_t count, double distanceThreshold)
87     : MultiFingersRecognizer(fingers), count_(count), distanceThreshold_(distanceThreshold)
88 {
89     if (fingers_ > MAX_TAP_FINGERS || fingers_ < DEFAULT_TAP_FINGERS) {
90         fingers_ = DEFAULT_TAP_FINGERS;
91     }
92     if (distanceThreshold_ <= 0) {
93         distanceThreshold_ = std::numeric_limits<double>::infinity();
94     }
95 }
96 
InitGlobalValue(SourceType sourceType)97 void ClickRecognizer::InitGlobalValue(SourceType sourceType)
98 {
99     switch (sourceType) {
100         case SourceType::TOUCH:
101             MULTI_FINGER_TIMEOUT = MULTI_FINGER_TIMEOUT_TOUCH;
102             MULTI_TAP_TIMEOUT = MULTI_TAP_TIMEOUT_TOUCH;
103             break;
104         case SourceType::MOUSE:
105         case SourceType::TOUCH_PAD:
106             MULTI_FINGER_TIMEOUT = MULTI_FINGER_TIMEOUT_MOUSE;
107             MULTI_TAP_TIMEOUT = MULTI_TAP_TIMEOUT_MOUSE;
108             break;
109         default:
110             break;
111     }
112 }
113 
GetClickInfo()114 ClickInfo ClickRecognizer::GetClickInfo()
115 {
116     TouchEvent touchPoint = {};
117     if (!touchPoints_.empty()) {
118         touchPoint = touchPoints_.begin()->second;
119     }
120     ClickInfo info(touchPoint.id);
121     PointF localPoint(touchPoint.GetOffset().GetX(), touchPoint.GetOffset().GetY());
122     NGGestureRecognizer::Transform(localPoint, GetAttachedNode(), false,
123         isPostEventResult_, touchPoint.postEventNodeId);
124     Offset localOffset(localPoint.GetX(), localPoint.GetY());
125     info.SetTimeStamp(touchPoint.time);
126     info.SetScreenLocation(touchPoint.GetScreenOffset());
127     info.SetGlobalLocation(touchPoint.GetOffset()).SetLocalLocation(localOffset);
128     info.SetSourceDevice(deviceType_);
129     info.SetDeviceId(deviceId_);
130     info.SetTarget(GetEventTarget().value_or(EventTarget()));
131     info.SetForce(touchPoint.force);
132     auto frameNode = GetAttachedNode().Upgrade();
133     std::string patternName = "";
134     if (frameNode) {
135         patternName = frameNode->GetTag();
136     }
137     info.SetPatternName(patternName.c_str());
138     if (touchPoint.tiltX.has_value()) {
139         info.SetTiltX(touchPoint.tiltX.value());
140     }
141     if (touchPoint.tiltY.has_value()) {
142         info.SetTiltY(touchPoint.tiltY.value());
143     }
144     info.SetSourceTool(touchPoint.sourceTool);
145     return info;
146 }
147 
OnAccepted()148 void ClickRecognizer::OnAccepted()
149 {
150     int64_t acceptTime = GetSysTimestamp();
151     int64_t inputTime = acceptTime;
152     if (firstInputTime_.has_value()) {
153         inputTime = static_cast<int64_t>(firstInputTime_.value().time_since_epoch().count());
154     }
155     if (SystemProperties::GetTraceInputEventEnabled()) {
156         ACE_SCOPED_TRACE("UserEvent InputTime:%lld AcceptTime:%lld InputType:ClickGesture",
157             static_cast<long long>(inputTime), static_cast<long long>(acceptTime));
158     }
159     firstInputTime_.reset();
160 
161     auto node = GetAttachedNode().Upgrade();
162     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Click accepted, tag: %{public}s",
163         node ? node->GetTag().c_str() : "null");
164     if (onAccessibilityEventFunc_) {
165         onAccessibilityEventFunc_(AccessibilityEventType::CLICK);
166     }
167     refereeState_ = RefereeState::SUCCEED;
168     ResSchedReport::GetInstance().ResSchedDataReport("click");
169     TouchEvent touchPoint = {};
170     if (!touchPoints_.empty()) {
171         touchPoint = touchPoints_.begin()->second;
172     }
173     PointF localPoint(touchPoint.GetOffset().GetX(), touchPoint.GetOffset().GetY());
174     NGGestureRecognizer::Transform(localPoint, GetAttachedNode(), false,
175         isPostEventResult_, touchPoint.postEventNodeId);
176     Offset localOffset(localPoint.GetX(), localPoint.GetY());
177     if (onClick_) {
178         ClickInfo info = GetClickInfo();
179         onClick_(info);
180     }
181 
182     if (remoteMessage_) {
183         ClickInfo info = GetClickInfo();
184         info.SetTimeStamp(touchPoint.time);
185         info.SetGlobalLocation(touchPoint.GetOffset()).SetLocalLocation(localOffset);
186         remoteMessage_(info);
187     }
188     UpdateFingerListInfo();
189     SendCallbackMsg(onAction_);
190 
191     int64_t overTime = GetSysTimestamp();
192     if (SystemProperties::GetTraceInputEventEnabled()) {
193         ACE_SCOPED_TRACE("UserEvent InputTime:%lld OverTime:%lld InputType:ClickGesture",
194             static_cast<long long>(inputTime), static_cast<long long>(overTime));
195     }
196     firstInputTime_.reset();
197 }
198 
OnRejected()199 void ClickRecognizer::OnRejected()
200 {
201     SendRejectMsg();
202     refereeState_ = RefereeState::FAIL;
203     firstInputTime_.reset();
204 }
205 
HandleTouchDownEvent(const TouchEvent & event)206 void ClickRecognizer::HandleTouchDownEvent(const TouchEvent& event)
207 {
208     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW,
209         "Id:%{public}d, click %{public}d down, ETF: %{public}d, CTP: %{public}d, state: %{public}d",
210         event.touchEventId, event.id, equalsToFingers_, currentTouchPointsNum_, refereeState_);
211     if (!firstInputTime_.has_value()) {
212         firstInputTime_ = event.time;
213     }
214 
215     auto pipeline = PipelineBase::GetCurrentContext();
216     if (pipeline && pipeline->IsFormRender()) {
217         touchDownTime_ = event.time;
218     }
219     if (IsRefereeFinished()) {
220         auto node = GetAttachedNode().Upgrade();
221         TAG_LOGI(AceLogTag::ACE_GESTURE,
222             "Click recognizer handle touch down event refereeState is %{public}d, node tag = %{public}s, id = "
223             "%{public}s",
224             refereeState_, node ? node->GetTag().c_str() : "null",
225             node ? std::to_string(node->GetId()).c_str() : "invalid");
226         return;
227     }
228     InitGlobalValue(event.sourceType);
229     if (!IsInAttachedNode(event, false)) {
230         Adjudicate(Claim(this), GestureDisposal::REJECT);
231         return;
232     }
233     // The last recognition sequence has been completed, reset the timer.
234     if (tappedCount_ > 0 && currentTouchPointsNum_ == 0) {
235         responseRegionBuffer_.clear();
236         tapDeadlineTimer_.Cancel();
237     }
238     if (currentTouchPointsNum_ == 0) {
239         auto frameNode = GetAttachedNode();
240         if (!frameNode.Invalid()) {
241             auto host = frameNode.Upgrade();
242             responseRegionBuffer_ = host->GetResponseRegionListForRecognizer(static_cast<int32_t>(event.sourceType));
243         }
244     }
245     if (fingersId_.find(event.id) == fingersId_.end()) {
246         fingersId_.insert(event.id);
247         ++currentTouchPointsNum_;
248         touchPoints_[event.id] = event;
249     }
250     UpdateFingerListInfo();
251     if (fingers_ > currentTouchPointsNum_) {
252         // waiting for multi-finger press
253         DeadlineTimer(fingerDeadlineTimer_, MULTI_FINGER_TIMEOUT);
254     } else {
255         // Turn off the multi-finger press deadline timer
256         fingerDeadlineTimer_.Cancel();
257         equalsToFingers_ = true;
258         if (ExceedSlop()) {
259             TAG_LOGW(AceLogTag::ACE_GESTURE, "Fail to detect multi finger tap due to offset is out of slop");
260             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
261         }
262     }
263     if (currentTouchPointsNum_ == fingers_) {
264         focusPoint_ = ComputeFocusPoint();
265     }
266 }
267 
IsFormRenderClickRejected(const TouchEvent & event)268 bool ClickRecognizer::IsFormRenderClickRejected(const TouchEvent& event)
269 {
270     Offset offset = event.GetScreenOffset() - touchPoints_[event.id].GetScreenOffset();
271     if (event.time.time_since_epoch().count() - touchDownTime_.time_since_epoch().count() >
272         DEFAULT_LONGPRESS_DURATION || offset.GetDistance() > MAX_THRESHOLD) {
273         TAG_LOGI(AceLogTag::ACE_GESTURE, "reject click when up, offset is %{public}f",
274             static_cast<float>(offset.GetDistance()));
275         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
276         return true;
277     }
278     return false;
279 }
280 
TriggerClickAccepted(const TouchEvent & event)281 void ClickRecognizer::TriggerClickAccepted(const TouchEvent& event)
282 {
283     TAG_LOGI(AceLogTag::ACE_GESTURE, "Click try accept");
284     time_ = event.time;
285     if (!useCatchMode_) {
286         OnAccepted();
287         return;
288     }
289     auto onGestureJudgeBeginResult = TriggerGestureJudgeCallback();
290     if (onGestureJudgeBeginResult == GestureJudgeResult::REJECT) {
291         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
292         return;
293     }
294     Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
295 }
296 
HandleTouchUpEvent(const TouchEvent & event)297 void ClickRecognizer::HandleTouchUpEvent(const TouchEvent& event)
298 {
299     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Id:%{public}d, click %{public}d up, state: %{public}d", event.touchEventId,
300         event.id, refereeState_);
301     auto pipeline = PipelineBase::GetCurrentContext();
302     // In a card scenario, determine the interval between finger pressing and finger lifting. Delete this section of
303     // logic when the formal scenario is complete.
304     if (pipeline && pipeline->IsFormRender() && IsFormRenderClickRejected(event)) {
305         return;
306     }
307     if (IsRefereeFinished()) {
308         return;
309     }
310     InitGlobalValue(event.sourceType);
311     touchPoints_[event.id] = event;
312     UpdateFingerListInfo();
313     auto isUpInRegion = IsPointInRegion(event);
314     if (fingersId_.find(event.id) != fingersId_.end()) {
315         fingersId_.erase(event.id);
316         --currentTouchPointsNum_;
317     }
318     if (currentTouchPointsNum_ == 0) {
319         responseRegionBuffer_.clear();
320     }
321     bool fingersNumberSatisfied = equalsToFingers_;
322     // Check whether multi-finger taps are completed in count_ times
323     if (equalsToFingers_ && (currentTouchPointsNum_ == 0) && isUpInRegion) {
324         // Turn off the multi-finger lift deadline timer
325         fingerDeadlineTimer_.Cancel();
326         tappedCount_++;
327         if (tappedCount_ == count_) {
328             TriggerClickAccepted(event);
329             return;
330         }
331         equalsToFingers_ = false;
332         // waiting for multi-finger lift
333         DeadlineTimer(tapDeadlineTimer_, MULTI_TAP_TIMEOUT);
334     }
335     if (refereeState_ != RefereeState::PENDING && refereeState_ != RefereeState::FAIL) {
336         if (fingersNumberSatisfied) {
337             Adjudicate(AceType::Claim(this), GestureDisposal::PENDING);
338         } else {
339             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
340         }
341     }
342     if (currentTouchPointsNum_ < fingers_ && equalsToFingers_) {
343         DeadlineTimer(fingerDeadlineTimer_, MULTI_FINGER_TIMEOUT);
344     }
345 }
346 
HandleTouchMoveEvent(const TouchEvent & event)347 void ClickRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
348 {
349     if (currentFingers_ < fingers_) {
350         return;
351     }
352     if (IsRefereeFinished()) {
353         return;
354     }
355     InitGlobalValue(event.sourceType);
356     // In form scenario, if move more than 20vp, reject click gesture.
357     // Remove form scenario when formal solution is completed.
358     auto pipeline = PipelineBase::GetCurrentContext();
359     if (pipeline && pipeline->IsFormRender()) {
360         Offset offset = event.GetScreenOffset() - touchPoints_[event.id].GetScreenOffset();
361         if (offset.GetDistance() > MAX_THRESHOLD) {
362             TAG_LOGI(AceLogTag::ACE_GESTURE, "This gesture is out of offset, try to reject it");
363             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
364         }
365     }
366     IsPointInRegion(event);
367     UpdateFingerListInfo();
368 }
369 
HandleTouchCancelEvent(const TouchEvent & event)370 void ClickRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
371 {
372     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Id:%{public}d, click %{public}d cancel", event.touchEventId, event.id);
373     if (IsRefereeFinished()) {
374         return;
375     }
376     InitGlobalValue(event.sourceType);
377     Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
378 }
379 
HandleOverdueDeadline()380 void ClickRecognizer::HandleOverdueDeadline()
381 {
382     if (currentTouchPointsNum_ < fingers_ || tappedCount_ < count_) {
383         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
384     }
385 }
386 
DeadlineTimer(CancelableCallback<void ()> & deadlineTimer,int32_t time)387 void ClickRecognizer::DeadlineTimer(CancelableCallback<void()>& deadlineTimer, int32_t time)
388 {
389     auto context = PipelineContext::GetCurrentContext();
390     CHECK_NULL_VOID(context);
391 
392     auto&& callback = [weakPtr = AceType::WeakClaim(this)]() {
393         auto refPtr = weakPtr.Upgrade();
394         if (refPtr) {
395             refPtr->HandleOverdueDeadline();
396         }
397     };
398 
399     deadlineTimer.Reset(callback);
400     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
401     taskExecutor.PostDelayedTask(deadlineTimer, time, "ArkUIGestureClickDeadlineTimer");
402 }
403 
ComputeFocusPoint()404 Offset ClickRecognizer::ComputeFocusPoint()
405 {
406     Offset sumOfPoints;
407     int32_t count = 0;
408     for (auto& element : touchPoints_) {
409         if (count >= fingers_) {
410             break;
411         }
412         sumOfPoints = sumOfPoints + element.second.GetOffset();
413         count++;
414     }
415     Offset focusPoint = sumOfPoints / count;
416     return focusPoint;
417 }
418 
ExceedSlop()419 bool ClickRecognizer::ExceedSlop()
420 {
421     if (tappedCount_ > 0 && tappedCount_ < count_) {
422         Offset currentFocusPoint = ComputeFocusPoint();
423         Offset slop = currentFocusPoint - focusPoint_;
424         if (GreatOrEqual(PipelineBase::Px2VpWithCurrentDensity(slop.GetDistance()), MAX_THRESHOLD_MANYTAP)) {
425             return true;
426         }
427     }
428     return false;
429 }
430 
GetGestureEventInfo()431 GestureEvent ClickRecognizer::GetGestureEventInfo()
432 {
433     GestureEvent info;
434     info.SetTimeStamp(time_);
435     info.SetFingerList(fingerList_);
436     TouchEvent touchPoint = {};
437     for (const auto& pointKeyVal : touchPoints_) {
438         auto pointVal = pointKeyVal.second;
439         if (pointVal.sourceType != SourceType::NONE) {
440             touchPoint = pointVal;
441             break;
442         }
443     }
444     PointF localPoint(touchPoint.GetOffset().GetX(), touchPoint.GetOffset().GetY());
445     NGGestureRecognizer::Transform(localPoint, GetAttachedNode(), false,
446         isPostEventResult_, touchPoint.postEventNodeId);
447     info.SetTimeStamp(touchPoint.time);
448     info.SetScreenLocation(touchPoint.GetScreenOffset());
449     info.SetGlobalLocation(touchPoint.GetOffset()).SetLocalLocation(Offset(localPoint.GetX(), localPoint.GetY()));
450     info.SetSourceDevice(deviceType_);
451     info.SetDeviceId(deviceId_);
452     info.SetTarget(GetEventTarget().value_or(EventTarget()));
453     info.SetForce(touchPoint.force);
454     auto frameNode = GetAttachedNode().Upgrade();
455     std::string patternName = "";
456     if (frameNode) {
457         patternName = frameNode->GetTag();
458     }
459     info.SetPatternName(patternName.c_str());
460 
461     if (touchPoint.tiltX.has_value()) {
462         info.SetTiltX(touchPoint.tiltX.value());
463     }
464     if (touchPoint.tiltY.has_value()) {
465         info.SetTiltY(touchPoint.tiltY.value());
466     }
467     info.SetSourceTool(touchPoint.sourceTool);
468 #ifdef SECURITY_COMPONENT_ENABLE
469     info.SetDisplayX(touchPoint.screenX);
470     info.SetDisplayY(touchPoint.screenY);
471 #endif
472     info.SetPointerEvent(lastPointEvent_);
473     info.SetPressedKeyCodes(touchPoint.pressedKeyCodes_);
474     return info;
475 }
476 
SendCallbackMsg(const std::unique_ptr<GestureEventFunc> & onAction)477 void ClickRecognizer::SendCallbackMsg(const std::unique_ptr<GestureEventFunc>& onAction)
478 {
479     if (gestureInfo_ && gestureInfo_->GetDisposeTag()) {
480         return;
481     }
482     if (onAction && *onAction) {
483         GestureEvent info = GetGestureEventInfo();
484         // onAction may be overwritten in its invoke so we copy it first
485         auto onActionFunction = *onAction;
486         onActionFunction(info);
487     }
488 }
489 
TriggerGestureJudgeCallback()490 GestureJudgeResult ClickRecognizer::TriggerGestureJudgeCallback()
491 {
492     auto targetComponent = GetTargetComponent();
493     CHECK_NULL_RETURN(targetComponent, GestureJudgeResult::CONTINUE);
494     auto gestureRecognizerJudgeFunc = targetComponent->GetOnGestureRecognizerJudgeBegin();
495     auto callback = targetComponent->GetOnGestureJudgeBeginCallback();
496     if (!callback && !sysJudge_ && !gestureRecognizerJudgeFunc) {
497         return GestureJudgeResult::CONTINUE;
498     }
499     auto info = std::make_shared<TapGestureEvent>();
500     info->SetTimeStamp(time_);
501     info->SetDeviceId(deviceId_);
502     info->SetFingerList(fingerList_);
503     TouchEvent touchPoint = {};
504     if (!touchPoints_.empty()) {
505         touchPoint = touchPoints_.begin()->second;
506     }
507     info->SetSourceDevice(deviceType_);
508     info->SetTarget(GetEventTarget().value_or(EventTarget()));
509     info->SetForce(touchPoint.force);
510     if (gestureInfo_) {
511         gestureInfo_->SetInputEventType(inputEventType_);
512     }
513     if (touchPoint.tiltX.has_value()) {
514         info->SetTiltX(touchPoint.tiltX.value());
515     }
516     if (touchPoint.tiltY.has_value()) {
517         info->SetTiltY(touchPoint.tiltY.value());
518     }
519     info->SetSourceTool(touchPoint.sourceTool);
520     if (sysJudge_) {
521         return sysJudge_(gestureInfo_, info);
522     }
523     if (gestureRecognizerJudgeFunc) {
524         return gestureRecognizerJudgeFunc(info, Claim(this), responseLinkRecognizer_);
525     }
526     return callback(gestureInfo_, info);
527 }
528 
ReconcileFrom(const RefPtr<NGGestureRecognizer> & recognizer)529 bool ClickRecognizer::ReconcileFrom(const RefPtr<NGGestureRecognizer>& recognizer)
530 {
531     RefPtr<ClickRecognizer> curr = AceType::DynamicCast<ClickRecognizer>(recognizer);
532     if (!curr) {
533         ResetStatus();
534         return false;
535     }
536 
537     if (curr->count_ != count_ || curr->fingers_ != fingers_ || curr->priorityMask_ != priorityMask_ ||
538         curr->distanceThreshold_ != distanceThreshold_) {
539         ResetStatus();
540         return false;
541     }
542 
543     onAction_ = std::move(curr->onAction_);
544     return true;
545 }
546 
Dump() const547 RefPtr<GestureSnapshot> ClickRecognizer::Dump() const
548 {
549     RefPtr<GestureSnapshot> info = NGGestureRecognizer::Dump();
550     std::stringstream oss;
551     oss << "count: " << count_ << ", "
552         << "fingers: " << fingers_;
553     info->customInfo = oss.str();
554     return info;
555 }
556 
CreateGestureFromRecognizer() const557 RefPtr<Gesture> ClickRecognizer::CreateGestureFromRecognizer() const
558 {
559     return AceType::MakeRefPtr<TapGesture>(count_, fingers_, distanceThreshold_);
560 }
561 
CleanRecognizerState()562 void ClickRecognizer::CleanRecognizerState()
563 {
564     if ((refereeState_ == RefereeState::SUCCEED ||
565         refereeState_ == RefereeState::FAIL ||
566         refereeState_ == RefereeState::DETECTING) &&
567         currentFingers_ == 0) {
568         tappedCount_ = 0;
569         refereeState_ = RefereeState::READY;
570         disposal_ = GestureDisposal::NONE;
571     }
572 }
573 } // namespace OHOS::Ace::NG
574