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 auto node = GetAttachedNode().Upgrade();
56 TAG_LOGI(AceLogTag::ACE_GESTURE, "Long press gesture has been accepted, node tag = %{public}s, id = %{public}s",
57 node ? node->GetTag().c_str() : "null", node ? std::to_string(node->GetId()).c_str() : "invalid");
58 if (onAccessibilityEventFunc_) {
59 onAccessibilityEventFunc_(AccessibilityEventType::LONG_PRESS);
60 }
61 refereeState_ = RefereeState::SUCCEED;
62 if (onLongPress_ && !touchPoints_.empty()) {
63 TouchEvent trackPoint = touchPoints_.begin()->second;
64 PointF localPoint(trackPoint.GetOffset().GetX(), trackPoint.GetOffset().GetY());
65 NGGestureRecognizer::Transform(localPoint, GetAttachedNode(), false, isPostEventResult_);
66 LongPressInfo info(trackPoint.id);
67 info.SetTimeStamp(time_);
68 info.SetScreenLocation(trackPoint.GetScreenOffset());
69 info.SetGlobalLocation(trackPoint.GetOffset()).SetLocalLocation(Offset(localPoint.GetX(), localPoint.GetY()));
70 info.SetTarget(GetEventTarget().value_or(EventTarget()));
71 onLongPress_(info);
72 }
73
74 UpdateFingerListInfo();
75 SendCallbackMsg(onActionUpdate_, false);
76 SendCallbackMsg(onAction_, false, true);
77 if (repeat_) {
78 StartRepeatTimer();
79 }
80 }
81
OnRejected()82 void LongPressRecognizer::OnRejected()
83 {
84 refereeState_ = RefereeState::FAIL;
85 }
86
ThumbnailTimer(int32_t time)87 void LongPressRecognizer::ThumbnailTimer(int32_t time)
88 {
89 auto context = PipelineContext::GetCurrentContext();
90 CHECK_NULL_VOID(context);
91 if (!callback_) {
92 return;
93 }
94 auto&& callback = [weakPtr = AceType::WeakClaim(this), customCallback = callback_]() {
95 auto refPtr = weakPtr.Upgrade();
96 if (!refPtr) {
97 return;
98 }
99 if (refPtr->refereeState_ == RefereeState::DETECTING) {
100 customCallback(Offset(refPtr->globalPoint_.GetX(), refPtr->globalPoint_.GetY()));
101 }
102 };
103 thumbnailTimer_.Reset(callback);
104 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
105 taskExecutor.PostDelayedTask(thumbnailTimer_, time);
106 }
107
HandleTouchDownEvent(const TouchEvent & event)108 void LongPressRecognizer::HandleTouchDownEvent(const TouchEvent& event)
109 {
110 if (isDisableMouseLeft_ && event.sourceType == SourceType::MOUSE) {
111 TAG_LOGI(AceLogTag::ACE_GESTURE, "Mouse left button is disabled for long press recognizer");
112 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
113 return;
114 }
115
116 TAG_LOGI(AceLogTag::ACE_GESTURE,
117 "Long press recognizer receives %{public}d touch down event, begin to detect long press event", event.id);
118 if (!IsInAttachedNode(event)) {
119 Adjudicate(Claim(this), GestureDisposal::REJECT);
120 return;
121 }
122 int32_t curDuration = duration_;
123 #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW)
124 if (!IsPostEventResult()) {
125 int64_t currentTimeStamp = GetSysTimestamp();
126 int64_t eventTimeStamp = static_cast<int64_t>(event.time.time_since_epoch().count());
127 if (currentTimeStamp > eventTimeStamp) {
128 TAG_LOGI(AceLogTag::ACE_GESTURE,
129 "CurrentTimeStamp is larger than eventTimeStamp, need to minus time spent waiting");
130 // nanoseconds to millisceond.
131 curDuration = curDuration - static_cast<int32_t>((currentTimeStamp - eventTimeStamp) / (1000 * 1000));
132 if (curDuration < 0) {
133 curDuration = 0;
134 }
135 }
136 }
137 #endif
138
139 if (isForDrag_ && event.sourceType == SourceType::MOUSE) {
140 curDuration = 0;
141 }
142 if ((touchRestrict_.forbiddenType & TouchRestrict::LONG_PRESS) == TouchRestrict::LONG_PRESS) {
143 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
144 return;
145 }
146 globalPoint_ = Point(event.x, event.y);
147 touchPoints_[event.id] = event;
148 UpdateFingerListInfo();
149 auto pointsCount = static_cast<int32_t>(touchPoints_.size());
150
151 if (pointsCount == fingers_) {
152 refereeState_ = RefereeState::DETECTING;
153 if (useCatchMode_) {
154 DeadlineTimer(curDuration, true);
155 } else {
156 DeadlineTimer(curDuration, false);
157 }
158 }
159
160 ThumbnailTimer(thumbnailDeadline);
161 }
162
HandleTouchUpEvent(const TouchEvent & event)163 void LongPressRecognizer::HandleTouchUpEvent(const TouchEvent& event)
164 {
165 TAG_LOGI(AceLogTag::ACE_GESTURE, "Long press recognizer receives touch up event");
166 auto context = PipelineContext::GetCurrentContext();
167 CHECK_NULL_VOID(context);
168 context->RemoveGestureTask(task_);
169 if (touchPoints_.find(event.id) != touchPoints_.end()) {
170 touchPoints_.erase(event.id);
171 }
172 if (refereeState_ == RefereeState::SUCCEED) {
173 SendCallbackMsg(onActionUpdate_, false);
174 if (static_cast<int32_t>(touchPoints_.size()) == 0) {
175 SendCallbackMsg(onActionEnd_, false);
176 }
177 } else {
178 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
179 }
180 }
181
HandleTouchMoveEvent(const TouchEvent & event)182 void LongPressRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
183 {
184 if (static_cast<int32_t>(touchPoints_.size()) < fingers_) {
185 return;
186 }
187 if (IsRefereeFinished()) {
188 return;
189 }
190 Offset offset = event.GetOffset() - touchPoints_[event.id].GetOffset();
191 if (offset.GetDistance() > MAX_THRESHOLD) {
192 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
193 return;
194 }
195 UpdateFingerListInfo();
196 time_ = event.time;
197 }
198
HandleTouchCancelEvent(const TouchEvent & event)199 void LongPressRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
200 {
201 TAG_LOGI(AceLogTag::ACE_GESTURE, "long press recognizer receives touch cancel event");
202 if (IsRefereeFinished()) {
203 return;
204 }
205 if (refereeState_ == RefereeState::SUCCEED) {
206 SendCancelMsg();
207 } else {
208 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
209 }
210 }
211
HandleOverdueDeadline(bool isCatchMode)212 void LongPressRecognizer::HandleOverdueDeadline(bool isCatchMode)
213 {
214 if (refereeState_ != RefereeState::DETECTING) {
215 return;
216 }
217 if (!isCatchMode) {
218 OnAccepted();
219 return;
220 }
221 if (gestureInfo_ && gestureInfo_->GetType() == GestureTypeName::DRAG) {
222 auto dragEventActuator = GetDragEventActuator();
223 CHECK_NULL_VOID(dragEventActuator);
224 if (dragEventActuator->IsDragUserReject()) {
225 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
226 return;
227 }
228 }
229 auto onGestureJudgeBeginResult = TriggerGestureJudgeCallback();
230 if (onGestureJudgeBeginResult == GestureJudgeResult::REJECT) {
231 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
232 if (gestureInfo_ && gestureInfo_->GetType() == GestureTypeName::DRAG) {
233 auto dragEventActuator = GetDragEventActuator();
234 CHECK_NULL_VOID(dragEventActuator);
235 dragEventActuator->SetIsDragUserReject(true);
236 }
237 return;
238 }
239 Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
240 }
241
DeadlineTimer(int32_t time,bool isCatchMode)242 void LongPressRecognizer::DeadlineTimer(int32_t time, bool isCatchMode)
243 {
244 auto context = PipelineContext::GetCurrentContext();
245 CHECK_NULL_VOID(context);
246
247 auto&& callback = [weakPtr = AceType::WeakClaim(this), isCatchMode]() {
248 auto refPtr = weakPtr.Upgrade();
249 if (refPtr) {
250 refPtr->HandleOverdueDeadline(isCatchMode);
251 }
252 };
253 task_ = { WeakClaim(this), GetSysTimestamp(), time, callback };
254 context->AddGestureTask(task_);
255
256 auto&& flushCallback = []() {
257 auto context = PipelineContext::GetCurrentContext();
258 CHECK_NULL_VOID(context);
259 context->RequestFrame();
260 };
261 deadlineTimer_.Reset(flushCallback);
262 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
263 taskExecutor.PostDelayedTask(deadlineTimer_, time);
264 }
265
DoRepeat()266 void LongPressRecognizer::DoRepeat()
267 {
268 if (static_cast<int32_t>(touchPoints_.size()) < fingers_) {
269 return;
270 }
271 if (refereeState_ == RefereeState::SUCCEED) {
272 SendCallbackMsg(onAction_, true, true);
273 StartRepeatTimer();
274 }
275 }
276
StartRepeatTimer()277 void LongPressRecognizer::StartRepeatTimer()
278 {
279 auto context = PipelineContext::GetCurrentContext();
280 CHECK_NULL_VOID(context);
281
282 auto&& callback = [weakPtr = AceType::WeakClaim(this)]() {
283 auto refPtr = weakPtr.Upgrade();
284 if (refPtr) {
285 refPtr->DoRepeat();
286 }
287 };
288 timer_.Reset(callback);
289 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
290 taskExecutor.PostDelayedTask(timer_, duration_);
291 }
292
ConvertPxToVp(double offset) const293 double LongPressRecognizer::ConvertPxToVp(double offset) const
294 {
295 auto context = PipelineContext::GetCurrentContext();
296 CHECK_NULL_RETURN(context, offset);
297
298 double vpOffset = context->ConvertPxToVp(Dimension(offset, DimensionUnit::PX));
299 return vpOffset;
300 }
301
SendCallbackMsg(const std::unique_ptr<GestureEventFunc> & callback,bool isRepeat,bool isOnAction)302 void LongPressRecognizer::SendCallbackMsg(
303 const std::unique_ptr<GestureEventFunc>& callback, bool isRepeat, bool isOnAction)
304 {
305 if (callback && *callback) {
306 GestureEvent info;
307 info.SetTimeStamp(time_);
308 info.SetRepeat(isRepeat);
309 info.SetFingerList(fingerList_);
310 TouchEvent trackPoint = {};
311 if (!touchPoints_.empty()) {
312 trackPoint = touchPoints_.begin()->second;
313 }
314 info.SetSourceDevice(deviceType_);
315 info.SetDeviceId(deviceId_);
316 info.SetTargetDisplayId(trackPoint.targetDisplayId);
317 info.SetGlobalPoint(globalPoint_);
318 info.SetScreenLocation(trackPoint.GetScreenOffset());
319 info.SetGlobalLocation(trackPoint.GetOffset()).SetLocalLocation(trackPoint.GetOffset() - coordinateOffset_);
320 info.SetTarget(GetEventTarget().value_or(EventTarget()));
321 info.SetForce(trackPoint.force);
322 if (trackPoint.tiltX.has_value()) {
323 info.SetTiltX(trackPoint.tiltX.value());
324 }
325 if (trackPoint.tiltY.has_value()) {
326 info.SetTiltY(trackPoint.tiltY.value());
327 }
328 info.SetSourceTool(trackPoint.sourceTool);
329 // callback may be overwritten in its invoke so we copy it first
330 auto callbackFunction = *callback;
331 callbackFunction(info);
332 if (isOnAction && longPressRecorder_ && *longPressRecorder_) {
333 (*longPressRecorder_)(info);
334 }
335 }
336 }
337
OnResetStatus()338 void LongPressRecognizer::OnResetStatus()
339 {
340 MultiFingersRecognizer::OnResetStatus();
341 timer_.Cancel();
342 deadlineTimer_.Cancel();
343 auto context = PipelineContext::GetCurrentContext();
344 CHECK_NULL_VOID(context);
345 context->RemoveGestureTask(task_);
346 }
347
ReconcileFrom(const RefPtr<NGGestureRecognizer> & recognizer)348 bool LongPressRecognizer::ReconcileFrom(const RefPtr<NGGestureRecognizer>& recognizer)
349 {
350 RefPtr<LongPressRecognizer> curr = AceType::DynamicCast<LongPressRecognizer>(recognizer);
351 if (!curr) {
352 ResetStatus();
353 return false;
354 }
355
356 if (curr->duration_ != duration_ || curr->fingers_ != fingers_ || curr->repeat_ != repeat_ ||
357 curr->priorityMask_ != priorityMask_) {
358 ResetStatus();
359 return false;
360 }
361
362 onAction_ = std::move(curr->onAction_);
363 onActionEnd_ = std::move(curr->onActionEnd_);
364 onActionCancel_ = std::move(curr->onActionCancel_);
365
366 return true;
367 }
368
GetLongPressActionFunc()369 GestureEventFunc LongPressRecognizer::GetLongPressActionFunc()
370 {
371 auto callback = [weak = WeakClaim(this)](GestureEvent& info) {
372 auto longPressRecognizer = weak.Upgrade();
373 CHECK_NULL_VOID(longPressRecognizer);
374 if (longPressRecognizer->onActionUpdate_) {
375 (*(longPressRecognizer->onActionUpdate_))(info);
376 }
377 if (longPressRecognizer->onAction_) {
378 (*(longPressRecognizer->onAction_))(info);
379 }
380 if (longPressRecognizer->onActionUpdate_) {
381 (*(longPressRecognizer->onActionUpdate_))(info);
382 }
383 if (longPressRecognizer->onActionEnd_) {
384 (*(longPressRecognizer->onActionEnd_))(info);
385 }
386 };
387 return callback;
388 }
389
Dump() const390 RefPtr<GestureSnapshot> LongPressRecognizer::Dump() const
391 {
392 RefPtr<GestureSnapshot> info = NGGestureRecognizer::Dump();
393 std::stringstream oss;
394 oss << "duration: " << duration_ << ", "
395 << "isForDrag: " << isForDrag_ << ", "
396 << "repeat: " << repeat_ << ", "
397 << "fingers: " << fingers_;
398 info->customInfo = oss.str();
399 return info;
400 }
401
TriggerGestureJudgeCallback()402 GestureJudgeResult LongPressRecognizer::TriggerGestureJudgeCallback()
403 {
404 auto targetComponent = GetTargetComponent();
405 CHECK_NULL_RETURN(targetComponent, GestureJudgeResult::CONTINUE);
406 auto callback = targetComponent->GetOnGestureJudgeBeginCallback();
407 CHECK_NULL_RETURN(callback, GestureJudgeResult::CONTINUE);
408 auto info = std::make_shared<LongPressGestureEvent>();
409 info->SetTimeStamp(time_);
410 info->SetRepeat(repeat_);
411 info->SetFingerList(fingerList_);
412 TouchEvent trackPoint = {};
413 if (!touchPoints_.empty()) {
414 trackPoint = touchPoints_.begin()->second;
415 }
416 info->SetSourceDevice(deviceType_);
417 info->SetTarget(GetEventTarget().value_or(EventTarget()));
418 info->SetForce(trackPoint.force);
419 if (trackPoint.tiltX.has_value()) {
420 info->SetTiltX(trackPoint.tiltX.value());
421 }
422 if (trackPoint.tiltY.has_value()) {
423 info->SetTiltY(trackPoint.tiltY.value());
424 }
425 info->SetSourceTool(trackPoint.sourceTool);
426 return callback(gestureInfo_, info);
427 }
428
GetDragEventActuator()429 RefPtr<DragEventActuator> LongPressRecognizer::GetDragEventActuator()
430 {
431 auto targetComponent = GetTargetComponent();
432 CHECK_NULL_RETURN(targetComponent, nullptr);
433 auto uiNode = targetComponent->GetUINode().Upgrade();
434 CHECK_NULL_RETURN(uiNode, nullptr);
435 auto frameNode = AceType::DynamicCast<FrameNode>(uiNode);
436 CHECK_NULL_RETURN(frameNode, nullptr);
437 auto gestureEventHub = frameNode->GetOrCreateGestureEventHub();
438 CHECK_NULL_RETURN(gestureEventHub, nullptr);
439 return gestureEventHub->GetDragEventActuator();
440 }
441
442 } // namespace OHOS::Ace::NG
443