• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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