1 /*
2 * Copyright (c) 2024 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 "touch_gesture_detector.h"
17 #include <numeric>
18
19 #include "input_event_handler.h"
20
21 #undef MMI_LOG_DOMAIN
22 #define MMI_LOG_DOMAIN MMI_LOG_HANDLER
23 #undef MMI_LOG_TAG
24 #define MMI_LOG_TAG "TouchGestureDetector"
25
26 namespace OHOS {
27 namespace MMI {
28 namespace {
29 constexpr int32_t REPEAT_ONCE { 1 };
30 constexpr int32_t MAX_PHYSCAL_POINTER_NUM { 10 };
31 constexpr int32_t ANGLE_PI { 180 };
32
33 constexpr double ANGLE_RIGHT_DOWN { -45.0 };
34 constexpr double ANGLE_RIGHT_UP { 45.0 };
35 constexpr double ANGLE_LEFT_DOWN { -135.0 };
36 constexpr double ANGLE_LEFT_UP { 135.0 };
37
38 // gesture threshold values
39 constexpr float MAXIMUM_POINTER_SPACING { 2000 };
40 constexpr int64_t MAXIMUM_POINTER_INTERVAL { 100000 };
41 constexpr int64_t MAXIMUM_PRESS_DELAY { 15 }; // ms
42 constexpr double MAXIMUM_SINGLE_SLIDE_DISTANCE { 3.0 };
43 constexpr double MINIMUM_GRAVITY_OFFSET { 0.5 };
44 constexpr int32_t MAXIMUM_CONTINUOUS_COUNTS { 2 };
45 constexpr int32_t MINIMUM_FINGER_COUNT_OFFSET { 1 };
46 constexpr size_t SINGLE_TOUCH { 1 };
47 constexpr size_t SINGLE_DIRECTION { 1 };
48 } // namespace
49
OnTouchEvent(std::shared_ptr<PointerEvent> event)50 bool TouchGestureDetector::OnTouchEvent(std::shared_ptr<PointerEvent> event)
51 {
52 CHKPF(event);
53 if (WhetherDiscardTouchEvent(event)) {
54 return false;
55 }
56 int32_t action = event->GetPointerAction();
57 switch (action) {
58 case PointerEvent::POINTER_ACTION_DOWN: {
59 HandleDownEvent(event);
60 break;
61 }
62 case PointerEvent::POINTER_ACTION_MOVE: {
63 HandleMoveEvent(event);
64 break;
65 }
66 case PointerEvent::POINTER_ACTION_UP:
67 case PointerEvent::POINTER_ACTION_CANCEL:
68 case PointerEvent::POINTER_ACTION_PULL_UP: {
69 HandleUpEvent(event);
70 break;
71 }
72 default: {
73 MMI_HILOGD("action:%{public}s is invalid", event->DumpPointerAction());
74 break;
75 }
76 }
77 return isRecognized_;
78 }
79
HandleDownEvent(std::shared_ptr<PointerEvent> event)80 void TouchGestureDetector::HandleDownEvent(std::shared_ptr<PointerEvent> event)
81 {
82 CALL_INFO_TRACE;
83 if (isRecognized_) {
84 if (haveGestureWinEmerged_) {
85 return;
86 }
87 MMI_HILOGI("Touch-down while touch gesture is pending");
88 isRecognized_ = false;
89
90 if (lastTouchEvent_ != nullptr) {
91 auto now = GetSysClockTime();
92 lastTouchEvent_->SetActionTime(now);
93 NotifyGestureEvent(lastTouchEvent_, GestureMode::ACTION_GESTURE_END);
94 }
95 }
96 int32_t pointerId = event->GetPointerId();
97 PointerEvent::PointerItem item {};
98
99 if (!event->GetPointerItem(pointerId, item)) {
100 MMI_HILOGE("Get pointer item:%{public}d fail", pointerId);
101 return;
102 }
103 auto [_, isNew] = downPoint_.insert_or_assign(
104 pointerId, Point { item.GetDisplayX(), item.GetDisplayY(), item.GetDownTime() });
105 if (!isNew) {
106 MMI_HILOGE("Insert value failed, duplicated pointerId:%{public}d", pointerId);
107 }
108 if (gestureTimer_ >= 0) {
109 TimerMgr->RemoveTimer(gestureTimer_);
110 gestureTimer_ = -1;
111 }
112 if (gestureType_ == TOUCH_GESTURE_TYPE_SWIPE) {
113 isFingerReady_ = HandleFingerDown();
114 } else if (gestureType_ == TOUCH_GESTURE_TYPE_PINCH) {
115 CalcAndStoreDistance();
116 movePoint_ = downPoint_;
117 }
118 MMI_HILOGI("The gestureType:%{public}d, touches:%{public}s, isFingerReady:%{public}d, pointerId:%{public}d",
119 gestureType_, DumpTouches().c_str(), isFingerReady_, pointerId);
120 }
121
HandleMoveEvent(std::shared_ptr<PointerEvent> event)122 void TouchGestureDetector::HandleMoveEvent(std::shared_ptr<PointerEvent> event)
123 {
124 if (isRecognized_ || (gestureTimer_ >= 0)) {
125 return;
126 }
127 if (!GestureMonitorHandler::CheckMonitorValid(gestureType_, static_cast<int32_t>(downPoint_.size()))) {
128 return;
129 }
130 if (!IsMatchGesture(event->GetPointerCount()) && !IsMatchGesture(ALL_FINGER_COUNT)) {
131 return;
132 }
133 if (gestureType_ == TOUCH_GESTURE_TYPE_SWIPE) {
134 HandleSwipeMoveEvent(event);
135 } else if (gestureType_ == TOUCH_GESTURE_TYPE_PINCH) {
136 HandlePinchMoveEvent(event);
137 }
138 if (isRecognized_) {
139 lastTouchEvent_ = std::make_shared<PointerEvent>(*event);
140 if (listener_ != nullptr) {
141 listener_->OnGestureTrend(event);
142 }
143 }
144 }
145
HandleSwipeMoveEvent(std::shared_ptr<PointerEvent> event)146 void TouchGestureDetector::HandleSwipeMoveEvent(std::shared_ptr<PointerEvent> event)
147 {
148 if (!isFingerReady_) {
149 return;
150 }
151 auto state = ClacFingerMoveDirection(event);
152 if (state == SlideState::DIRECTION_UNKNOW) {
153 return;
154 }
155 GestureMode type = ChangeToGestureMode(state);
156 if (type == GestureMode::ACTION_UNKNOWN) {
157 return;
158 }
159 gestureTimer_ = TimerMgr->AddTimer(MAXIMUM_PRESS_DELAY, REPEAT_ONCE,
160 [this, pointerEvent = std::make_shared<PointerEvent>(*event), type]() {
161 gestureTimer_ = -1;
162 isRecognized_ = true;
163 lastTouchEvent_ = pointerEvent;
164 NotifyGestureEvent(pointerEvent, type);
165 #ifdef TOUCH_GESTURE_MONITOR_ENABLED
166 MMI_HILOGW("Touch-swipe identified, cancel all touches");
167 if (listener_ != nullptr) {
168 listener_->OnGestureTrend(pointerEvent);
169 }
170 #else
171 MMI_HILOGI("Touch-swipe identified, will not cancel touches");
172 #endif // TOUCH_GESTURE_MONITOR_ENABLED
173 }, "TouchGestureDetector");
174 if (gestureTimer_ < 0) {
175 MMI_HILOGE("TimerMgr::AddTimer fail");
176 }
177 }
178
HandlePinchMoveEvent(std::shared_ptr<PointerEvent> event)179 void TouchGestureDetector::HandlePinchMoveEvent(std::shared_ptr<PointerEvent> event)
180 {
181 std::map<int32_t, Point> movePoints;
182 std::unordered_set<SlideState> directions;
183
184 for (const auto &[pointerId, downPt] : downPoint_) {
185 PointerEvent::PointerItem item {};
186 if (!event->GetPointerItem(pointerId, item)) {
187 MMI_HILOGE("Get pointer item:%{public}d fail", pointerId);
188 return;
189 }
190 Point movePt { item.GetDisplayX(), item.GetDisplayY(), item.GetDownTime() };
191
192 if (IsFingerMove(downPt, movePt)) {
193 double angle = GetAngle(downPt.x, downPt.y, movePt.x, movePt.y);
194 auto direction = GetSlidingDirection(angle);
195 directions.insert(direction);
196 }
197 auto [_, isNew] = movePoints.insert_or_assign(pointerId, movePt);
198 if (!isNew) {
199 MMI_HILOGE("Insert value failed, duplicated pointerId:%{public}d", pointerId);
200 }
201 }
202 if (!InDiverseDirections(directions)) {
203 return;
204 }
205 if (CalcMultiFingerMovement(movePoints) >=
206 static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
207 movePoint_ = movePoints;
208 GestureMode type = JudgeOperationMode(movePoints);
209 isRecognized_ = AntiJitter(event, type);
210 }
211 }
212
InOppositeDirections(const std::unordered_set<SlideState> & directions) const213 bool TouchGestureDetector::InOppositeDirections(const std::unordered_set<SlideState> &directions) const
214 {
215 bool up = directions.find(SlideState::DIRECTION_DOWN) != directions.cend();
216 bool down = directions.find(SlideState::DIRECTION_UP) != directions.cend();
217 bool left = directions.find(SlideState::DIRECTION_LEFT) != directions.cend();
218 bool right = directions.find(SlideState::DIRECTION_RIGHT) != directions.cend();
219 return (up && down) || (up && left) || (up && right) || (down && left) || (down && right) || (left && right);
220 }
221
InDiverseDirections(const std::unordered_set<SlideState> & directions) const222 bool TouchGestureDetector::InDiverseDirections(const std::unordered_set<SlideState> &directions) const
223 {
224 return (directions.size() > SINGLE_DIRECTION);
225 }
226
HandleUpEvent(std::shared_ptr<PointerEvent> event)227 void TouchGestureDetector::HandleUpEvent(std::shared_ptr<PointerEvent> event)
228 {
229 CALL_INFO_TRACE;
230 downPoint_.erase(event->GetPointerId());
231 movePoint_.erase(event->GetPointerId());
232 MMI_HILOGI("The gestureType:%{public}d, touches:%{public}s, isFingerReady:%{public}d, pointerId:%{public}d",
233 gestureType_, DumpTouches().c_str(), isFingerReady_, event->GetPointerId());
234 if (gestureTimer_ >= 0) {
235 TimerMgr->RemoveTimer(gestureTimer_);
236 gestureTimer_ = -1;
237 }
238 if (isRecognized_) {
239 PointerEvent::PointerItem pointerItem {};
240
241 if ((lastTouchEvent_ != nullptr) &&
242 lastTouchEvent_->GetPointerItem(event->GetPointerId(), pointerItem) &&
243 event->GetPointerItem(event->GetPointerId(), pointerItem)) {
244 lastTouchEvent_->UpdatePointerItem(event->GetPointerId(), pointerItem);
245 }
246 if (!haveGestureWinEmerged_) {
247 MMI_HILOGI("Touch-up while touch gesture is pending");
248 if (lastTouchEvent_ != nullptr) {
249 auto now = GetSysClockTime();
250 lastTouchEvent_->SetActionTime(now);
251 NotifyGestureEvent(lastTouchEvent_, GestureMode::ACTION_GESTURE_END);
252 }
253 }
254 }
255 if (IsLastTouchUp(event)) {
256 if (isRecognized_ && (lastTouchEvent_ != nullptr)) {
257 auto now = GetSysClockTime();
258 lastTouchEvent_->SetActionTime(now);
259 NotifyGestureEvent(lastTouchEvent_, GestureMode::ACTION_GESTURE_END);
260 }
261 ReleaseData();
262 }
263 }
264
IsPhysicalPointer(std::shared_ptr<PointerEvent> event)265 bool TouchGestureDetector::IsPhysicalPointer(std::shared_ptr<PointerEvent> event)
266 {
267 CHKPF(event);
268 if (event->HasFlag(InputEvent::EVENT_FLAG_SIMULATE)) {
269 return false;
270 }
271 const int32_t pointerId = event->GetPointerId();
272 return ((pointerId >= 0) && (pointerId < MAX_PHYSCAL_POINTER_NUM));
273 }
274
ReleaseData()275 void TouchGestureDetector::ReleaseData()
276 {
277 CALL_INFO_TRACE;
278 if (gestureTimer_ >= 0) {
279 TimerMgr->RemoveTimer(gestureTimer_);
280 gestureTimer_ = -1;
281 }
282 isRecognized_ = false;
283 isFingerReady_ = false;
284 haveGestureWinEmerged_ = false;
285 lastTouchEvent_ = nullptr;
286 continuousCloseCount_ = 0;
287 continuousOpenCount_ = 0;
288 lastDistance_.clear();
289 downPoint_.clear();
290 movePoint_.clear();
291 }
292
WhetherDiscardTouchEvent(std::shared_ptr<PointerEvent> event)293 bool TouchGestureDetector::WhetherDiscardTouchEvent(std::shared_ptr<PointerEvent> event)
294 {
295 if (event->GetSourceType() != PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
296 return true;
297 }
298 if (!gestureEnable_) {
299 return true;
300 }
301 if (!IsPhysicalPointer(event)) {
302 return true;
303 }
304 int32_t displayId = event->GetTargetDisplayId();
305 if (gestureDisplayId_ != INT32_MAX && gestureDisplayId_ != displayId) {
306 MMI_HILOGE("touch event from two different display, discards touch event");
307 return true;
308 }
309 int32_t action = event->GetPointerAction();
310 if (action == PointerEvent::POINTER_ACTION_UP) {
311 gestureDisplayId_ = INT32_MAX;
312 return false;
313 }
314 if (action == PointerEvent::POINTER_ACTION_DOWN) {
315 gestureDisplayId_ = displayId;
316 }
317 return false;
318 }
319
HandleFingerDown()320 bool TouchGestureDetector::HandleFingerDown()
321 {
322 if (!GestureMonitorHandler::CheckMonitorValid(gestureType_, static_cast<int32_t>(downPoint_.size()))) {
323 return false;
324 }
325 float maxDistance = GetMaxFingerSpacing();
326 if (maxDistance > MAXIMUM_POINTER_SPACING) {
327 MMI_HILOGE("Too much finger spacing");
328 return false;
329 }
330 int64_t interval = GetMaxDownInterval();
331 if (interval > MAXIMUM_POINTER_INTERVAL) {
332 MMI_HILOGE("The pointers down time interval is too long");
333 return false;
334 }
335 return true;
336 }
337
GetMaxDownInterval() const338 int64_t TouchGestureDetector::GetMaxDownInterval() const
339 {
340 int64_t earliestTime = std::numeric_limits<int64_t>::max();
341 int64_t latestTime = std::numeric_limits<int64_t>::min();
342
343 for (const auto &point : downPoint_) {
344 int64_t touchTime = point.second.time;
345 if (touchTime > latestTime) {
346 latestTime = touchTime;
347 }
348 if (touchTime < earliestTime) {
349 earliestTime = touchTime;
350 }
351 }
352 MMI_HILOGI("Down interval:%{public}" PRId64, (latestTime - earliestTime));
353 return latestTime - earliestTime;
354 }
355
GetMaxFingerSpacing() const356 float TouchGestureDetector::GetMaxFingerSpacing() const
357 {
358 float maxSpacing = 0.0f;
359
360 for (auto iter = downPoint_.cbegin(); iter != downPoint_.cend(); ++iter) {
361 auto innerIter = iter;
362 float pX = iter->second.x;
363 float pY = iter->second.y;
364
365 for (++innerIter; innerIter != downPoint_.cend(); ++innerIter) {
366 float nX = innerIter->second.x;
367 float nY = innerIter->second.y;
368 maxSpacing = std::max<float>(maxSpacing, std::hypot(pX - nX, pY - nY));
369 }
370 }
371 MMI_HILOGI("Down max spacing:%{public}.2f", maxSpacing);
372 return maxSpacing;
373 }
374
GetAngle(float startX,float startY,float endX,float endY) const375 double TouchGestureDetector::GetAngle(float startX, float startY, float endX, float endY) const
376 {
377 return std::atan2((endY - startY), (endX - startX)) * (ANGLE_PI / M_PI);
378 }
379
IsFingerMove(const Point & downPt,const Point & movePt) const380 bool TouchGestureDetector::IsFingerMove(const Point &downPt, const Point &movePt) const
381 {
382 return (CalcTwoPointsDistance(downPt, movePt) > MAXIMUM_SINGLE_SLIDE_DISTANCE);
383 }
384
GetSlidingDirection(double angle)385 TouchGestureDetector::SlideState TouchGestureDetector::GetSlidingDirection(double angle)
386 {
387 if (angle >= ANGLE_RIGHT_DOWN && angle < ANGLE_RIGHT_UP) {
388 return TouchGestureDetector::SlideState::DIRECTION_RIGHT;
389 } else if (angle >= ANGLE_RIGHT_UP && angle < ANGLE_LEFT_UP) {
390 return TouchGestureDetector::SlideState::DIRECTION_DOWN;
391 } else if (angle >= ANGLE_LEFT_DOWN && angle < ANGLE_RIGHT_DOWN) {
392 return TouchGestureDetector::SlideState::DIRECTION_UP;
393 } else {
394 return TouchGestureDetector::SlideState::DIRECTION_LEFT;
395 }
396 }
397
ChangeToGestureMode(TouchGestureDetector::SlideState state)398 GestureMode TouchGestureDetector::ChangeToGestureMode(TouchGestureDetector::SlideState state)
399 {
400 switch (state) {
401 case TouchGestureDetector::SlideState::DIRECTION_UP: {
402 return GestureMode::ACTION_SWIPE_UP;
403 }
404 case TouchGestureDetector::SlideState::DIRECTION_DOWN: {
405 return GestureMode::ACTION_SWIPE_DOWN;
406 }
407 case TouchGestureDetector::SlideState::DIRECTION_LEFT: {
408 return GestureMode::ACTION_SWIPE_LEFT;
409 }
410 case TouchGestureDetector::SlideState::DIRECTION_RIGHT: {
411 return GestureMode::ACTION_SWIPE_RIGHT;
412 }
413 default: {
414 MMI_HILOGW("unknow state:%{public}d", state);
415 return GestureMode::ACTION_UNKNOWN;
416 }
417 }
418 }
419
ClacFingerMoveDirection(std::shared_ptr<PointerEvent> event)420 TouchGestureDetector::SlideState TouchGestureDetector::ClacFingerMoveDirection(std::shared_ptr<PointerEvent> event)
421 {
422 if (event->GetPointerAction() != PointerEvent::POINTER_ACTION_MOVE) {
423 return SlideState::DIRECTION_UNKNOW;
424 }
425 if (downPoint_.size() < THREE_FINGER_COUNT) {
426 return SlideState::DIRECTION_UNKNOW;
427 }
428 size_t recognizedCount { 0 };
429 std::unordered_set<SlideState> directions;
430
431 for (const auto &[pointerId, downPt] : downPoint_) {
432 PointerEvent::PointerItem item {};
433 if (!event->GetPointerItem(pointerId, item)) {
434 MMI_HILOGE("Get pointer item:%{public}d fail", pointerId);
435 return SlideState::DIRECTION_UNKNOW;
436 }
437 Point movePt { item.GetDisplayX(), item.GetDisplayY() };
438
439 if (!IsFingerMove(downPt, movePt)) {
440 continue;
441 }
442 double angle = GetAngle(downPt.x, downPt.y, movePt.x, movePt.y);
443 auto direction = GetSlidingDirection(angle);
444 if (direction != SlideState::DIRECTION_UNKNOW) {
445 directions.insert(direction);
446 ++recognizedCount;
447 }
448 MMI_HILOGI("The pointerId:%{public}d,angle:%{public}.2f,direction:%{public}d", pointerId, angle, direction);
449 }
450 if ((recognizedCount < downPoint_.size()) || InDiverseDirections(directions)) {
451 return SlideState::DIRECTION_UNKNOW;
452 }
453 return *(directions.begin());
454 }
455
CalcTwoPointsDistance(const Point & p1,const Point & p2) const456 double TouchGestureDetector::CalcTwoPointsDistance(const Point &p1, const Point &p2) const
457 {
458 return std::hypot(p1.x - p2.x, p1.y - p2.y);
459 }
460
SortPoints(std::map<int32_t,Point> & points)461 std::vector<std::pair<int32_t, Point>> TouchGestureDetector::SortPoints(std::map<int32_t, Point> &points)
462 {
463 if (points.empty()) {
464 MMI_HILOGW("Points are empty");
465 return {};
466 }
467 std::vector<std::pair<int32_t, Point>> sequence(points.begin(), points.end());
468 std::sort(sequence.begin(), sequence.end(),
469 [](std::pair<int32_t, Point> right, std::pair<int32_t, Point> left) {
470 return right.second.x < left.second.x;
471 });
472 auto iter = std::max_element(sequence.begin(), sequence.end(),
473 [](std::pair<int32_t, Point> right, std::pair<int32_t, Point> left) {
474 return right.second.y < left.second.y;
475 });
476 std::pair<int32_t, Point> temp = *iter;
477 sequence.erase(iter);
478 sequence.push_back(temp);
479 return sequence;
480 }
481
CalcClusterCenter(const std::map<int32_t,Point> & points) const482 Point TouchGestureDetector::CalcClusterCenter(const std::map<int32_t, Point> &points) const
483 {
484 if (points.empty()) {
485 return Point {};
486 }
487 Point acc = std::accumulate(points.cbegin(), points.cend(), Point{},
488 [](const auto &init, const auto &item) {
489 return Point { init.x + item.second.x, init.y + item.second.y };
490 });
491 return Point { acc.x / points.size(), acc.y / points.size() };
492 }
493
CalcGravityCenter(std::map<int32_t,Point> & points)494 Point TouchGestureDetector::CalcGravityCenter(std::map<int32_t, Point> &points)
495 {
496 double xSum = 0.0;
497 double ySum = 0.0;
498 double area = 0.0;
499 const int32_t arrCount = 2;
500 int32_t count = static_cast<int32_t>(points.size());
501 if (count < FOUR_FINGER_COUNT || count > MAX_FINGERS_COUNT) {
502 return Point(static_cast<float>(xSum), static_cast<float>(ySum));
503 }
504 double **vertices = new (std::nothrow) double *[count];
505 if (vertices == nullptr) {
506 return Point(static_cast<float>(xSum), static_cast<float>(ySum));
507 }
508 int32_t i = 0;
509 std::vector<std::pair<int32_t, Point>> sequence = SortPoints(points);
510 if (sequence.empty()) {
511 MMI_HILOGW("Points sorting failed");
512 goto end;
513 }
514 for (const auto &pointData : sequence) {
515 vertices[i] = new (std::nothrow) double[arrCount];
516 if (vertices[i] == nullptr) {
517 goto end;
518 }
519 Point value = pointData.second;
520 vertices[i][0] = value.x;
521 vertices[i][1] = value.y;
522 ++i;
523 }
524 for (int32_t j = 0; j < count; ++j) {
525 double *current = vertices[j];
526 double *next = vertices[(j + 1) % count];
527 double crossProduct = current[0] * next[1] - next[0] * current[1];
528 area += crossProduct;
529 xSum += (current[0] + next[0]) * crossProduct;
530 ySum += (current[1] + next[1]) * crossProduct;
531 }
532 area /= arrCount;
533 xSum /= count * area;
534 ySum /= count * area;
535 end:
536 for (int32_t n = 0; n < count; ++n) {
537 if (vertices[n] != nullptr) {
538 delete[] vertices[n];
539 }
540 }
541 delete[] vertices;
542 return Point(static_cast<float>(xSum), static_cast<float>(ySum));
543 }
544
CalcAndStoreDistance()545 void TouchGestureDetector::CalcAndStoreDistance()
546 {
547 if (!GestureMonitorHandler::CheckMonitorValid(gestureType_, static_cast<int32_t>(downPoint_.size()))) {
548 return;
549 }
550 lastDistance_.clear();
551 int64_t interval = GetMaxDownInterval();
552 if (interval > MAXIMUM_POINTER_INTERVAL) {
553 MMI_HILOGE("The pointers down time interval is too long");
554 return;
555 }
556 Point center = CalcClusterCenter(downPoint_);
557
558 for (const auto &[pointerId, downPt] : downPoint_) {
559 double distance = CalcTwoPointsDistance(center, downPt);
560 lastDistance_.emplace(pointerId, distance);
561 }
562 }
563
CalcMultiFingerMovement(std::map<int32_t,Point> & points)564 int32_t TouchGestureDetector::CalcMultiFingerMovement(std::map<int32_t, Point> &points)
565 {
566 int32_t movementCount = 0;
567 for (const auto &[id, point] : movePoint_) {
568 auto movePoints = points.find(id);
569 if (movePoints == points.end()) {
570 continue;
571 }
572 if (CalcTwoPointsDistance(point, movePoints->second) >= MAXIMUM_SINGLE_SLIDE_DISTANCE) {
573 ++movementCount;
574 }
575 }
576 return movementCount;
577 }
578
JudgeOperationMode(std::map<int32_t,Point> & movePoints)579 GestureMode TouchGestureDetector::JudgeOperationMode(std::map<int32_t, Point> &movePoints)
580 {
581 Point center = CalcClusterCenter(movePoints);
582 std::map<int32_t, double> tempDistance;
583 int32_t closeCount = 0;
584 int32_t openCount = 0;
585
586 for (const auto &[pointerId, _] : downPoint_) {
587 auto movePointIter = movePoints.find(pointerId);
588 if (movePointIter == movePoints.end()) {
589 return GestureMode::ACTION_UNKNOWN;
590 }
591 double currentDistance = CalcTwoPointsDistance(center, movePointIter->second);
592 auto distanceIter = lastDistance_.find(pointerId);
593 if (distanceIter == lastDistance_.end()) {
594 return GestureMode::ACTION_UNKNOWN;
595 }
596 double lastDistance = distanceIter->second;
597 if (currentDistance < lastDistance &&
598 lastDistance - currentDistance >= MINIMUM_GRAVITY_OFFSET) {
599 ++closeCount;
600 } else if (currentDistance > lastDistance &&
601 currentDistance - lastDistance >= MINIMUM_GRAVITY_OFFSET) {
602 ++openCount;
603 }
604 tempDistance.emplace(pointerId, currentDistance);
605 MMI_HILOGI("The pointerId:%{public}d,lastDistance:%{public}.2f,"
606 "currentDistance:%{public}.2f,closeCount:%{public}d,openCount:%{public}d",
607 pointerId, lastDistance, currentDistance, closeCount, openCount);
608 }
609
610 lastDistance_.swap(tempDistance);
611 GestureMode type = GestureMode::ACTION_UNKNOWN;
612
613 if (closeCount >= static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
614 type = GestureMode::ACTION_PINCH_CLOSED;
615 } else if (openCount >= static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
616 type = GestureMode::ACTION_PINCH_OPENED;
617 }
618 return type;
619 }
620
AntiJitter(std::shared_ptr<PointerEvent> event,GestureMode mode)621 bool TouchGestureDetector::AntiJitter(std::shared_ptr<PointerEvent> event, GestureMode mode)
622 {
623 if (mode == GestureMode::ACTION_PINCH_CLOSED) {
624 ++continuousCloseCount_;
625 if (continuousCloseCount_ >= MAXIMUM_CONTINUOUS_COUNTS) {
626 return NotifyGestureEvent(event, mode);
627 }
628 continuousOpenCount_ = 0;
629 } else if (mode == GestureMode::ACTION_PINCH_OPENED) {
630 ++continuousOpenCount_;
631 if (continuousOpenCount_ >= MAXIMUM_CONTINUOUS_COUNTS) {
632 return NotifyGestureEvent(event, mode);
633 }
634 continuousCloseCount_ = 0;
635 } else {
636 continuousCloseCount_ = 0;
637 continuousOpenCount_ = 0;
638 }
639 return false;
640 }
641
AddGestureFingers(int32_t fingers)642 void TouchGestureDetector::AddGestureFingers(int32_t fingers)
643 {
644 auto iter = fingers_.insert(fingers);
645 if (!iter.second) {
646 MMI_HILOGE("Insert finger failed, finger:%{public}d", fingers);
647 return;
648 }
649 if (!fingers_.empty()) {
650 gestureEnable_ = true;
651 MMI_HILOGI("Start detection of touch-gesture(%{public}u)", gestureType_);
652 }
653 }
654
RemoveGestureFingers(int32_t fingers)655 void TouchGestureDetector::RemoveGestureFingers(int32_t fingers)
656 {
657 auto iter = fingers_.find(fingers);
658 if (iter != fingers_.end()) {
659 fingers_.erase(iter);
660 }
661 if (fingers_.empty()) {
662 MMI_HILOGI("Stop detection of touch-gesture(%{public}u)", gestureType_);
663 gestureEnable_ = false;
664 ReleaseData();
665 }
666 }
667
HandleGestureWindowEmerged(int32_t windowId,std::shared_ptr<PointerEvent> lastTouchEvent)668 void TouchGestureDetector::HandleGestureWindowEmerged(int32_t windowId, std::shared_ptr<PointerEvent> lastTouchEvent)
669 {
670 if ((gestureType_ == TOUCH_GESTURE_TYPE_PINCH) && isRecognized_ && !haveGestureWinEmerged_) {
671 MMI_HILOGI("Gesture window of UNI-CUBIC emerges, redirect touches");
672 haveGestureWinEmerged_ = true;
673 OnGestureSendEvent(lastTouchEvent);
674 }
675 }
676
IsMatchGesture(int32_t count) const677 bool TouchGestureDetector::IsMatchGesture(int32_t count) const
678 {
679 return fingers_.find(count) != fingers_.end();
680 }
681
IsMatchGesture(GestureMode mode,int32_t count) const682 bool TouchGestureDetector::IsMatchGesture(GestureMode mode, int32_t count) const
683 {
684 if (!IsMatchGesture(count) && !IsMatchGesture(ALL_FINGER_COUNT)) {
685 return false;
686 }
687 switch (mode) {
688 case GestureMode::ACTION_SWIPE_DOWN:
689 case GestureMode::ACTION_SWIPE_UP:
690 case GestureMode::ACTION_SWIPE_LEFT:
691 case GestureMode::ACTION_SWIPE_RIGHT:
692 return gestureType_ == TOUCH_GESTURE_TYPE_SWIPE;
693 case GestureMode::ACTION_PINCH_OPENED:
694 case GestureMode::ACTION_PINCH_CLOSED:
695 return gestureType_ == TOUCH_GESTURE_TYPE_PINCH;
696 case GestureMode::ACTION_GESTURE_END:
697 return true;
698 default:{
699 MMI_HILOGW("Unknown mode:%{public}d", mode);
700 return false;
701 }
702 }
703 }
704
NotifyGestureEvent(std::shared_ptr<PointerEvent> event,GestureMode mode)705 bool TouchGestureDetector::NotifyGestureEvent(std::shared_ptr<PointerEvent> event, GestureMode mode)
706 {
707 CHKPF(event);
708 CHKPF(listener_);
709 if (!IsMatchGesture(mode, event->GetPointerCount())) {
710 return false;
711 }
712 if (mode == GestureMode::ACTION_UNKNOWN) {
713 MMI_HILOGE("Wrong gesture");
714 return false;
715 }
716 if (event->GetSourceType() != PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
717 MMI_HILOGW("Handles only touchscreen events");
718 return false;
719 }
720 if (!listener_->OnGestureEvent(event, mode)) {
721 MMI_HILOGE("Failed to notify the gesture(%{public}d) event", mode);
722 return false;
723 }
724 MMI_HILOGI("Gesture(%{public}d) identified successfully", mode);
725 return true;
726 }
727
CheckGestureTrend(std::shared_ptr<PointerEvent> event) const728 void TouchGestureDetector::CheckGestureTrend(std::shared_ptr<PointerEvent> event) const
729 {
730 CHKPV(listener_);
731 int32_t nMovements { 0 };
732
733 for (const auto &[pointerId, downPt] : downPoint_) {
734 PointerEvent::PointerItem item {};
735 CHKPC(event);
736 if (!event->GetPointerItem(pointerId, item)) {
737 MMI_HILOGW("No touch(%{public}d) record", pointerId);
738 return;
739 }
740 Point movePt { item.GetDisplayX(), item.GetDisplayY() };
741
742 if (IsFingerMove(downPt, movePt)) {
743 ++nMovements;
744 }
745 }
746 if (nMovements >= THREE_FINGER_COUNT) {
747 listener_->OnGestureTrend(event);
748 }
749 }
750
IsLastTouchUp(std::shared_ptr<PointerEvent> event) const751 bool TouchGestureDetector::IsLastTouchUp(std::shared_ptr<PointerEvent> event) const
752 {
753 return ((event->GetPointerAction() == PointerEvent::POINTER_ACTION_UP) &&
754 (event->GetPointerIds().size() == SINGLE_TOUCH));
755 }
756
OnGestureSendEvent(std::shared_ptr<PointerEvent> event) const757 void TouchGestureDetector::OnGestureSendEvent(std::shared_ptr<PointerEvent> event) const
758 {
759 CALL_INFO_TRACE;
760 CHKPV(event);
761 event->SetTargetWindowId(-1);
762 auto pointerEvent = std::make_shared<PointerEvent>(*event);
763 pointerEvent->RemoveAllPointerItems();
764 auto items = event->GetAllPointerItems();
765 for (auto &item : items) {
766 if (!item.IsPressed()) {
767 continue;
768 }
769 int32_t pointerId = item.GetPointerId();
770 pointerEvent->SetPointerId(pointerId);
771 pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_DOWN);
772 auto now = GetSysClockTime();
773 pointerEvent->SetActionTime(now);
774 pointerEvent->UpdateId();
775 pointerEvent->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT | InputEvent::EVENT_FLAG_NO_MONITOR);
776
777 item.SetTargetWindowId(-1);
778 event->UpdatePointerItem(pointerId, item);
779 pointerEvent->AddPointerItem(item);
780
781 MMI_HILOGI("Redirect touch on transparent window of UNI-CUBIC, No:%{public}d, PI:%{public}d",
782 pointerEvent->GetId(), pointerEvent->GetPointerId());
783 auto inputEventNormalizeHandler = InputHandler->GetEventNormalizeHandler();
784 CHKPV(inputEventNormalizeHandler);
785 inputEventNormalizeHandler->HandleTouchEvent(pointerEvent);
786 }
787 }
788
DumpTouches() const789 std::string TouchGestureDetector::DumpTouches() const
790 {
791 std::ostringstream output;
792
793 if (auto iter = downPoint_.cbegin(); iter != downPoint_.cend()) {
794 output << "(" << iter->first;
795
796 for (++iter; iter != downPoint_.cend(); ++iter) {
797 output << "," << iter->first;
798 }
799 output << ")";
800 }
801 return std::move(output).str();
802 }
803 } // namespace MMI
804 } // namespace OHOS