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