• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed On an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "accessibility_gesture_recognizer.h"
17 #include "hilog_wrapper.h"
18 
19 namespace OHOS {
20 namespace Accessibility {
21 namespace {
22     constexpr int32_t LIMIT_SIZE_TWO = 2;
23     constexpr int32_t LIMIT_SIZE_THREE = 3;
24     constexpr int32_t POINTER_COUNT_1 = 1;
25     constexpr float EPSINON = 0.0001f;
26 } // namespace
27 
GestureHandler(const std::shared_ptr<AppExecFwk::EventRunner> & runner,AccessibilityGestureRecognizer & server)28 GestureHandler::GestureHandler(const std::shared_ptr<AppExecFwk::EventRunner> &runner,
29     AccessibilityGestureRecognizer &server) : AppExecFwk::EventHandler(runner), server_(server)
30 {
31 }
32 
ProcessEvent(const AppExecFwk::InnerEvent::Pointer & event)33 void GestureHandler::ProcessEvent(const AppExecFwk::InnerEvent::Pointer &event)
34 {
35     HILOG_DEBUG();
36     if (!event) {
37         HILOG_ERROR("event is null");
38         return;
39     }
40 
41     switch (event->GetInnerEventId()) {
42         case AccessibilityGestureRecognizer::LONG_PRESS_MSG:
43             RemoveEvent(AccessibilityGestureRecognizer::SINGLE_TAP_MSG);
44             server_.SetIsLongpress(true);
45             server_.MaybeRecognizeLongPress(*server_.GetCurDown());
46             break;
47         case AccessibilityGestureRecognizer::SINGLE_TAP_MSG:
48             if (!server_.GetContinueDown()) {
49                 server_.SingleTapDetected();
50             }
51             break;
52         default:
53             break;
54     }
55 }
56 
AccessibilityGestureRecognizer()57 AccessibilityGestureRecognizer::AccessibilityGestureRecognizer()
58 {
59     HILOG_DEBUG();
60 
61     AccessibilityDisplayManager &displayMgr = Singleton<AccessibilityDisplayManager>::GetInstance();
62     auto display = displayMgr.GetDefaultDisplay();
63     if (!display) {
64         HILOG_ERROR("get display is nullptr");
65         return;
66     }
67 
68     threshold_ = CALCULATION_DIMENSION(display->GetWidth());
69     xMinPixels_ = MIN_PIXELS(display->GetWidth());
70     yMinPixels_ = MIN_PIXELS(display->GetHeight());
71 
72     float densityPixels = display->GetVirtualPixelRatio();
73     int32_t slop = static_cast<int32_t>(densityPixels * DOUBLE_TAP_SLOP + 0.5f);
74     doubleTapScaledSlop_ = slop * slop;
75 
76     runner_ = Singleton<AccessibleAbilityManagerService>::GetInstance().GetMainRunner();
77     if (!runner_) {
78         HILOG_ERROR("get runner failed");
79         return;
80     }
81     handler_ = std::make_shared<GestureHandler>(runner_, *this);
82     if (!handler_) {
83         HILOG_ERROR("create event handler failed");
84         return;
85     }
86 }
87 
RegisterListener(AccessibilityGestureRecognizeListener & listener)88 void AccessibilityGestureRecognizer::RegisterListener(AccessibilityGestureRecognizeListener& listener)
89 {
90     HILOG_DEBUG();
91 
92     listener_ = &listener;
93 }
94 
UnregisterListener()95 void AccessibilityGestureRecognizer::UnregisterListener()
96 {
97     HILOG_DEBUG();
98 
99     listener_ = nullptr;
100 }
101 
OnPointerEvent(MMI::PointerEvent & event)102 bool AccessibilityGestureRecognizer::OnPointerEvent(MMI::PointerEvent &event)
103 {
104     HILOG_DEBUG();
105 
106     switch (event.GetPointerAction()) {
107         case MMI::PointerEvent::POINTER_ACTION_DOWN:
108             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
109                 HandleTouchDownEvent(event);
110             } else {
111                 isRecognizingGesture_ = false;
112                 isGestureStarted_ = false;
113                 pointerRoute_.clear();
114             }
115             break;
116         case MMI::PointerEvent::POINTER_ACTION_MOVE:
117             return HandleTouchMoveEvent(event);
118         case MMI::PointerEvent::POINTER_ACTION_UP:
119             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
120                 return HandleTouchUpEvent(event);
121             }
122             break;
123         case MMI::PointerEvent::POINTER_ACTION_CANCEL:
124             Clear();
125             break;
126         default:
127             break;
128     }
129     if (!isRecognizingGesture_) {
130         return false;
131     }
132     return StandardGestureRecognizer(event);
133 }
134 
Clear()135 void AccessibilityGestureRecognizer::Clear()
136 {
137     HILOG_DEBUG();
138 
139     isFirstTapUp_ = false;
140     isDoubleTap_ = false;
141     isGestureStarted_ = false;
142     isRecognizingGesture_ = false;
143     pointerRoute_.clear();
144     continueDown_ = false;
145     StandardGestureCancled();
146 }
147 
HandleTouchDownEvent(MMI::PointerEvent & event)148 void AccessibilityGestureRecognizer::HandleTouchDownEvent(MMI::PointerEvent &event)
149 {
150     HILOG_DEBUG();
151 
152     Pointer mp;
153     MMI::PointerEvent::PointerItem pointerIterm;
154     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
155         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
156     }
157     mp.px_ = static_cast<float>(pointerIterm.GetDisplayX());
158     mp.py_ = static_cast<float>(pointerIterm.GetDisplayY());
159     isDoubleTap_ = false;
160     isRecognizingGesture_ = true;
161     isGestureStarted_ = false;
162     pointerRoute_.clear();
163     pointerRoute_.push_back(mp);
164     prePointer_ = pointerIterm;
165     startPointer_ = pointerIterm;
166     startTime_ = event.GetActionTime();
167 }
168 
HandleTouchMoveEvent(MMI::PointerEvent & event)169 bool AccessibilityGestureRecognizer::HandleTouchMoveEvent(MMI::PointerEvent &event)
170 {
171     HILOG_DEBUG();
172 
173     Pointer mp;
174     MMI::PointerEvent::PointerItem pointerIterm;
175     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
176         HILOG_ERROR("get GetPointerItem(%d) failed", event.GetPointerId());
177         return false;
178     }
179     int64_t eventTime = event.GetActionTime();
180     float offsetX = startPointer_.GetDisplayX() - pointerIterm.GetDisplayX();
181     float offsetY = startPointer_.GetDisplayY() - pointerIterm.GetDisplayY();
182     double duration = hypot(offsetX, offsetY);
183     if (isRecognizingGesture_) {
184         if (duration > threshold_) {
185             startPointer_ = pointerIterm;
186             startTime_ = eventTime;
187             isFirstTapUp_ = false;
188             isDoubleTap_ = false;
189             if (!isGestureStarted_) {
190                 isGestureStarted_ = true;
191                 listener_->OnStarted();
192                 return false;
193             }
194         } else if (!isFirstTapUp_) {
195             int64_t durationTime = eventTime - startTime_;
196             int64_t thresholdTime = isGestureStarted_ ?
197                 GESTURE_STARTED_TIME_THRESHOLD : GESTURE_NOT_STARTED_TIME_THRESHOLD;
198             if (durationTime > thresholdTime) {
199                 isRecognizingGesture_ = false;
200                 isGestureStarted_ = false;
201                 pointerRoute_.clear();
202                 return listener_->OnCancelled(event);
203             }
204         }
205         if ((abs(pointerIterm.GetDisplayX() - prePointer_.GetDisplayX())) >= xMinPixels_ ||
206             (abs(pointerIterm.GetDisplayY() - prePointer_.GetDisplayY())) >= yMinPixels_) {
207             HILOG_DEBUG("Add position to pointer route.");
208             prePointer_ = pointerIterm;
209             mp.px_ = pointerIterm.GetDisplayX();
210             mp.py_ = pointerIterm.GetDisplayY();
211             pointerRoute_.push_back(mp);
212         }
213     }
214     if (!isRecognizingGesture_) {
215         return false;
216     }
217     return StandardGestureRecognizer(event);
218 }
219 
HandleTouchUpEvent(MMI::PointerEvent & event)220 bool AccessibilityGestureRecognizer::HandleTouchUpEvent(MMI::PointerEvent &event)
221 {
222     HILOG_DEBUG();
223 
224     Pointer mp;
225     MMI::PointerEvent::PointerItem pointerIterm;
226     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
227         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
228     }
229 
230     if (isDoubleTap_) {
231         return DoubleTapRecognized(event);
232     }
233     if (isGestureStarted_) {
234         if ((abs(pointerIterm.GetDisplayX() - prePointer_.GetDisplayX())) >= xMinPixels_ ||
235             (abs(pointerIterm.GetDisplayY() - prePointer_.GetDisplayY())) >= yMinPixels_) {
236             HILOG_DEBUG("Add position to pointer route.");
237             mp.px_ = pointerIterm.GetDisplayX();
238             mp.py_ = pointerIterm.GetDisplayY();
239             pointerRoute_.push_back(mp);
240         }
241         return recognizeDirectionGesture(event);
242     }
243     if (!isRecognizingGesture_) {
244         return false;
245     }
246     return StandardGestureRecognizer(event);
247 }
248 
StandardGestureRecognizer(MMI::PointerEvent & event)249 bool AccessibilityGestureRecognizer::StandardGestureRecognizer(MMI::PointerEvent &event)
250 {
251     HILOG_DEBUG();
252 
253     switch (event.GetPointerAction()) {
254         case MMI::PointerEvent::POINTER_ACTION_DOWN:
255             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
256                 if (pCurDown_ && pPreUp_ && isDoubleTap(event)) {
257                     HILOG_DEBUG("Double tap is recognized");
258                     isDoubleTapdetecting_ = true;
259                     isDoubleTap_ = true;
260                 } else {
261                     handler_->SendEvent(SINGLE_TAP_MSG, 0, DOUBLE_TAP_TIMEOUT / US_TO_MS);
262                 }
263                 pCurDown_ = std::make_shared<MMI::PointerEvent>(event);
264                 isTapDown_ = true;
265                 continueDown_ = true;
266                 isLongpress_ = false;
267                 handler_->RemoveEvent(LONG_PRESS_MSG);
268                 handler_->SendEvent(LONG_PRESS_MSG, 0, LONG_PRESS_TIMEOUT / US_TO_MS);
269             } else {
270                 StandardGestureCancled();
271             }
272             break;
273         case MMI::PointerEvent::POINTER_ACTION_UP:
274             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
275                 continueDown_ = false;
276                 if (isLongpress_) {
277                     handler_->RemoveEvent(SINGLE_TAP_MSG);
278                     isLongpress_ = false;
279                 } else if (!isDoubleTapdetecting_ && isTapDown_) {
280                     isFirstTapUp_ = true;
281                 }
282                 pPreUp_ = std::make_unique<MMI::PointerEvent>(event);
283                 isDoubleTapdetecting_ = false;
284                 handler_->RemoveEvent(LONG_PRESS_MSG);
285             }
286             break;
287         default:
288             break;
289     }
290     return false;
291 }
292 
StandardGestureCancled()293 void AccessibilityGestureRecognizer::StandardGestureCancled()
294 {
295     HILOG_DEBUG();
296 
297     handler_->RemoveEvent(LONG_PRESS_MSG);
298     handler_->RemoveEvent(SINGLE_TAP_MSG);
299     isLongpress_ = false;
300     isDoubleTapdetecting_ = false;
301     isTapDown_ = false;
302 }
303 
SingleTapDetected()304 void AccessibilityGestureRecognizer::SingleTapDetected()
305 {
306     HILOG_DEBUG();
307 
308     Clear();
309 }
310 
MaybeRecognizeLongPress(MMI::PointerEvent & event)311 void AccessibilityGestureRecognizer::MaybeRecognizeLongPress(MMI::PointerEvent &event)
312 {
313     HILOG_DEBUG();
314 
315     if (!isDoubleTap_) {
316         return;
317     }
318     Clear();
319 }
320 
DoubleTapRecognized(MMI::PointerEvent & event)321 bool AccessibilityGestureRecognizer::DoubleTapRecognized(MMI::PointerEvent &event)
322 {
323     HILOG_DEBUG();
324 
325     Clear();
326     return listener_->OnDoubleTap(event);
327 }
328 
recognizeDirectionGesture(MMI::PointerEvent & event)329 bool AccessibilityGestureRecognizer::recognizeDirectionGesture(MMI::PointerEvent &event)
330 {
331     HILOG_DEBUG();
332     if (!listener_) {
333         HILOG_ERROR("listener_ is nullptr.");
334         return false;
335     }
336 
337     if (pointerRoute_.size() < LIMIT_SIZE_TWO) {
338         return listener_->OnCancelled(event);
339     }
340 
341     // Check the angle of the most recent motion vector versus the preceding motion vector,
342     // segment the line if the angle is about 90 degrees.
343     std::vector<Pointer> pointerPath = GetPointerPath(pointerRoute_);
344 
345     if (pointerPath.size() == LIMIT_SIZE_TWO) {
346         int32_t swipeDirection = GetSwipeDirection(pointerPath[0], pointerPath[1]);
347         return listener_->OnCompleted(GESTURE_DIRECTION[swipeDirection]);
348     } else if (pointerPath.size() == LIMIT_SIZE_THREE) {
349         int32_t swipeDirectionH = GetSwipeDirection(pointerPath[0], pointerPath[1]);
350         int32_t swipeDirectionHV = GetSwipeDirection(pointerPath[1], pointerPath[2]);
351         return listener_->OnCompleted(GESTURE_DIRECTION_TO_ID[swipeDirectionH][swipeDirectionHV]);
352     }
353     return listener_->OnCancelled(event);
354 }
355 
GetSwipeDirection(Pointer firstP,Pointer secondP)356 int32_t AccessibilityGestureRecognizer::GetSwipeDirection(Pointer firstP, Pointer secondP)
357 {
358     float offsetX = secondP.px_ - firstP.px_;
359     float offsetY = secondP.py_ - firstP.py_;
360     if (abs(offsetX) > abs(offsetY)) {
361         return offsetX > EPSINON ? SWIPE_RIGHT : SWIPE_LEFT;
362     } else {
363         return offsetY < EPSINON ? SWIPE_UP : SWIPE_DOWN;
364     }
365 }
366 
GetPointerPath(std::vector<Pointer> & route)367 std::vector<Pointer> AccessibilityGestureRecognizer::GetPointerPath(std::vector<Pointer> &route)
368 {
369     HILOG_DEBUG();
370 
371     std::vector<Pointer> pointerPath;
372     Pointer firstSeparation = route[0];
373     Pointer nextPoint;
374     Pointer newSeparation;
375     float xUnitVector = 0;
376     float yUnitVector = 0;
377     float xVector = 0;
378     float yVector = 0;
379     float vectorLength = 0;
380     int32_t numSinceFirstSep = 0;
381 
382     pointerPath.push_back(firstSeparation);
383     for (size_t i = 1; i < route.size(); i++) {
384         nextPoint = route[i];
385         if (numSinceFirstSep > 0) {
386             xVector = xUnitVector / numSinceFirstSep;
387             yVector = yUnitVector / numSinceFirstSep;
388             newSeparation.px_ = vectorLength * xVector + firstSeparation.px_;
389             newSeparation.py_ = vectorLength * yVector + firstSeparation.py_;
390 
391             float xNextUnitVector = nextPoint.px_ - newSeparation.px_;
392             float yNextUnitVector = nextPoint.py_ - newSeparation.py_;
393             float nextVectorLength = hypot(xNextUnitVector, yNextUnitVector);
394             if (nextVectorLength > EPSINON) {
395                 xNextUnitVector /= nextVectorLength;
396                 yNextUnitVector /= nextVectorLength;
397             }
398 
399             if ((xVector * xNextUnitVector + yVector * yNextUnitVector) < DEGREES_THRESHOLD) {
400                 pointerPath.push_back(newSeparation);
401                 firstSeparation = newSeparation;
402                 xUnitVector = 0;
403                 yUnitVector = 0;
404                 numSinceFirstSep = 0;
405             }
406         }
407         xVector = nextPoint.px_ - firstSeparation.px_;
408         yVector = nextPoint.py_ - firstSeparation.py_;
409         vectorLength = hypot(xVector, yVector);
410         numSinceFirstSep += 1;
411         if (vectorLength > EPSINON) {
412             xUnitVector += xVector / vectorLength;
413             yUnitVector += yVector / vectorLength;
414         }
415     }
416     pointerPath.push_back(nextPoint);
417     return pointerPath;
418 }
419 
isDoubleTap(MMI::PointerEvent & event)420 bool AccessibilityGestureRecognizer::isDoubleTap(MMI::PointerEvent &event)
421 {
422     HILOG_DEBUG();
423     int64_t durationTime = event.GetActionTime() - pPreUp_->GetActionTime();
424     if (!(durationTime <= DOUBLE_TAP_TIMEOUT && durationTime >= MIN_DOUBLE_TAP_TIME)) {
425         HILOG_WARN("durationTime[%{public}" PRId64 "] is wrong", durationTime);
426         return false;
427     }
428 
429     MMI::PointerEvent::PointerItem curPI;
430     if (!event.GetPointerItem(event.GetPointerId(), curPI)) {
431         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
432     }
433 
434     MMI::PointerEvent::PointerItem firstPI;
435     pCurDown_->GetPointerItem(pCurDown_->GetPointerId(), firstPI);
436     int32_t durationX = firstPI.GetDisplayX() - curPI.GetDisplayX();
437     int32_t durationY = firstPI.GetDisplayY() - curPI.GetDisplayY();
438 
439     return (durationX * durationX + durationY * durationY < doubleTapScaledSlop_);
440 }
441 } // namespace Accessibility
442 } // namespace OHOS
443