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