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