• 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     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