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