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