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