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