• 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_HILOGW("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     }
117     MMI_HILOGI("The gestureType:%{public}d, finger count:%{public}zu, isFingerReady:%{public}s, pointerId:%{public}d",
118         gestureType_, downPoint_.size(), (isFingerReady_ ? "true" : "false"), pointerId);
119     movePoint_ = downPoint_;
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         });
166     if (gestureTimer_ < 0) {
167         MMI_HILOGE("TimerMgr::AddTimer fail");
168     }
169 }
170 
HandlePinchMoveEvent(std::shared_ptr<PointerEvent> event)171 void TouchGestureDetector::HandlePinchMoveEvent(std::shared_ptr<PointerEvent> event)
172 {
173     std::map<int32_t, Point> movePoints;
174     std::unordered_set<SlideState> directions;
175 
176     for (const auto &[pointerId, downPt] : downPoint_) {
177         PointerEvent::PointerItem item {};
178         if (!event->GetPointerItem(pointerId, item)) {
179             MMI_HILOGE("Get pointer item:%{public}d fail", pointerId);
180             return;
181         }
182         Point movePt { item.GetDisplayX(), item.GetDisplayY(), item.GetDownTime() };
183 
184         if (IsFingerMove(downPt, movePt)) {
185             double angle = GetAngle(downPt.x, downPt.y, movePt.x, movePt.y);
186             auto direction = GetSlidingDirection(angle);
187             directions.insert(direction);
188         }
189         auto [_, isNew] = movePoints.insert_or_assign(pointerId, movePt);
190         if (!isNew) {
191             MMI_HILOGE("Insert value failed, duplicated pointerId:%{public}d", pointerId);
192         }
193     }
194     if (!InDiverseDirections(directions)) {
195         return;
196     }
197     if (CalcMultiFingerMovement(movePoints) >
198         static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
199         movePoint_ = movePoints;
200         GestureMode type = JudgeOperationMode(movePoints);
201         isRecognized_ = AntiJitter(event, type);
202     }
203 }
204 
InOppositeDirections(const std::unordered_set<SlideState> & directions) const205 bool TouchGestureDetector::InOppositeDirections(const std::unordered_set<SlideState> &directions) const
206 {
207     return (((directions.find(SlideState::DIRECTION_DOWN) != directions.cend()) &&
208              (directions.find(SlideState::DIRECTION_UP) != directions.cend())) ||
209             ((directions.find(SlideState::DIRECTION_LEFT) != directions.cend()) &&
210              (directions.find(SlideState::DIRECTION_RIGHT) != directions.cend())));
211 }
212 
InDiverseDirections(const std::unordered_set<SlideState> & directions) const213 bool TouchGestureDetector::InDiverseDirections(const std::unordered_set<SlideState> &directions) const
214 {
215     return (directions.size() > SINGLE_DIRECTION);
216 }
217 
HandleUpEvent(std::shared_ptr<PointerEvent> event)218 void TouchGestureDetector::HandleUpEvent(std::shared_ptr<PointerEvent> event)
219 {
220     CALL_INFO_TRACE;
221     downPoint_.erase(event->GetPointerId());
222     movePoint_.erase(event->GetPointerId());
223     if (gestureTimer_ >= 0) {
224         TimerMgr->RemoveTimer(gestureTimer_);
225         gestureTimer_ = -1;
226     }
227     if (isRecognized_) {
228         PointerEvent::PointerItem pointerItem {};
229 
230         if ((lastTouchEvent_ != nullptr) &&
231             lastTouchEvent_->GetPointerItem(event->GetPointerId(), pointerItem) &&
232             event->GetPointerItem(event->GetPointerId(), pointerItem)) {
233             lastTouchEvent_->UpdatePointerItem(event->GetPointerId(), pointerItem);
234         }
235         if (!haveGestureWinEmerged_) {
236             MMI_HILOGI("Touch-up while touch gesture is pending");
237             isRecognized_ = false;
238 
239             if (lastTouchEvent_ != nullptr) {
240                 auto now = GetSysClockTime();
241                 lastTouchEvent_->SetActionTime(now);
242                 NotifyGestureEvent(lastTouchEvent_, GestureMode::ACTION_GESTURE_END);
243             }
244         }
245     }
246     if (IsLastTouchUp(event)) {
247         if (isRecognized_ && (lastTouchEvent_ != nullptr)) {
248             auto now = GetSysClockTime();
249             lastTouchEvent_->SetActionTime(now);
250             NotifyGestureEvent(lastTouchEvent_, GestureMode::ACTION_GESTURE_END);
251         }
252         ReleaseData();
253     }
254 }
255 
IsPhysicalPointer(std::shared_ptr<PointerEvent> event)256 bool TouchGestureDetector::IsPhysicalPointer(std::shared_ptr<PointerEvent> event)
257 {
258     CHKPF(event);
259     if (event->HasFlag(InputEvent::EVENT_FLAG_SIMULATE)) {
260         return false;
261     }
262     const int32_t pointerId = event->GetPointerId();
263     return ((pointerId >= 0) && (pointerId < MAX_PHYSCAL_POINTER_NUM));
264 }
265 
ReleaseData()266 void TouchGestureDetector::ReleaseData()
267 {
268     CALL_INFO_TRACE;
269     isRecognized_ = false;
270     isFingerReady_ = false;
271     haveGestureWinEmerged_ = false;
272     lastTouchEvent_ = nullptr;
273     continuousCloseCount_ = 0;
274     continuousOpenCount_ = 0;
275     lastDistance_.clear();
276     downPoint_.clear();
277     movePoint_.clear();
278 }
279 
WhetherDiscardTouchEvent(std::shared_ptr<PointerEvent> event)280 bool TouchGestureDetector::WhetherDiscardTouchEvent(std::shared_ptr<PointerEvent> event)
281 {
282     if (event->GetSourceType() != PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
283         return true;
284     }
285     if (!gestureEnable_) {
286         return true;
287     }
288     if (!IsPhysicalPointer(event)) {
289         return true;
290     }
291     int32_t displayId = event->GetTargetDisplayId();
292     if (gestureDisplayId_ != INT32_MAX && gestureDisplayId_ != displayId) {
293         MMI_HILOGE("touch event from two different display, discards touch event");
294         return true;
295     }
296     int32_t action = event->GetPointerAction();
297     if (action == PointerEvent::POINTER_ACTION_UP) {
298         gestureDisplayId_ = INT32_MAX;
299         return false;
300     }
301     if (action == PointerEvent::POINTER_ACTION_DOWN) {
302         gestureDisplayId_ = displayId;
303     }
304     return false;
305 }
306 
HandleFingerDown()307 bool TouchGestureDetector::HandleFingerDown()
308 {
309     if (!GestureMonitorHandler::CheckMonitorValid(gestureType_, static_cast<int32_t>(downPoint_.size()))) {
310         return false;
311     }
312     float maxDistance = GetMaxFingerSpacing();
313     if (maxDistance > MAXIMUM_POINTER_SPACING) {
314         MMI_HILOGE("Too much finger spacing");
315         return false;
316     }
317     int64_t interval = GetMaxDownInterval();
318     if (interval > MAXIMUM_POINTER_INTERVAL) {
319         MMI_HILOGE("The pointers down time interval is too long");
320         return false;
321     }
322     return true;
323 }
324 
GetMaxDownInterval()325 int64_t TouchGestureDetector::GetMaxDownInterval()
326 {
327     int64_t earliestTime = std::numeric_limits<int64_t>::max();
328     int64_t latestTime = std::numeric_limits<int64_t>::min();
329 
330     for (const auto &point : downPoint_) {
331         int64_t touchTime = point.second.time;
332         if (touchTime > latestTime) {
333             latestTime = touchTime;
334         }
335         if (touchTime < earliestTime) {
336             earliestTime = touchTime;
337         }
338     }
339     MMI_HILOGI("Down interval:%{public}" PRId64, (latestTime - earliestTime));
340     return latestTime - earliestTime;
341 }
342 
GetMaxFingerSpacing()343 float TouchGestureDetector::GetMaxFingerSpacing()
344 {
345     float maxSpacing = 0.0f;
346     for (size_t i = 0; i < downPoint_.size(); ++i) {
347         for (size_t j = i + 1; j < downPoint_.size(); ++j) {
348             float pX = downPoint_[i].x;
349             float pY = downPoint_[i].y;
350             float nX = downPoint_[j].x;
351             float nY = downPoint_[j].y;
352             maxSpacing = std::max(maxSpacing, (float)hypot(pX - nX, pY - nY));
353         }
354     }
355     MMI_HILOGI("Down max spacing:%{public}.2f", maxSpacing);
356     return maxSpacing;
357 }
358 
GetAngle(float startX,float startY,float endX,float endY)359 double TouchGestureDetector::GetAngle(float startX, float startY, float endX, float endY)
360 {
361     return atan2((endY - startY), (endX - startX)) * (ANGLE_PI / M_PI);
362 }
363 
IsFingerMove(const Point & downPt,const Point & movePt) const364 bool TouchGestureDetector::IsFingerMove(const Point &downPt, const Point &movePt) const
365 {
366     return (CalcTwoPointsDistance(downPt, movePt) > MAXIMUM_SINGLE_SLIDE_DISTANCE);
367 }
368 
GetSlidingDirection(double angle)369 TouchGestureDetector::SlideState TouchGestureDetector::GetSlidingDirection(double angle)
370 {
371     if (angle >= ANGLE_RIGHT_DOWN && angle < ANGLE_RIGHT_UP) {
372         return TouchGestureDetector::SlideState::DIRECTION_RIGHT;
373     } else if (angle >= ANGLE_RIGHT_UP && angle < ANGLE_LEFT_UP) {
374         return TouchGestureDetector::SlideState::DIRECTION_DOWN;
375     } else if (angle >= ANGLE_LEFT_DOWN && angle < ANGLE_RIGHT_DOWN) {
376         return TouchGestureDetector::SlideState::DIRECTION_UP;
377     } else {
378         return TouchGestureDetector::SlideState::DIRECTION_LEFT;
379     }
380 }
381 
ChangeToGestureMode(TouchGestureDetector::SlideState state)382 GestureMode TouchGestureDetector::ChangeToGestureMode(TouchGestureDetector::SlideState state)
383 {
384     switch (state) {
385         case TouchGestureDetector::SlideState::DIRECTION_UP: {
386             return GestureMode::ACTION_SWIPE_UP;
387         }
388         case TouchGestureDetector::SlideState::DIRECTION_DOWN: {
389             return GestureMode::ACTION_SWIPE_DOWN;
390         }
391         case TouchGestureDetector::SlideState::DIRECTION_LEFT: {
392             return GestureMode::ACTION_SWIPE_LEFT;
393         }
394         case TouchGestureDetector::SlideState::DIRECTION_RIGHT: {
395             return GestureMode::ACTION_SWIPE_RIGHT;
396         }
397         default: {
398             MMI_HILOGW("unknow state:%{public}d", state);
399             return GestureMode::ACTION_UNKNOWN;
400         }
401     }
402 }
403 
ClacFingerMoveDirection(std::shared_ptr<PointerEvent> event)404 TouchGestureDetector::SlideState TouchGestureDetector::ClacFingerMoveDirection(std::shared_ptr<PointerEvent> event)
405 {
406     if (event->GetPointerAction() != PointerEvent::POINTER_ACTION_MOVE) {
407         return SlideState::DIRECTION_UNKNOW;
408     }
409     if (downPoint_.size() < THREE_FINGER_COUNT) {
410         return SlideState::DIRECTION_UNKNOW;
411     }
412     size_t recognizedCount { 0 };
413     std::unordered_set<SlideState> directions;
414 
415     for (const auto &[pointerId, downPt] : downPoint_) {
416         PointerEvent::PointerItem item {};
417         if (!event->GetPointerItem(pointerId, item)) {
418             MMI_HILOGE("Get pointer item:%{public}d fail", pointerId);
419             return SlideState::DIRECTION_UNKNOW;
420         }
421         Point movePt { item.GetDisplayX(), item.GetDisplayY() };
422 
423         if (!IsFingerMove(downPt, movePt)) {
424             continue;
425         }
426         double angle = GetAngle(downPt.x, downPt.y, movePt.x, movePt.y);
427         auto direction = GetSlidingDirection(angle);
428         if (direction != SlideState::DIRECTION_UNKNOW) {
429             directions.insert(direction);
430             ++recognizedCount;
431         }
432         MMI_HILOGI("The pointerId:%{public}d,angle:%{public}.2f,direction:%{public}d", pointerId, angle, direction);
433     }
434     if ((recognizedCount < downPoint_.size()) || InDiverseDirections(directions)) {
435         return SlideState::DIRECTION_UNKNOW;
436     }
437     return *(directions.begin());
438 }
439 
CalcTwoPointsDistance(const Point & p1,const Point & p2) const440 double TouchGestureDetector::CalcTwoPointsDistance(const Point &p1, const Point &p2) const
441 {
442     return std::hypot(p1.x - p2.x, p1.y - p2.y);
443 }
444 
SortPoints(std::map<int32_t,Point> & points)445 std::vector<std::pair<int32_t, Point>> TouchGestureDetector::SortPoints(std::map<int32_t, Point> &points)
446 {
447     if (points.empty()) {
448         MMI_HILOGW("Points are empty");
449         return {};
450     }
451     std::vector<std::pair<int32_t, Point>> sequence(points.begin(), points.end());
452     std::sort(sequence.begin(), sequence.end(),
453         [](std::pair<int32_t, Point> right, std::pair<int32_t, Point> left) {
454         return right.second.x < left.second.x;
455     });
456     auto iter = std::max_element(sequence.begin(), sequence.end(),
457         [](std::pair<int32_t, Point> right, std::pair<int32_t, Point> left) {
458         return right.second.y < left.second.y;
459     });
460     std::pair<int32_t, Point> temp = *iter;
461     sequence.erase(iter);
462     sequence.push_back(temp);
463     return sequence;
464 }
465 
CalcClusterCenter(const std::map<int32_t,Point> & points) const466 Point TouchGestureDetector::CalcClusterCenter(const std::map<int32_t, Point> &points) const
467 {
468     if (points.empty()) {
469         return Point {};
470     }
471     Point acc = std::accumulate(points.cbegin(), points.cend(), Point{},
472         [](const auto &init, const auto &item) {
473             return Point { init.x + item.second.x, init.y + item.second.y };
474         });
475     return Point { acc.x / points.size(), acc.y / points.size() };
476 }
477 
CalcGravityCenter(std::map<int32_t,Point> & points)478 Point TouchGestureDetector::CalcGravityCenter(std::map<int32_t, Point> &points)
479 {
480     double xSum = 0.0;
481     double ySum = 0.0;
482     double area = 0.0;
483     const int32_t arrCount = 2;
484     int32_t count = static_cast<int32_t>(points.size());
485     if (count < FOUR_FINGER_COUNT || count > MAX_FINGERS_COUNT) {
486         return Point(static_cast<float>(xSum), static_cast<float>(ySum));
487     }
488     double **vertices = new (std::nothrow) double *[count];
489     if (vertices == nullptr) {
490         return Point(static_cast<float>(xSum), static_cast<float>(ySum));
491     }
492     int32_t i = 0;
493     std::vector<std::pair<int32_t, Point>> sequence = SortPoints(points);
494     if (sequence.empty()) {
495         MMI_HILOGW("Points sorting failed");
496         goto end;
497     }
498     for (const auto &pointData : sequence) {
499         vertices[i] = new (std::nothrow) double[arrCount];
500         if (vertices[i] == nullptr) {
501             goto end;
502         }
503         Point value = pointData.second;
504         vertices[i][0] = value.x;
505         vertices[i][1] = value.y;
506         ++i;
507     }
508     for (int32_t j = 0; j < count; ++j) {
509         double *current = vertices[j];
510         double *next = vertices[(j + 1) % count];
511         double crossProduct = current[0] * next[1] - next[0] * current[1];
512         area += crossProduct;
513         xSum += (current[0] + next[0]) * crossProduct;
514         ySum += (current[1] + next[1]) * crossProduct;
515     }
516     area /= arrCount;
517     xSum /= count * area;
518     ySum /= count * area;
519 end:
520     for (int32_t n = 0; n < count; ++n) {
521         if (vertices[n] != nullptr) {
522             delete[] vertices[n];
523         }
524     }
525     delete[] vertices;
526     return Point(static_cast<float>(xSum), static_cast<float>(ySum));
527 }
528 
CalcAndStoreDistance()529 void TouchGestureDetector::CalcAndStoreDistance()
530 {
531     if (!GestureMonitorHandler::CheckMonitorValid(gestureType_, static_cast<int32_t>(downPoint_.size()))) {
532         return;
533     }
534     lastDistance_.clear();
535     int64_t interval = GetMaxDownInterval();
536     if (interval > MAXIMUM_POINTER_INTERVAL) {
537         MMI_HILOGE("The pointers down time interval is too long");
538         return;
539     }
540     Point center = CalcClusterCenter(downPoint_);
541 
542     for (const auto &[pointerId, downPt] : downPoint_) {
543         double distance = CalcTwoPointsDistance(center, downPt);
544         lastDistance_.emplace(pointerId, distance);
545     }
546 }
547 
CalcMultiFingerMovement(std::map<int32_t,Point> & points)548 int32_t TouchGestureDetector::CalcMultiFingerMovement(std::map<int32_t, Point> &points)
549 {
550     int32_t movementCount = 0;
551     for (const auto &[id, point] : movePoint_) {
552         auto movePoints = points.find(id);
553         if (movePoints == points.end()) {
554             return 0;
555         }
556         if (CalcTwoPointsDistance(point, movePoints->second) >= MAXIMUM_SINGLE_SLIDE_DISTANCE) {
557             ++movementCount;
558         }
559     }
560     return movementCount;
561 }
562 
JudgeOperationMode(std::map<int32_t,Point> & movePoints)563 GestureMode TouchGestureDetector::JudgeOperationMode(std::map<int32_t, Point> &movePoints)
564 {
565     Point center = CalcClusterCenter(movePoints);
566     std::map<int32_t, double> tempDistance;
567     int32_t closeCount = 0;
568     int32_t openCount = 0;
569 
570     for (const auto &[pointerId, _] : downPoint_) {
571         auto movePointIter = movePoints.find(pointerId);
572         if (movePointIter == movePoints.end()) {
573             return GestureMode::ACTION_UNKNOWN;
574         }
575         double currentDistance = CalcTwoPointsDistance(center, movePointIter->second);
576         auto distanceIter = lastDistance_.find(pointerId);
577         if (distanceIter == lastDistance_.end()) {
578             return GestureMode::ACTION_UNKNOWN;
579         }
580         double lastDistance = distanceIter->second;
581         if (currentDistance < lastDistance &&
582             lastDistance - currentDistance >= MINIMUM_GRAVITY_OFFSET) {
583             ++closeCount;
584         } else if (currentDistance > lastDistance &&
585             currentDistance - lastDistance >= MINIMUM_GRAVITY_OFFSET) {
586             ++openCount;
587         }
588         tempDistance.emplace(pointerId, currentDistance);
589         MMI_HILOGI("The pointerId:%{public}d,lastDistance:%{public}.2f,"
590             "currentDistance:%{public}.2f,closeCount:%{public}d,openCount:%{public}d",
591             pointerId, lastDistance, currentDistance, closeCount, openCount);
592     }
593 
594     lastDistance_.swap(tempDistance);
595     GestureMode type = GestureMode::ACTION_UNKNOWN;
596 
597     if (closeCount >= static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
598         type = GestureMode::ACTION_PINCH_CLOSED;
599     } else if (openCount >= static_cast<int32_t>(downPoint_.size() - MINIMUM_FINGER_COUNT_OFFSET)) {
600         type = GestureMode::ACTION_PINCH_OPENED;
601     }
602     return type;
603 }
604 
AntiJitter(std::shared_ptr<PointerEvent> event,GestureMode mode)605 bool TouchGestureDetector::AntiJitter(std::shared_ptr<PointerEvent> event, GestureMode mode)
606 {
607     if (mode == GestureMode::ACTION_PINCH_CLOSED) {
608         ++continuousCloseCount_;
609         if (continuousCloseCount_ >= MAXIMUM_CONTINUOUS_COUNTS) {
610             return NotifyGestureEvent(event, mode);
611         }
612         continuousOpenCount_ = 0;
613     } else if (mode == GestureMode::ACTION_PINCH_OPENED) {
614         ++continuousOpenCount_;
615         if (continuousOpenCount_ >= MAXIMUM_CONTINUOUS_COUNTS) {
616             return NotifyGestureEvent(event, mode);
617         }
618         continuousCloseCount_ = 0;
619     } else {
620         continuousCloseCount_ = 0;
621         continuousOpenCount_ = 0;
622     }
623     return false;
624 }
625 
AddGestureFingers(int32_t fingers)626 void TouchGestureDetector::AddGestureFingers(int32_t fingers)
627 {
628     auto iter = fingers_.insert(fingers);
629     if (!iter.second) {
630         MMI_HILOGE("Insert finger failed, finger:%{public}d", fingers);
631         return;
632     }
633     if (!fingers_.empty()) {
634         gestureEnable_ = true;
635     }
636 }
637 
RemoveGestureFingers(int32_t fingers)638 void TouchGestureDetector::RemoveGestureFingers(int32_t fingers)
639 {
640     auto iter = fingers_.find(fingers);
641     if (iter != fingers_.end()) {
642         fingers_.erase(iter);
643     }
644     if (fingers_.empty()) {
645         gestureEnable_ = false;
646     }
647 }
648 
HandleGestureWindowEmerged(int32_t windowId,std::shared_ptr<PointerEvent> lastTouchEvent)649 void TouchGestureDetector::HandleGestureWindowEmerged(int32_t windowId, std::shared_ptr<PointerEvent> lastTouchEvent)
650 {
651     if ((gestureType_ == TOUCH_GESTURE_TYPE_PINCH) && isRecognized_ && !haveGestureWinEmerged_) {
652         MMI_HILOGI("Gesture window of UNI-CUBIC emerges, redirect touches");
653         haveGestureWinEmerged_ = true;
654         OnGestureSendEvent(lastTouchEvent);
655     }
656 }
657 
IsMatchGesture(int32_t count) const658 bool TouchGestureDetector::IsMatchGesture(int32_t count) const
659 {
660     return fingers_.find(count) != fingers_.end();
661 }
662 
IsMatchGesture(GestureMode mode,int32_t count) const663 bool TouchGestureDetector::IsMatchGesture(GestureMode mode, int32_t count) const
664 {
665     if (!IsMatchGesture(count) && !IsMatchGesture(ALL_FINGER_COUNT)) {
666         return false;
667     }
668     switch (mode) {
669         case GestureMode::ACTION_SWIPE_DOWN:
670         case GestureMode::ACTION_SWIPE_UP:
671         case GestureMode::ACTION_SWIPE_LEFT:
672         case GestureMode::ACTION_SWIPE_RIGHT:
673             return gestureType_ == TOUCH_GESTURE_TYPE_SWIPE;
674         case GestureMode::ACTION_PINCH_OPENED:
675         case GestureMode::ACTION_PINCH_CLOSED:
676             return gestureType_ == TOUCH_GESTURE_TYPE_PINCH;
677         case GestureMode::ACTION_GESTURE_END:
678             return true;
679         default:{
680             MMI_HILOGW("Unknown mode:%{public}d", mode);
681             return false;
682         }
683     }
684 }
685 
NotifyGestureEvent(std::shared_ptr<PointerEvent> event,GestureMode mode)686 bool TouchGestureDetector::NotifyGestureEvent(std::shared_ptr<PointerEvent> event, GestureMode mode)
687 {
688     CHKPF(event);
689     CHKPF(listener_);
690     if (!IsMatchGesture(mode, event->GetPointerCount())) {
691         return false;
692     }
693     if (mode == GestureMode::ACTION_UNKNOWN) {
694         MMI_HILOGE("Wrong gesture");
695         return false;
696     }
697     if (event->GetSourceType() != PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
698         MMI_HILOGW("Handles only touchscreen events");
699         return false;
700     }
701     if (!listener_->OnGestureEvent(event, mode)) {
702         MMI_HILOGE("Failed to notify the gesture(%{public}d) event", mode);
703         return false;
704     }
705     MMI_HILOGI("Gesture(%{public}d) identified successfully", mode);
706     return true;
707 }
708 
CheckGestureTrend(std::shared_ptr<PointerEvent> event) const709 void TouchGestureDetector::CheckGestureTrend(std::shared_ptr<PointerEvent> event) const
710 {
711     CHKPV(listener_);
712     int32_t nMovements { 0 };
713 
714     for (const auto &[pointerId, downPt] : downPoint_) {
715         PointerEvent::PointerItem item {};
716 
717         if (!event->GetPointerItem(pointerId, item)) {
718             MMI_HILOGW("No touch(%{public}d) record", pointerId);
719             return;
720         }
721         Point movePt { item.GetDisplayX(), item.GetDisplayY() };
722 
723         if (IsFingerMove(downPt, movePt)) {
724             ++nMovements;
725         }
726     }
727     if (nMovements >= THREE_FINGER_COUNT) {
728         listener_->OnGestureTrend(event);
729     }
730 }
731 
IsLastTouchUp(std::shared_ptr<PointerEvent> event) const732 bool TouchGestureDetector::IsLastTouchUp(std::shared_ptr<PointerEvent> event) const
733 {
734     return ((event->GetPointerAction() == PointerEvent::POINTER_ACTION_UP) &&
735             (event->GetPointerIds().size() == SINGLE_TOUCH));
736 }
737 
OnGestureSendEvent(std::shared_ptr<PointerEvent> event) const738 void TouchGestureDetector::OnGestureSendEvent(std::shared_ptr<PointerEvent> event) const
739 {
740     CALL_INFO_TRACE;
741     CHKPV(event);
742     event->SetTargetWindowId(-1);
743     auto pointerEvent = std::make_shared<PointerEvent>(*event);
744     pointerEvent->RemoveAllPointerItems();
745     auto items = event->GetAllPointerItems();
746     for (auto &item : items) {
747         if (!item.IsPressed()) {
748             continue;
749         }
750         int32_t pointerId = item.GetPointerId();
751         pointerEvent->SetPointerId(pointerId);
752         pointerEvent->SetPointerAction(PointerEvent::POINTER_ACTION_DOWN);
753         auto now = GetSysClockTime();
754         pointerEvent->SetActionTime(now);
755         pointerEvent->UpdateId();
756         pointerEvent->AddFlag(InputEvent::EVENT_FLAG_NO_INTERCEPT | InputEvent::EVENT_FLAG_NO_MONITOR);
757 
758         item.SetTargetWindowId(-1);
759         event->UpdatePointerItem(pointerId, item);
760         pointerEvent->AddPointerItem(item);
761 
762         MMI_HILOGI("Redirect touch on transparent window of UNI-CUBIC, No:%{public}d, PI:%{public}d",
763             pointerEvent->GetId(), pointerEvent->GetPointerId());
764         auto inputEventNormalizeHandler = InputHandler->GetEventNormalizeHandler();
765         CHKPV(inputEventNormalizeHandler);
766         inputEventNormalizeHandler->HandleTouchEvent(pointerEvent);
767     }
768 }
769 } // namespace MMI
770 } // namespace OHOS