1 /*
2 * Copyright (c) 2021 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/gestures/long_press_recognizer.h"
17
18 #include "core/gestures/gesture_referee.h"
19
20 namespace OHOS::Ace {
21 namespace {
22
23 constexpr double MAX_THRESHOLD = 15.0;
24 constexpr int32_t MAX_FINGERS = 10;
25 } // namespace
26
OnAccepted()27 void LongPressRecognizer::OnAccepted()
28 {
29 LOGI("long press gesture has been accepted!");
30
31 if (onLongPress_ && !touchMap_.empty()) {
32 TouchEvent trackPoint = touchMap_.begin()->second;
33 LongPressInfo info(trackPoint.id);
34 info.SetTimeStamp(time_);
35 info.SetScreenLocation(trackPoint.GetScreenOffset());
36 info.SetGlobalLocation(trackPoint.GetOffset()).SetLocalLocation(trackPoint.GetOffset() - coordinateOffset_);
37 onLongPress_(info);
38 }
39
40 SetFingerList(touchMap_, coordinateOffset_, fingerList_);
41 SendCallbackMsg(onActionUpdate_, false);
42 SendCallbackMsg(onAction_, false);
43 if (repeat_) {
44 StartRepeatTimer();
45 }
46
47 if (pendingEnd_) {
48 SendCallbackMsg(onActionEnd_, false);
49 Reset();
50 } else if (pendingCancel_) {
51 SendCancelMsg();
52 Reset();
53 }
54 }
55
OnRejected()56 void LongPressRecognizer::OnRejected()
57 {
58 LOGI("long press gesture has been rejected!");
59 Reset();
60 }
61
HandleTouchDownEvent(const TouchEvent & event)62 void LongPressRecognizer::HandleTouchDownEvent(const TouchEvent& event)
63 {
64 if (fingers_ > MAX_FINGERS) {
65 return;
66 }
67 LOGD("long press recognizer receives touch down event, begin to detect long press event");
68 if ((touchRestrict_.forbiddenType & TouchRestrict::LONG_PRESS) == TouchRestrict::LONG_PRESS) {
69 LOGI("the long press is forbidden");
70 return;
71 }
72 if (state_ == DetectState::READY) {
73 touchMap_[event.id] = event;
74 pointsCount_++;
75 AddToReferee(event.id, AceType::Claim(this));
76 if (pointsCount_ == fingers_) {
77 state_ = DetectState::DETECTING;
78 DeadlineTimer(duration_);
79 }
80 } else {
81 LOGW("the state is not ready for detecting long press gesture, state id %{public}d, id is %{public}d",
82 state_, event.id);
83 }
84 }
85
HandleTouchUpEvent(const TouchEvent & event)86 void LongPressRecognizer::HandleTouchUpEvent(const TouchEvent& event)
87 {
88 LOGD("long press recognizer receives touch up event");
89 auto it = touchMap_.find(event.id);
90 if (it == touchMap_.end()) {
91 return;
92 }
93
94 if (state_ != DetectState::DETECTED) {
95 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
96 return;
97 }
98
99 if (refereeState_ == RefereeState::SUCCEED) {
100 SendCallbackMsg(onActionUpdate_, false);
101 SendCallbackMsg(onActionEnd_, false);
102 Reset();
103 } else {
104 pendingEnd_ = true;
105 }
106 }
107
HandleTouchMoveEvent(const TouchEvent & event)108 void LongPressRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
109 {
110 LOGD("long press recognizer receives touch move event");
111 auto it = touchMap_.find(event.id);
112 if (it == touchMap_.end()) {
113 return;
114 }
115
116 Offset offset = event.GetOffset() - touchMap_[event.id].GetOffset();
117 if (offset.GetDistance() > MAX_THRESHOLD) {
118 if (state_ == DetectState::DETECTED && refereeState_ == RefereeState::SUCCEED) {
119 SendCallbackMsg(onActionEnd_, false);
120 Reset();
121 return;
122 }
123
124 LOGD("this gesture is not long press, try to reject it");
125 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
126 return;
127 }
128
129 time_ = event.time;
130 }
131
HandleTouchCancelEvent(const TouchEvent & event)132 void LongPressRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
133 {
134 LOGD("long press recognizer receives touch cancel event");
135 if (state_ == DetectState::READY || state_ == DetectState::DETECTING) {
136 LOGD("cancel long press gesture detect, try to reject it");
137 Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
138 SendCancelMsg();
139 return;
140 }
141
142 if (refereeState_ == RefereeState::SUCCEED) {
143 SendCancelMsg();
144 Reset();
145 } else {
146 pendingCancel_ = true;
147 }
148 }
149
HandleOverdueDeadline()150 void LongPressRecognizer::HandleOverdueDeadline()
151 {
152 if (state_ == DetectState::DETECTING) {
153 LOGI("this gesture is long press, try to accept it");
154 state_ = DetectState::DETECTED;
155 Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
156 SendCallbackMsg(onActionUpdate_, false);
157 } else {
158 LOGW("the state is not detecting for accept long press gesture");
159 }
160 }
161
DeadlineTimer(int32_t time)162 void LongPressRecognizer::DeadlineTimer(int32_t time)
163 {
164 auto context = context_.Upgrade();
165 if (!context) {
166 LOGI("fail to detect long press gesture due to context is nullptr");
167 return;
168 }
169 auto&& callback = [weakPtr = AceType::WeakClaim(this)]() {
170 auto refPtr = weakPtr.Upgrade();
171 if (refPtr) {
172 refPtr->HandleOverdueDeadline();
173 } else {
174 LOGI("fail to handle overdue deadline due to context is nullptr");
175 }
176 };
177 deadlineTimer_.Reset(callback);
178 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
179 taskExecutor.PostDelayedTask(deadlineTimer_, time);
180 }
181
DoRepeat()182 void LongPressRecognizer::DoRepeat()
183 {
184 SendCallbackMsg(onAction_, true);
185 StartRepeatTimer();
186 }
187
StartRepeatTimer()188 void LongPressRecognizer::StartRepeatTimer()
189 {
190 auto context = context_.Upgrade();
191 if (!context) {
192 LOGW("fail to detect long press gesture due to context is nullptr");
193 return;
194 }
195 auto&& callback = [weakPtr = AceType::WeakClaim(this)]() {
196 auto refPtr = weakPtr.Upgrade();
197 if (refPtr) {
198 refPtr->DoRepeat();
199 } else {
200 LOGW("fail to handle overdue deadline due to context is nullptr");
201 }
202 };
203 timer_.Reset(callback);
204 auto taskExecutor = SingleTaskExecutor::Make(context->GetTaskExecutor(), TaskExecutor::TaskType::UI);
205 taskExecutor.PostDelayedTask(timer_, duration_);
206 }
207
ConvertPxToVp(double offset) const208 double LongPressRecognizer::ConvertPxToVp(double offset) const
209 {
210 auto context = context_.Upgrade();
211 if (!context) {
212 LOGE("fail to detect tap gesture due to context is nullptr");
213 return offset;
214 }
215 double vpOffset = context->ConvertPxToVp(Dimension(offset, DimensionUnit::PX));
216 return vpOffset;
217 }
218
SendCallbackMsg(const std::unique_ptr<GestureEventFunc> & callback,bool isRepeat)219 void LongPressRecognizer::SendCallbackMsg(const std::unique_ptr<GestureEventFunc>& callback, bool isRepeat)
220 {
221 if (callback && *callback) {
222 GestureEvent info;
223 info.SetTimeStamp(time_);
224 info.SetRepeat(isRepeat);
225 info.SetFingerList(fingerList_);
226 TouchEvent trackPoint = {};
227 if (!touchMap_.empty()) {
228 trackPoint = touchMap_.begin()->second;
229 }
230 info.SetSourceDevice(deviceType_);
231 info.SetDeviceId(deviceId_);
232 info.SetScreenLocation(trackPoint.GetScreenOffset());
233 info.SetGlobalLocation(trackPoint.GetOffset()).SetLocalLocation(trackPoint.GetOffset() - coordinateOffset_);
234 (*callback)(info);
235 }
236 }
237
Reset()238 void LongPressRecognizer::Reset()
239 {
240 touchMap_.clear();
241 fingerList_.clear();
242 pointsCount_ = 0;
243 timer_.Cancel();
244 deadlineTimer_.Cancel();
245 state_ = DetectState::READY;
246 pendingEnd_ = false;
247 pendingCancel_ = false;
248 }
249
ReconcileFrom(const RefPtr<GestureRecognizer> & recognizer)250 bool LongPressRecognizer::ReconcileFrom(const RefPtr<GestureRecognizer>& recognizer)
251 {
252 RefPtr<LongPressRecognizer> curr = AceType::DynamicCast<LongPressRecognizer>(recognizer);
253 if (!curr) {
254 Reset();
255 return false;
256 }
257
258 if (curr->duration_ != duration_ || curr->fingers_ != fingers_ || curr->repeat_ != repeat_ ||
259 curr->priorityMask_ != priorityMask_) {
260 Reset();
261 return false;
262 }
263
264 context_ = curr->context_;
265 onAction_ = std::move(curr->onAction_);
266 onActionEnd_ = std::move(curr->onActionEnd_);
267 onActionCancel_ = std::move(curr->onActionCancel_);
268
269 return true;
270 }
271 } // namespace OHOS::Ace
272