• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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