1 /*
2  * Copyright (c) 2021-2022 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/slide_recognizer.h"
17 
18 namespace OHOS::Ace {
19 
20 namespace {
21 
22 constexpr int32_t MAX_SLIDE_FINGERS = 10;
23 constexpr int32_t AXIS_SLIDE_FINGERS = 1;
24 constexpr int32_t RATIO_MS_TO_S = 1000;
25 constexpr int32_t RATIO_US_TO_MS = 1000;
26 constexpr double ANGLE_SUM_OF_TRIANGLE = 180.0;
27 
ChangeValueRange(double value)28 double ChangeValueRange(double value)
29 {
30     double result = 0.0;
31     if (LessOrEqual(value, -180.0)) {
32         result = value + 360.0;
33     } else if (GreatNotEqual(value, 180.0)) {
34         result = value - 360.0;
35     } else {
36         result = value;
37     }
38 
39     return result;
40 }
41 
42 } // namespace
43 
OnAccepted()44 void SlideRecognizer::OnAccepted()
45 {
46     if (slidingEnd_) {
47         Reset();
48     } else if (slidingCancel_) {
49         SendCancelMsg();
50         Reset();
51     }
52 }
53 
OnRejected()54 void SlideRecognizer::OnRejected()
55 {
56     Reset();
57 }
58 
HandleTouchDownEvent(const TouchEvent & event)59 void SlideRecognizer::HandleTouchDownEvent(const TouchEvent& event)
60 {
61     fingers_ = newFingers_;
62     speed_ = newSpeed_;
63     direction_ = newDirection_;
64 
65     if (fingers_ > MAX_SLIDE_FINGERS) {
66         return;
67     }
68 
69     if (direction_.type == SwipeDirection::NONE) {
70         return;
71     }
72 
73     touchPoints_[event.id] = event;
74     lastTouchEvent_ = event;
75     fingersDistance_[event.id] = Offset();
76     touchDownTime_ = event.time;
77 
78     if (state_ == DetectState::READY) {
79         AddToReferee(event.id, AceType::Claim(this));
80         if (static_cast<int32_t>(refereePointers_.size()) == fingers_) {
81             initialAngle_ = ComputeAngle();
82             state_ = DetectState::DETECTING;
83         }
84     }
85 }
86 
HandleTouchDownEvent(const AxisEvent & event)87 void SlideRecognizer::HandleTouchDownEvent(const AxisEvent& event)
88 {
89     fingers_ = newFingers_;
90     speed_ = newSpeed_;
91     direction_ = newDirection_;
92 
93     if (fingers_ != AXIS_SLIDE_FINGERS) {
94         return;
95     }
96 
97     if (direction_.type == SwipeDirection::NONE) {
98         return;
99     }
100 
101     axisEventStart_ = event;
102     axisVerticalTotal_ = 0.0;
103     axisHorizontalTotal_ = 0.0;
104     touchDownTime_ = event.time;
105 
106     if (state_ == DetectState::READY) {
107         initialAngle_ = ComputeAngle(event);
108         state_ = DetectState::DETECTING;
109     }
110 }
111 
HandleTouchUpEvent(const TouchEvent & event)112 void SlideRecognizer::HandleTouchUpEvent(const TouchEvent& event)
113 {
114     auto itr = touchPoints_.find(event.id);
115     if (itr == touchPoints_.end()) {
116         return;
117     }
118     auto itf = fingersDistance_.find(event.id);
119     if (itf == fingersDistance_.end()) {
120         return;
121     }
122 
123     globalPoint_ = Point(event.x, event.y);
124     lastTouchEvent_ = event;
125     touchPoints_.erase(itr);
126     auto distanceData = fingersDistance_;
127     fingersDistance_.erase(itf);
128     if (state_ == DetectState::READY) {
129         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
130         return;
131     }
132 
133     if (state_ == DetectState::DETECTING) {
134         size_t inRefereeNum = refereePointers_.size();
135         bool inReferee = IsInReferee(static_cast<size_t>(event.id));
136         if (inReferee) {
137             inRefereeNum--;
138         }
139 
140         if (static_cast<int32_t>(touchPoints_.size()) < fingers_ || inRefereeNum < 1) {
141             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
142             return;
143         }
144 
145         if (inReferee) {
146             DelFromReferee(event.id, AceType::Claim(this));
147         }
148         return;
149     }
150 
151     if (static_cast<int32_t>(touchPoints_.size()) <= fingers_) {
152         if (refereeState_ == RefereeState::SUCCEED) {
153             double averageSpeed = 0.0;
154             bool isAvailable = true;
155             for (const auto& element : distanceData) {
156                 Offset offset = element.second;
157                 double distance = 0.0;
158                 if ((direction_.type & SwipeDirection::ALL) == SwipeDirection::ALL) {
159                     distance = offset.GetDistance();
160                 } else {
161                     if ((direction_.type & SwipeDirection::HORIZONTAL) != 0) {
162                         distance = std::abs(offset.GetX());
163                     } else if ((direction_.type & SwipeDirection::VERTICAL) != 0) {
164                         distance = std::abs(offset.GetY());
165                     }
166                 }
167                 auto slidingTime = event.time - touchDownTime_;
168                 auto duration_ms =
169                     std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1000>>>(slidingTime);
170                 double slidingSpeed = (distance / duration_ms.count()) * 1000;
171                 if (speed_ >= slidingSpeed) {
172                     isAvailable = false;
173                     break;
174                 }
175                 averageSpeed += slidingSpeed;
176             }
177             if (isAvailable) {
178                 resultSpeed_ = averageSpeed / distanceData.size();
179                 SendCallbackMsg(onAction_);
180             }
181             Reset();
182         } else {
183             slidingEnd_ = true;
184         }
185     }
186 }
187 
HandleTouchUpEvent(const AxisEvent & event)188 void SlideRecognizer::HandleTouchUpEvent(const AxisEvent& event)
189 {
190     if (fingers_ != AXIS_SLIDE_FINGERS) {
191         return;
192     }
193     globalPoint_ = Point(event.x, event.y);
194     if (state_ == DetectState::READY) {
195         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
196         return;
197     }
198 
199     if (state_ == DetectState::DETECTING) {
200         return;
201     }
202 
203     auto slidingTime = event.time - touchDownTime_;
204     auto duration_ms =
205         std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, RATIO_US_TO_MS>>>(slidingTime);
206     double verticalMoveTotal = axisVerticalTotal_ * DP_PER_LINE_DESKTOP * LINE_NUMBER_DESKTOP / MOUSE_WHEEL_DEGREES;
207     double horizontalMoveTotal = axisHorizontalTotal_ * DP_PER_LINE_DESKTOP * LINE_NUMBER_DESKTOP / MOUSE_WHEEL_DEGREES;
208     resultSpeed_ = Offset(horizontalMoveTotal, verticalMoveTotal).GetDistance() / duration_ms.count() * RATIO_MS_TO_S;
209     if (resultSpeed_ > speed_) {
210         SendCallbackMsg(onAction_);
211     }
212     Reset();
213 }
214 
HandleTouchMoveEvent(const TouchEvent & event)215 void SlideRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
216 {
217     auto itr = touchPoints_.find(event.id);
218     if (itr == touchPoints_.end()) {
219         return;
220     }
221     auto itf = fingersDistance_.find(event.id);
222     if (itf == fingersDistance_.end()) {
223         return;
224     }
225     globalPoint_ = Point(event.x, event.y);
226     lastTouchEvent_ = event;
227     if (state_ == DetectState::READY) {
228         touchPoints_[event.id] = event;
229         return;
230     }
231 
232     Offset moveDistance = event.GetOffset() - touchPoints_[event.id].GetOffset();
233     fingersDistance_[event.id] = itf->second + moveDistance;
234     touchPoints_[event.id] = event;
235     currentAngle_ = ComputeAngle();
236     time_ = event.time;
237 
238     if (state_ == DetectState::DETECTING) {
239         double diffAngle = fabs((currentAngle_ - initialAngle_));
240         if (GreatOrEqual(diffAngle, angle_)) {
241             resultAngle_ = ChangeValueRange(currentAngle_ - initialAngle_);
242         }
243         auto result = ParseFingersOffset();
244         if (result == GestureAcceptResult::ACCEPT) {
245             state_ = DetectState::DETECTED;
246             Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
247         } else if (result == GestureAcceptResult::REJECT) {
248             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
249         }
250     } else if (state_ == DetectState::DETECTED && refereeState_ == RefereeState::SUCCEED) {
251         resultAngle_ = ChangeValueRange(currentAngle_ - initialAngle_);
252     }
253 }
254 
HandleTouchMoveEvent(const AxisEvent & event)255 void SlideRecognizer::HandleTouchMoveEvent(const AxisEvent& event)
256 {
257     if (fingers_ != AXIS_SLIDE_FINGERS) {
258         return;
259     }
260     globalPoint_ = Point(event.x, event.y);
261     if (state_ == DetectState::READY) {
262         axisEventStart_ = event;
263         return;
264     }
265 
266     axisVerticalTotal_ += fabs(event.verticalAxis);
267     axisHorizontalTotal_ += fabs(event.horizontalAxis);
268     currentAngle_ = ComputeAngle(event);
269     time_ = event.time;
270 
271     if (state_ == DetectState::DETECTING) {
272         if (GreatOrEqual(fabs(currentAngle_), angle_)) {
273             resultAngle_ = ChangeValueRange(currentAngle_);
274         }
275         auto result = ParseAxisOffset();
276         if (result == GestureAcceptResult::ACCEPT) {
277             state_ = DetectState::DETECTED;
278             Adjudicate(AceType::Claim(this), GestureDisposal::ACCEPT);
279         } else if (result == GestureAcceptResult::REJECT) {
280             Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
281         }
282     } else if (state_ == DetectState::DETECTED) {
283         resultAngle_ = ChangeValueRange(currentAngle_);
284     }
285 }
286 
HandleTouchCancelEvent(const TouchEvent & event)287 void SlideRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
288 {
289     if (state_ == DetectState::READY || state_ == DetectState::DETECTING) {
290         Adjudicate(AceType::Claim(this), GestureDisposal::REJECT);
291         return;
292     }
293 
294     if (refereeState_ == RefereeState::SUCCEED) {
295         SendCancelMsg();
296         Reset();
297     } else {
298         slidingCancel_ = true;
299     }
300 }
301 
HandleTouchCancelEvent(const AxisEvent & event)302 void SlideRecognizer::HandleTouchCancelEvent(const AxisEvent& event)
303 {
304     SendCancelMsg();
305     Reset();
306 }
307 
ParseFingersOffset() const308 SlideRecognizer::GestureAcceptResult SlideRecognizer::ParseFingersOffset() const
309 {
310     if ((direction_.type & SwipeDirection::ALL) == SwipeDirection::ALL) {
311         for (const auto& element : fingersDistance_) {
312             Offset offset = element.second;
313             double distance = offset.GetDistance();
314             if (fabs(distance) < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
315                 return GestureAcceptResult::DETECTING;
316             }
317         }
318         return GestureAcceptResult::ACCEPT;
319     }
320 
321     for (const auto& element : fingersDistance_) {
322         Offset offset = element.second;
323         if (fabs(offset.GetX()) > fabs(offset.GetY())) {
324             if ((direction_.type & SwipeDirection::HORIZONTAL) != 0) {
325                 double offsetX = offset.GetX();
326                 if (fabs(offsetX) < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
327                     return GestureAcceptResult::DETECTING;
328                 }
329             } else {
330                 return GestureAcceptResult::REJECT;
331             }
332         } else {
333             if ((direction_.type & SwipeDirection::VERTICAL) != 0) {
334                 double offsetY = offset.GetY();
335                 if (fabs(offsetY) < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
336                     return GestureAcceptResult::DETECTING;
337                 }
338             } else {
339                 return GestureAcceptResult::REJECT;
340             }
341         }
342     }
343 
344     return GestureAcceptResult::ACCEPT;
345 }
346 
ParseAxisOffset() const347 SlideRecognizer::GestureAcceptResult SlideRecognizer::ParseAxisOffset() const
348 {
349     if ((direction_.type & SwipeDirection::ALL) == SwipeDirection::ALL) {
350         double distance = Offset(axisHorizontalTotal_, axisVerticalTotal_).GetDistance();
351         if (fabs(distance) < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
352             return GestureAcceptResult::DETECTING;
353         }
354         return GestureAcceptResult::ACCEPT;
355     }
356 
357     if (axisHorizontalTotal_ > axisVerticalTotal_) {
358         if ((direction_.type & SwipeDirection::HORIZONTAL) != 0) {
359             if (axisHorizontalTotal_ < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
360                 return GestureAcceptResult::DETECTING;
361             }
362         } else {
363             return GestureAcceptResult::REJECT;
364         }
365     } else {
366         if ((direction_.type & SwipeDirection::VERTICAL) != 0) {
367             if (axisVerticalTotal_ < DEFAULT_SLIDE_DISTANCE.ConvertToPx()) {
368                 return GestureAcceptResult::DETECTING;
369             }
370         } else {
371             return GestureAcceptResult::REJECT;
372         }
373     }
374 
375     return GestureAcceptResult::ACCEPT;
376 }
377 
Reset()378 void SlideRecognizer::Reset()
379 {
380     axisHorizontalTotal_ = 0.0;
381     axisVerticalTotal_ = 0.0;
382     touchPoints_.clear();
383     fingersDistance_.clear();
384     resultSpeed_ = 0.0;
385     state_ = DetectState::READY;
386     slidingEnd_ = false;
387     slidingCancel_ = false;
388 }
389 
SendCallbackMsg(const std::unique_ptr<GestureEventFunc> & callback)390 void SlideRecognizer::SendCallbackMsg(const std::unique_ptr<GestureEventFunc>& callback)
391 {
392     if (callback && *callback) {
393         GestureEvent info;
394         info.SetTimeStamp(time_);
395         info.SetGlobalPoint(globalPoint_);
396         info.SetAngle(resultAngle_);
397         if (deviceType_ == SourceType::MOUSE) {
398             info.SetSpeed(0.0);
399         } else {
400             info.SetSpeed(resultSpeed_);
401         }
402         info.SetSourceDevice(deviceType_);
403         info.SetDeviceId(deviceId_);
404         info.SetTarget(GetEventTarget().value_or(EventTarget()));
405         info.SetForce(lastTouchEvent_.force);
406         if (lastTouchEvent_.tiltX.has_value()) {
407             info.SetTiltX(lastTouchEvent_.tiltX.value());
408         }
409         if (lastTouchEvent_.tiltY.has_value()) {
410             info.SetTiltY(lastTouchEvent_.tiltY.value());
411         }
412         info.SetSourceTool(lastTouchEvent_.sourceTool);
413         (*callback)(info);
414     }
415 }
416 
ReconcileFrom(const RefPtr<GestureRecognizer> & recognizer)417 bool SlideRecognizer::ReconcileFrom(const RefPtr<GestureRecognizer>& recognizer)
418 {
419     RefPtr<SlideRecognizer> curr = AceType::DynamicCast<SlideRecognizer>(recognizer);
420     if (!curr) {
421         Reset();
422         return false;
423     }
424 
425     if (curr->fingers_ != fingers_ || !NearEqual(curr->angle_, angle_) || curr->priorityMask_ != priorityMask_) {
426         Reset();
427         return false;
428     }
429 
430     direction_.type = curr->direction_.type;
431     newDirection_.type = curr->newDirection_.type;
432     speed_ = curr->speed_;
433     newSpeed_ = curr->newSpeed_;
434 
435     onAction_ = std::move(curr->onAction_);
436 
437     return true;
438 }
439 
ChangeFingers(int32_t fingers)440 void SlideRecognizer::ChangeFingers(int32_t fingers)
441 {
442     if (fingers_ != fingers) {
443         newFingers_ = fingers;
444     }
445 }
446 
ChangeDirection(const SwipeDirection & direction)447 void SlideRecognizer::ChangeDirection(const SwipeDirection& direction)
448 {
449     if (direction_.type != direction.type) {
450         direction_.type = direction.type;
451         newDirection_.type = direction.type;
452     }
453 }
454 
ChangeSpeed(double speed)455 void SlideRecognizer::ChangeSpeed(double speed)
456 {
457     if (speed_ != speed) {
458         if (state_ == DetectState::READY || state_ == DetectState::DETECTING) {
459             speed_ = speed;
460         }
461         newSpeed_ = speed;
462     }
463 }
464 
ComputeAngle()465 double SlideRecognizer::ComputeAngle()
466 {
467     double fx = touchPoints_[0].x;
468     double fy = touchPoints_[0].y;
469     double sx = touchPoints_[1].x;
470     double sy = touchPoints_[1].y;
471     return atan2(fy - sy, fx - sx) * ANGLE_SUM_OF_TRIANGLE / M_PI;
472 }
473 
ComputeAngle(AxisEvent event)474 double SlideRecognizer::ComputeAngle(AxisEvent event)
475 {
476     return atan2(event.verticalAxis, event.horizontalAxis) * ANGLE_SUM_OF_TRIANGLE / M_PI;
477 }
478 
479 } // namespace OHOS::Ace
480