• 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/long_press_recognizer.h"
17 
18 #include "base/perf/socperf_client.h"
19 #include "base/thread/frame_trace_adapter.h"
20 #include "base/utils/time_util.h"
21 #include "base/utils/utils.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components_ng/base/frame_node.h"
24 #include "core/components_ng/event/gesture_event_hub.h"
25 #include "core/components_ng/gestures/base_gesture_event.h"
26 #include "core/components_ng/gestures/gesture_referee.h"
27 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
28 #include "core/components_ng/gestures/recognizers/multi_fingers_recognizer.h"
29 #include "core/event/ace_events.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 constexpr double MAX_THRESHOLD = 15.0;
35 constexpr int32_t MAX_LONGPRESS_FINGERS = 10;
36 constexpr int32_t DEFAULT_LONGPRESS_FINGERS = 1;
37 constexpr int32_t DEFAULT_LONGPRESS_DURATION = 500;
38 } // namespace
39 
LongPressRecognizer(int32_t duration,int32_t fingers,bool repeat,bool isForDrag,bool isDisableMouseLeft)40 LongPressRecognizer::LongPressRecognizer(
41     int32_t duration, int32_t fingers, bool repeat, bool isForDrag, bool isDisableMouseLeft)
42     : MultiFingersRecognizer(fingers), duration_(duration), repeat_(repeat), isForDrag_(isForDrag),
43       isDisableMouseLeft_(isDisableMouseLeft)
44 {
45     if (fingers_ > MAX_LONGPRESS_FINGERS || fingers_ < DEFAULT_LONGPRESS_FINGERS) {
46         fingers_ = DEFAULT_LONGPRESS_FINGERS;
47     }
48     if (duration_ <= 0) {
49         duration_ = DEFAULT_LONGPRESS_DURATION;
50     }
51 }
52 
OnAccepted()53 void LongPressRecognizer::OnAccepted()
54 {
55     int64_t acceptTime = GetSysTimestamp();
56     int64_t inputTime = acceptTime;
57     if (firstInputTime_.has_value()) {
58         inputTime = static_cast<int64_t>(firstInputTime_.value().time_since_epoch().count());
59     }
60     if (SystemProperties::GetTraceInputEventEnabled()) {
61         ACE_SCOPED_TRACE("UserEvent InputTime:%lld AcceptTime:%lld InputType:LongPressGesture",
62             static_cast<long long>(inputTime), static_cast<long long>(acceptTime));
63     }
64 
65     auto node = GetAttachedNode().Upgrade();
66     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "LongPress accepted, tag = %{public}s",
67         node ? node->GetTag().c_str() : "null");
68     if (onAccessibilityEventFunc_) {
69         onAccessibilityEventFunc_(AccessibilityEventType::LONG_PRESS);
70     }
71     refereeState_ = RefereeState::SUCCEED;
72     if (onLongPress_ && !touchPoints_.empty()) {
73         TouchEvent trackPoint = touchPoints_.begin()->second;
74         PointF localPoint(trackPoint.GetOffset().GetX(), trackPoint.GetOffset().GetY());
75         NGGestureRecognizer::Transform(localPoint, GetAttachedNode(), false,
76             isPostEventResult_, trackPoint.postEventNodeId);
77         LongPressInfo info(trackPoint.id);
78         info.SetTimeStamp(time_);
79         info.SetScreenLocation(trackPoint.GetScreenOffset());
80         info.SetGlobalLocation(trackPoint.GetOffset()).SetLocalLocation(Offset(localPoint.GetX(), localPoint.GetY()));
81         info.SetTarget(GetEventTarget().value_or(EventTarget()));
82         onLongPress_(info);
83     }
84 
85     UpdateFingerListInfo();
86     SendCallbackMsg(onActionUpdate_, false);
87     SendCallbackMsg(onAction_, false, true);
88     if (repeat_) {
89         StartRepeatTimer();
90     }
91 }
92 
OnRejected()93 void LongPressRecognizer::OnRejected()
94 {
95     if (refereeState_ == RefereeState::SUCCEED) {
96         return;
97     }
98     SendRejectMsg();
99     refereeState_ = RefereeState::FAIL;
100     firstInputTime_.reset();
101 }
102 
ThumbnailTimer(int32_t time)103 void LongPressRecognizer::ThumbnailTimer(int32_t time)
104 {
105     auto context = PipelineContext::GetCurrentContext();
106     CHECK_NULL_VOID(context);
107     if (!callback_) {
108         return;
109     }
110     auto&& callback = [weakPtr = AceType::WeakClaim(this), customCallback = callback_]() {
111         auto refPtr = weakPtr.Upgrade();
112         if (!refPtr) {
113             return;
114         }
115         if (refPtr->refereeState_ == RefereeState::DETECTING) {
116             customCallback(Offset(refPtr->globalPoint_.GetX(), refPtr->globalPoint_.GetY()));
117         }
118     };
119     thumbnailTimer_.Reset(callback);
120     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
121     taskExecutor.PostDelayedTask(thumbnailTimer_, time, "ArkUIGestureLongPressThumbnailTimer");
122 }
123 
HandleTouchDownEvent(const TouchEvent & event)124 void LongPressRecognizer::HandleTouchDownEvent(const TouchEvent& event)
125 {
126     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Id:%{public}d, LongPress %{public}d down, state: %{public}d",
127         event.touchEventId, event.id, refereeState_);
128     if (!firstInputTime_.has_value()) {
129         firstInputTime_ = event.time;
130     }
131 
132     if (isDisableMouseLeft_ && event.sourceType == SourceType::MOUSE) {
133         TAG_LOGI(AceLogTag::ACE_GESTURE, "Mouse left button is disabled for long press recognizer");
134         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
135         return;
136     }
137 
138     if (!IsInAttachedNode(event)) {
139         Adjudicate(Claim(this), GestureDisposal::REJECT);
140         return;
141     }
142     int32_t curDuration = duration_;
143 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
144     if (!IsPostEventResult()) {
145         int64_t currentTimeStamp = GetSysTimestamp();
146         int64_t eventTimeStamp = static_cast<int64_t>(event.time.time_since_epoch().count());
147         if (currentTimeStamp > eventTimeStamp) {
148             // nanoseconds to millisceond.
149             curDuration = curDuration - static_cast<int32_t>((currentTimeStamp - eventTimeStamp) / (1000 * 1000));
150             curDuration = curDuration < 0 ? 0 : curDuration;
151         }
152     }
153 #endif
154 
155     if (isForDrag_ && event.sourceType == SourceType::MOUSE) {
156         curDuration = 0;
157     }
158     if ((touchRestrict_.forbiddenType & TouchRestrict::LONG_PRESS) == TouchRestrict::LONG_PRESS) {
159         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
160         return;
161     }
162     if (fingersId_.find(event.id) == fingersId_.end()) {
163         fingersId_.insert(event.id);
164     }
165     globalPoint_ = Point(event.x, event.y);
166     touchPoints_[event.id] = event;
167     lastTouchEvent_ = event;
168     UpdateFingerListInfo();
169     if (GetValidFingersCount() == fingers_) {
170         refereeState_ = RefereeState::DETECTING;
171         if (useCatchMode_) {
172             DeadlineTimer(curDuration, true);
173         } else {
174             DeadlineTimer(curDuration, false);
175         }
176     } else {
177         PrintCurrentFingersInfo();
178     }
179 
180     ThumbnailTimer(thumbnailDeadline);
181 }
182 
HandleTouchUpEvent(const TouchEvent & event)183 void LongPressRecognizer::HandleTouchUpEvent(const TouchEvent& event)
184 {
185     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Id:%{public}d, LongPress %{public}d up, state: %{public}d",
186         event.touchEventId, event.id, refereeState_);
187     auto context = PipelineContext::GetCurrentContext();
188     CHECK_NULL_VOID(context);
189     context->RemoveGestureTask(task_);
190     if (fingersId_.find(event.id) != fingersId_.end()) {
191         fingersId_.erase(event.id);
192     }
193     if (touchPoints_.find(event.id) != touchPoints_.end()) {
194         touchPoints_.erase(event.id);
195     }
196     lastTouchEvent_ = event;
197     if (refereeState_ == RefereeState::SUCCEED) {
198         SendCallbackMsg(onActionUpdate_, false);
199         if (static_cast<int32_t>(touchPoints_.size()) == 0) {
200             SendCallbackMsg(onActionEnd_, false);
201             int64_t overTime = GetSysTimestamp();
202             int64_t inputTime = overTime;
203             if (firstInputTime_.has_value()) {
204                 inputTime = static_cast<int64_t>(firstInputTime_.value().time_since_epoch().count());
205             }
206             if (SystemProperties::GetTraceInputEventEnabled()) {
207                 ACE_SCOPED_TRACE("UserEvent InputTime:%lld OverTime:%lld InputType:LongPressGesture",
208                     static_cast<long long>(inputTime), static_cast<long long>(overTime));
209             }
210             firstInputTime_.reset();
211         }
212     } else {
213         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
214     }
215 }
216 
HandleTouchMoveEvent(const TouchEvent & event)217 void LongPressRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
218 {
219     lastTouchEvent_.pressedKeyCodes_ = event.pressedKeyCodes_;
220     if (static_cast<int32_t>(touchPoints_.size()) < fingers_) {
221         return;
222     }
223     if (IsRefereeFinished()) {
224         return;
225     }
226     Offset offset = event.GetOffset() - touchPoints_[event.id].GetOffset();
227     if (offset.GetDistance() > MAX_THRESHOLD) {
228         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
229         return;
230     }
231     lastTouchEvent_ = event;
232     UpdateFingerListInfo();
233     time_ = event.time;
234 }
235 
HandleTouchCancelEvent(const TouchEvent & event)236 void LongPressRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
237 {
238     TAG_LOGI(AceLogTag::ACE_INPUTKEYFLOW, "Id:%{public}d, LongPress %{public}d cancel, TPS:%{public}d",
239         event.touchEventId, event.id, static_cast<int32_t>(touchPoints_.size()));
240     if (refereeState_ == RefereeState::FAIL) {
241         return;
242     }
243     lastTouchEvent_ = event;
244     if (touchPoints_.find(event.id) != touchPoints_.end()) {
245         touchPoints_.erase(event.id);
246     }
247     if (refereeState_ == RefereeState::SUCCEED && static_cast<int32_t>(touchPoints_.size()) == 0) {
248         SendCancelMsg();
249         refereeState_ = RefereeState::READY;
250     } else {
251         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
252     }
253 }
254 
HandleOverdueDeadline(bool isCatchMode)255 void LongPressRecognizer::HandleOverdueDeadline(bool isCatchMode)
256 {
257     if (refereeState_ != RefereeState::DETECTING) {
258         return;
259     }
260     if (!isCatchMode) {
261         OnAccepted();
262         return;
263     }
264     if (gestureInfo_ && gestureInfo_->GetType() == GestureTypeName::DRAG) {
265         auto dragEventActuator = GetDragEventActuator();
266         CHECK_NULL_VOID(dragEventActuator);
267         if (dragEventActuator->IsDragUserReject()) {
268             TAG_LOGI(AceLogTag::ACE_GESTURE, "Drag long press reject because of user's reject");
269             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
270             return;
271         }
272     }
273     auto onGestureJudgeBeginResult = TriggerGestureJudgeCallback();
274     if (onGestureJudgeBeginResult == GestureJudgeResult::REJECT) {
275         TAG_LOGI(AceLogTag::ACE_GESTURE, "Long press reject as judge result is reject");
276         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
277         if (gestureInfo_ && gestureInfo_->GetType() == GestureTypeName::DRAG) {
278             auto dragEventActuator = GetDragEventActuator();
279             CHECK_NULL_VOID(dragEventActuator);
280             dragEventActuator->SetIsDragUserReject(true);
281         }
282         return;
283     }
284     Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
285 }
286 
DeadlineTimer(int32_t time,bool isCatchMode)287 void LongPressRecognizer::DeadlineTimer(int32_t time, bool isCatchMode)
288 {
289     auto context = PipelineContext::GetCurrentContext();
290     CHECK_NULL_VOID(context);
291 
292     auto&& callback = [weakPtr = AceType::WeakClaim(this), isCatchMode]() {
293         auto refPtr = weakPtr.Upgrade();
294         if (refPtr) {
295             refPtr->HandleOverdueDeadline(isCatchMode);
296         }
297     };
298     task_ = { WeakClaim(this), GetSysTimestamp(), time, callback };
299     context->AddGestureTask(task_);
300 
301     auto&& flushCallback = []() {
302         auto context = PipelineContext::GetCurrentContext();
303         CHECK_NULL_VOID(context);
304         context->RequestFrame();
305     };
306     deadlineTimer_.Reset(flushCallback);
307     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
308     taskExecutor.PostDelayedTask(deadlineTimer_, time, "ArkUIGestureLongPressDeadlineTimer");
309 }
310 
DoRepeat()311 void LongPressRecognizer::DoRepeat()
312 {
313     if (static_cast<int32_t>(touchPoints_.size()) < fingers_) {
314         return;
315     }
316     if (refereeState_ == RefereeState::SUCCEED) {
317         SendCallbackMsg(onAction_, true, true);
318         StartRepeatTimer();
319     }
320 }
321 
StartRepeatTimer()322 void LongPressRecognizer::StartRepeatTimer()
323 {
324     auto context = PipelineContext::GetCurrentContext();
325     CHECK_NULL_VOID(context);
326 
327     auto&& callback = [weakPtr = AceType::WeakClaim(this)]() {
328         auto refPtr = weakPtr.Upgrade();
329         if (refPtr) {
330             refPtr->DoRepeat();
331         }
332     };
333     timer_.Reset(callback);
334     auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
335     taskExecutor.PostDelayedTask(timer_, duration_, "ArkUIGestureLongPressRepeatTimer");
336 }
337 
ConvertPxToVp(double offset) const338 double LongPressRecognizer::ConvertPxToVp(double offset) const
339 {
340     auto context = PipelineContext::GetCurrentContext();
341     CHECK_NULL_RETURN(context, offset);
342 
343     double vpOffset = context->ConvertPxToVp(Dimension(offset, DimensionUnit::PX));
344     return vpOffset;
345 }
346 
SendCallbackMsg(const std::unique_ptr<GestureEventFunc> & callback,bool isRepeat,bool isOnAction)347 void LongPressRecognizer::SendCallbackMsg(
348     const std::unique_ptr<GestureEventFunc>& callback, bool isRepeat, bool isOnAction)
349 {
350     if (gestureInfo_ && gestureInfo_->GetDisposeTag()) {
351         return;
352     }
353     if (callback && *callback) {
354         GestureEvent info;
355         info.SetTimeStamp(time_);
356         info.SetRepeat(isRepeat);
357         info.SetFingerList(fingerList_);
358         info.SetSourceDevice(deviceType_);
359         info.SetDeviceId(deviceId_);
360         info.SetTargetDisplayId(lastTouchEvent_.targetDisplayId);
361         info.SetGlobalPoint(globalPoint_);
362         info.SetScreenLocation(lastTouchEvent_.GetScreenOffset());
363         info.SetGlobalLocation(lastTouchEvent_.GetOffset())
364             .SetLocalLocation(lastTouchEvent_.GetOffset() - coordinateOffset_);
365         info.SetTarget(GetEventTarget().value_or(EventTarget()));
366         info.SetForce(lastTouchEvent_.force);
367         if (lastTouchEvent_.tiltX.has_value()) {
368             info.SetTiltX(lastTouchEvent_.tiltX.value());
369         }
370         if (lastTouchEvent_.tiltY.has_value()) {
371             info.SetTiltY(lastTouchEvent_.tiltY.value());
372         }
373         info.SetSourceTool(lastTouchEvent_.sourceTool);
374         info.SetPointerEvent(lastPointEvent_);
375         Platform::UpdatePressedKeyCodes(lastTouchEvent_.pressedKeyCodes_);
376         info.SetPressedKeyCodes(lastTouchEvent_.pressedKeyCodes_);
377         // callback may be overwritten in its invoke so we copy it first
378         auto callbackFunction = *callback;
379         callbackFunction(info);
380         if (isOnAction && longPressRecorder_ && *longPressRecorder_) {
381             (*longPressRecorder_)(info);
382         }
383     }
384 }
385 
OnResetStatus()386 void LongPressRecognizer::OnResetStatus()
387 {
388     MultiFingersRecognizer::OnResetStatus();
389     timer_.Cancel();
390     deadlineTimer_.Cancel();
391     auto context = PipelineContext::GetCurrentContext();
392     CHECK_NULL_VOID(context);
393     context->RemoveGestureTask(task_);
394 }
395 
ReconcileFrom(const RefPtr<NGGestureRecognizer> & recognizer)396 bool LongPressRecognizer::ReconcileFrom(const RefPtr<NGGestureRecognizer>& recognizer)
397 {
398     RefPtr<LongPressRecognizer> curr = AceType::DynamicCast<LongPressRecognizer>(recognizer);
399     if (!curr) {
400         ResetStatus();
401         return false;
402     }
403 
404     if (curr->duration_ != duration_ || curr->fingers_ != fingers_ || curr->repeat_ != repeat_ ||
405         curr->priorityMask_ != priorityMask_) {
406         if (refereeState_ == RefereeState::SUCCEED && static_cast<int32_t>(touchPoints_.size()) > 0) {
407             SendCancelMsg();
408         }
409         ResetStatus();
410         return false;
411     }
412 
413     onAction_ = std::move(curr->onAction_);
414     onActionEnd_ = std::move(curr->onActionEnd_);
415     onActionCancel_ = std::move(curr->onActionCancel_);
416 
417     return true;
418 }
419 
GetLongPressActionFunc()420 GestureEventFunc LongPressRecognizer::GetLongPressActionFunc()
421 {
422     auto callback = [weak = WeakClaim(this)](GestureEvent& info) {
423         auto longPressRecognizer = weak.Upgrade();
424         CHECK_NULL_VOID(longPressRecognizer);
425         if (longPressRecognizer->onActionUpdate_) {
426             (*(longPressRecognizer->onActionUpdate_))(info);
427         }
428         if (longPressRecognizer->onAction_) {
429             (*(longPressRecognizer->onAction_))(info);
430         }
431         if (longPressRecognizer->onActionUpdate_) {
432             (*(longPressRecognizer->onActionUpdate_))(info);
433         }
434         if (longPressRecognizer->onActionEnd_) {
435             (*(longPressRecognizer->onActionEnd_))(info);
436         }
437     };
438     return callback;
439 }
440 
Dump() const441 RefPtr<GestureSnapshot> LongPressRecognizer::Dump() const
442 {
443     RefPtr<GestureSnapshot> info = NGGestureRecognizer::Dump();
444     std::stringstream oss;
445     oss << "duration: " << duration_ << ", "
446         << "isForDrag: " << isForDrag_ << ", "
447         << "repeat: " << repeat_ << ", "
448         << "fingers: " << fingers_;
449     info->customInfo = oss.str();
450     return info;
451 }
452 
PrintCurrentFingersInfo() const453 void LongPressRecognizer::PrintCurrentFingersInfo() const
454 {
455     std::string log = "Fingers number = ";
456     log += std::to_string(GetValidFingersCount());
457     log += " fingers_ = ";
458     log += std::to_string(fingers_);
459     log += ". ";
460     for (const auto& iter : touchPoints_) {
461         log += "Event id = ";
462         log += std::to_string(iter.first);
463         log += ", event type = ";
464         log += std::to_string(static_cast<int32_t>(iter.second.type));
465         log += "; ";
466     }
467     TAG_LOGI(AceLogTag::ACE_GESTURE, "Finger info : %{public}s", log.c_str());
468 }
469 
TriggerGestureJudgeCallback()470 GestureJudgeResult LongPressRecognizer::TriggerGestureJudgeCallback()
471 {
472     auto targetComponent = GetTargetComponent();
473     CHECK_NULL_RETURN(targetComponent, GestureJudgeResult::CONTINUE);
474     auto gestureRecognizerJudgeFunc = targetComponent->GetOnGestureRecognizerJudgeBegin();
475     auto callback = targetComponent->GetOnGestureJudgeBeginCallback();
476     if (!callback && !gestureRecognizerJudgeFunc) {
477         return GestureJudgeResult::CONTINUE;
478     }
479     auto info = std::make_shared<LongPressGestureEvent>();
480     info->SetTimeStamp(time_);
481     info->SetDeviceId(deviceId_);
482     info->SetRepeat(repeat_);
483     info->SetFingerList(fingerList_);
484     TouchEvent trackPoint = {};
485     if (!touchPoints_.empty()) {
486         trackPoint = touchPoints_.begin()->second;
487     }
488     info->SetSourceDevice(deviceType_);
489     info->SetTarget(GetEventTarget().value_or(EventTarget()));
490     info->SetForce(trackPoint.force);
491     if (gestureInfo_) {
492         gestureInfo_->SetInputEventType(inputEventType_);
493     }
494     if (trackPoint.tiltX.has_value()) {
495         info->SetTiltX(trackPoint.tiltX.value());
496     }
497     if (trackPoint.tiltY.has_value()) {
498         info->SetTiltY(trackPoint.tiltY.value());
499     }
500     info->SetSourceTool(trackPoint.sourceTool);
501     if (gestureRecognizerJudgeFunc) {
502         return gestureRecognizerJudgeFunc(info, Claim(this), responseLinkRecognizer_);
503     }
504     return callback(gestureInfo_, info);
505 }
506 
GetDragEventActuator()507 RefPtr<DragEventActuator> LongPressRecognizer::GetDragEventActuator()
508 {
509     auto targetComponent = GetTargetComponent();
510     CHECK_NULL_RETURN(targetComponent, nullptr);
511     auto uiNode = targetComponent->GetUINode().Upgrade();
512     CHECK_NULL_RETURN(uiNode, nullptr);
513     auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
514     CHECK_NULL_RETURN(frameNode, nullptr);
515     auto gestureEventHub = frameNode->GetOrCreateGestureEventHub();
516     CHECK_NULL_RETURN(gestureEventHub, nullptr);
517     return gestureEventHub->GetDragEventActuator();
518 }
519 
520 } // namespace OHOS::Ace::NG
521