• 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 #ifdef OHOS_BUILD_ENABLE_DISPLAY_MANAGER
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 #else
76     HILOG_DEBUG("not support display manager");
77     threshold_ = 1;
78     xMinPixels_ = 1;
79     yMinPixels_ = 1;
80     int32_t slop = static_cast<int32_t>(1 * DOUBLE_TAP_SLOP + 0.5f);
81     doubleTapScaledSlop_ = slop * slop;
82 #endif
83 
84     runner_ = Singleton<AccessibleAbilityManagerService>::GetInstance().GetMainRunner();
85     if (!runner_) {
86         HILOG_ERROR("get runner failed");
87         return;
88     }
89     handler_ = std::make_shared<GestureHandler>(runner_, *this);
90     if (!handler_) {
91         HILOG_ERROR("create event handler failed");
92         return;
93     }
94 }
95 
RegisterListener(AccessibilityGestureRecognizeListener & listener)96 void AccessibilityGestureRecognizer::RegisterListener(AccessibilityGestureRecognizeListener& listener)
97 {
98     HILOG_DEBUG();
99 
100     listener_ = &listener;
101 }
102 
UnregisterListener()103 void AccessibilityGestureRecognizer::UnregisterListener()
104 {
105     HILOG_DEBUG();
106 
107     listener_ = nullptr;
108 }
109 
OnPointerEvent(MMI::PointerEvent & event)110 bool AccessibilityGestureRecognizer::OnPointerEvent(MMI::PointerEvent &event)
111 {
112     HILOG_DEBUG();
113 
114     switch (event.GetPointerAction()) {
115         case MMI::PointerEvent::POINTER_ACTION_DOWN:
116             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
117                 HandleTouchDownEvent(event);
118             } else {
119                 Clear();
120                 isRecognizingGesture_ = false;
121                 isGestureStarted_ = false;
122                 pointerRoute_.clear();
123             }
124             break;
125         case MMI::PointerEvent::POINTER_ACTION_MOVE:
126             return HandleTouchMoveEvent(event);
127         case MMI::PointerEvent::POINTER_ACTION_UP:
128             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
129                 return HandleTouchUpEvent(event);
130             }
131             break;
132         case MMI::PointerEvent::POINTER_ACTION_CANCEL:
133             Clear();
134             break;
135         default:
136             break;
137     }
138     if (!isRecognizingGesture_) {
139         return false;
140     }
141     return StandardGestureRecognizer(event);
142 }
143 
Clear()144 void AccessibilityGestureRecognizer::Clear()
145 {
146     HILOG_DEBUG();
147 
148     isFirstTapUp_ = false;
149     isDoubleTap_ = false;
150     isGestureStarted_ = false;
151     isRecognizingGesture_ = false;
152     pointerRoute_.clear();
153     continueDown_ = false;
154     StandardGestureCanceled();
155 }
156 
HandleTouchDownEvent(MMI::PointerEvent & event)157 void AccessibilityGestureRecognizer::HandleTouchDownEvent(MMI::PointerEvent &event)
158 {
159     HILOG_DEBUG();
160 
161     Pointer mp;
162     MMI::PointerEvent::PointerItem pointerIterm;
163     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
164         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
165     }
166     mp.px_ = static_cast<float>(pointerIterm.GetDisplayX());
167     mp.py_ = static_cast<float>(pointerIterm.GetDisplayY());
168     isDoubleTap_ = false;
169     isRecognizingGesture_ = true;
170     isGestureStarted_ = false;
171     pointerRoute_.clear();
172     pointerRoute_.push_back(mp);
173     prePointer_ = pointerIterm;
174     startPointer_ = pointerIterm;
175     startTime_ = event.GetActionTime();
176 }
177 
HandleTouchMoveEvent(MMI::PointerEvent & event)178 bool AccessibilityGestureRecognizer::HandleTouchMoveEvent(MMI::PointerEvent &event)
179 {
180     HILOG_DEBUG();
181 
182     Pointer mp;
183     MMI::PointerEvent::PointerItem pointerIterm;
184     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
185         HILOG_ERROR("get GetPointerItem(%d) failed", event.GetPointerId());
186         return false;
187     }
188     int64_t eventTime = event.GetActionTime();
189     float offsetX = startPointer_.GetDisplayX() - pointerIterm.GetDisplayX();
190     float offsetY = startPointer_.GetDisplayY() - pointerIterm.GetDisplayY();
191     double duration = hypot(offsetX, offsetY);
192     if (isRecognizingGesture_) {
193         if (duration > threshold_) {
194             startPointer_ = pointerIterm;
195             startTime_ = eventTime;
196             isFirstTapUp_ = false;
197             isDoubleTap_ = false;
198             if (!isGestureStarted_) {
199                 isGestureStarted_ = true;
200                 listener_->OnStarted();
201                 return false;
202             }
203         } else if (!isFirstTapUp_) {
204             int64_t durationTime = eventTime - startTime_;
205             int64_t thresholdTime = isGestureStarted_ ?
206                 GESTURE_STARTED_TIME_THRESHOLD : GESTURE_NOT_STARTED_TIME_THRESHOLD;
207             if (durationTime > thresholdTime) {
208                 isRecognizingGesture_ = false;
209                 isGestureStarted_ = false;
210                 pointerRoute_.clear();
211                 return listener_->OnCancelled(event);
212             }
213         }
214         if ((abs(pointerIterm.GetDisplayX() - prePointer_.GetDisplayX())) >= xMinPixels_ ||
215             (abs(pointerIterm.GetDisplayY() - prePointer_.GetDisplayY())) >= yMinPixels_) {
216             HILOG_DEBUG("Add position to pointer route.");
217             prePointer_ = pointerIterm;
218             mp.px_ = pointerIterm.GetDisplayX();
219             mp.py_ = pointerIterm.GetDisplayY();
220             pointerRoute_.push_back(mp);
221         }
222     }
223     if (!isRecognizingGesture_) {
224         return false;
225     }
226     return StandardGestureRecognizer(event);
227 }
228 
HandleTouchUpEvent(MMI::PointerEvent & event)229 bool AccessibilityGestureRecognizer::HandleTouchUpEvent(MMI::PointerEvent &event)
230 {
231     HILOG_DEBUG();
232 
233     Pointer mp;
234     MMI::PointerEvent::PointerItem pointerIterm;
235     if (!event.GetPointerItem(event.GetPointerId(), pointerIterm)) {
236         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
237     }
238 
239     if (isDoubleTap_) {
240         return DoubleTapRecognized(event);
241     }
242     if (isGestureStarted_) {
243         if ((abs(pointerIterm.GetDisplayX() - prePointer_.GetDisplayX())) >= xMinPixels_ ||
244             (abs(pointerIterm.GetDisplayY() - prePointer_.GetDisplayY())) >= yMinPixels_) {
245             HILOG_DEBUG("Add position to pointer route.");
246             mp.px_ = pointerIterm.GetDisplayX();
247             mp.py_ = pointerIterm.GetDisplayY();
248             pointerRoute_.push_back(mp);
249         }
250         return recognizeDirectionGesture(event);
251     }
252     if (!isRecognizingGesture_) {
253         return false;
254     }
255     return StandardGestureRecognizer(event);
256 }
257 
StandardGestureRecognizer(MMI::PointerEvent & event)258 bool AccessibilityGestureRecognizer::StandardGestureRecognizer(MMI::PointerEvent &event)
259 {
260     HILOG_DEBUG();
261 
262     switch (event.GetPointerAction()) {
263         case MMI::PointerEvent::POINTER_ACTION_DOWN:
264             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
265                 if (pCurDown_ && pPreUp_ && isDoubleTap(event)) {
266                     HILOG_DEBUG("Double tap is recognized");
267                     isDoubleTapdetecting_ = true;
268                     isDoubleTap_ = true;
269                 } else {
270                     handler_->SendEvent(SINGLE_TAP_MSG, 0, DOUBLE_TAP_TIMEOUT / US_TO_MS);
271                 }
272                 pCurDown_ = std::make_shared<MMI::PointerEvent>(event);
273                 isTapDown_ = true;
274                 continueDown_ = true;
275                 isLongpress_ = false;
276                 handler_->RemoveEvent(LONG_PRESS_MSG);
277                 handler_->SendEvent(LONG_PRESS_MSG, 0, LONG_PRESS_TIMEOUT / US_TO_MS);
278             } else {
279                 StandardGestureCanceled();
280             }
281             break;
282         case MMI::PointerEvent::POINTER_ACTION_UP:
283             if (event.GetPointerIds().size() == POINTER_COUNT_1) {
284                 continueDown_ = false;
285                 if (isLongpress_) {
286                     handler_->RemoveEvent(SINGLE_TAP_MSG);
287                     isLongpress_ = false;
288                 } else if (!isDoubleTapdetecting_ && isTapDown_) {
289                     isFirstTapUp_ = true;
290                 }
291                 pPreUp_ = std::make_unique<MMI::PointerEvent>(event);
292                 isDoubleTapdetecting_ = false;
293                 handler_->RemoveEvent(LONG_PRESS_MSG);
294             }
295             break;
296         default:
297             break;
298     }
299     return false;
300 }
301 
StandardGestureCanceled()302 void AccessibilityGestureRecognizer::StandardGestureCanceled()
303 {
304     HILOG_DEBUG();
305 
306     handler_->RemoveEvent(LONG_PRESS_MSG);
307     handler_->RemoveEvent(SINGLE_TAP_MSG);
308     isLongpress_ = false;
309     isDoubleTapdetecting_ = false;
310     isTapDown_ = false;
311     isDoubleTap_ = false;
312 }
313 
SingleTapDetected()314 void AccessibilityGestureRecognizer::SingleTapDetected()
315 {
316     HILOG_DEBUG();
317 
318     Clear();
319 }
320 
MaybeRecognizeLongPress(MMI::PointerEvent & event)321 void AccessibilityGestureRecognizer::MaybeRecognizeLongPress(MMI::PointerEvent &event)
322 {
323     HILOG_DEBUG();
324 
325     if (!isDoubleTap_) {
326         return;
327     }
328     Clear();
329 }
330 
DoubleTapRecognized(MMI::PointerEvent & event)331 bool AccessibilityGestureRecognizer::DoubleTapRecognized(MMI::PointerEvent &event)
332 {
333     HILOG_DEBUG();
334 
335     Clear();
336     return listener_->OnDoubleTap(event);
337 }
338 
recognizeDirectionGesture(MMI::PointerEvent & event)339 bool AccessibilityGestureRecognizer::recognizeDirectionGesture(MMI::PointerEvent &event)
340 {
341     HILOG_DEBUG();
342     if (!listener_) {
343         HILOG_ERROR("listener_ is nullptr.");
344         return false;
345     }
346 
347     if (pointerRoute_.size() < LIMIT_SIZE_TWO) {
348         return listener_->OnCancelled(event);
349     }
350 
351     // Check the angle of the most recent motion vector versus the preceding motion vector,
352     // segment the line if the angle is about 90 degrees.
353     std::vector<Pointer> pointerPath = GetPointerPath(pointerRoute_);
354 
355     if (pointerPath.size() == LIMIT_SIZE_TWO) {
356         int32_t swipeDirection = GetSwipeDirection(pointerPath[0], pointerPath[1]);
357         return listener_->OnCompleted(GESTURE_DIRECTION[swipeDirection]);
358     } else if (pointerPath.size() == LIMIT_SIZE_THREE) {
359         int32_t swipeDirectionH = GetSwipeDirection(pointerPath[0], pointerPath[1]);
360         int32_t swipeDirectionHV = GetSwipeDirection(pointerPath[1], pointerPath[2]);
361         return listener_->OnCompleted(GESTURE_DIRECTION_TO_ID[swipeDirectionH][swipeDirectionHV]);
362     }
363     return listener_->OnCancelled(event);
364 }
365 
GetSwipeDirection(Pointer firstP,Pointer secondP)366 int32_t AccessibilityGestureRecognizer::GetSwipeDirection(Pointer firstP, Pointer secondP)
367 {
368     float offsetX = secondP.px_ - firstP.px_;
369     float offsetY = secondP.py_ - firstP.py_;
370     if (abs(offsetX) > abs(offsetY)) {
371         return offsetX > EPSINON ? SWIPE_RIGHT : SWIPE_LEFT;
372     } else {
373         return offsetY < EPSINON ? SWIPE_UP : SWIPE_DOWN;
374     }
375 }
376 
GetPointerPath(std::vector<Pointer> & route)377 std::vector<Pointer> AccessibilityGestureRecognizer::GetPointerPath(std::vector<Pointer> &route)
378 {
379     HILOG_DEBUG();
380 
381     std::vector<Pointer> pointerPath;
382     Pointer firstSeparation = route[0];
383     Pointer nextPoint;
384     Pointer newSeparation;
385     float xUnitVector = 0;
386     float yUnitVector = 0;
387     float xVector = 0;
388     float yVector = 0;
389     float vectorLength = 0;
390     int32_t numSinceFirstSep = 0;
391 
392     pointerPath.push_back(firstSeparation);
393     for (size_t i = 1; i < route.size(); i++) {
394         nextPoint = route[i];
395         if (numSinceFirstSep > 0) {
396             xVector = xUnitVector / numSinceFirstSep;
397             yVector = yUnitVector / numSinceFirstSep;
398             newSeparation.px_ = vectorLength * xVector + firstSeparation.px_;
399             newSeparation.py_ = vectorLength * yVector + firstSeparation.py_;
400 
401             float xNextUnitVector = nextPoint.px_ - newSeparation.px_;
402             float yNextUnitVector = nextPoint.py_ - newSeparation.py_;
403             float nextVectorLength = hypot(xNextUnitVector, yNextUnitVector);
404             if (nextVectorLength > EPSINON) {
405                 xNextUnitVector /= nextVectorLength;
406                 yNextUnitVector /= nextVectorLength;
407             }
408 
409             if ((xVector * xNextUnitVector + yVector * yNextUnitVector) < DEGREES_THRESHOLD) {
410                 pointerPath.push_back(newSeparation);
411                 firstSeparation = newSeparation;
412                 xUnitVector = 0;
413                 yUnitVector = 0;
414                 numSinceFirstSep = 0;
415             }
416         }
417         xVector = nextPoint.px_ - firstSeparation.px_;
418         yVector = nextPoint.py_ - firstSeparation.py_;
419         vectorLength = hypot(xVector, yVector);
420         numSinceFirstSep += 1;
421         if (vectorLength > EPSINON) {
422             xUnitVector += xVector / vectorLength;
423             yUnitVector += yVector / vectorLength;
424         }
425     }
426     pointerPath.push_back(nextPoint);
427     return pointerPath;
428 }
429 
isDoubleTap(MMI::PointerEvent & event)430 bool AccessibilityGestureRecognizer::isDoubleTap(MMI::PointerEvent &event)
431 {
432     HILOG_DEBUG();
433     int64_t durationTime = event.GetActionTime() - pPreUp_->GetActionTime();
434     if (!(durationTime <= DOUBLE_TAP_TIMEOUT && durationTime >= MIN_DOUBLE_TAP_TIME)) {
435         HILOG_WARN("durationTime[%{public}" PRId64 "] is wrong", durationTime);
436         return false;
437     }
438 
439     MMI::PointerEvent::PointerItem curPI;
440     if (!event.GetPointerItem(event.GetPointerId(), curPI)) {
441         HILOG_WARN("get GetPointerItem(%d) failed", event.GetPointerId());
442     }
443 
444     MMI::PointerEvent::PointerItem firstPI;
445     pCurDown_->GetPointerItem(pCurDown_->GetPointerId(), firstPI);
446     int32_t durationX = firstPI.GetDisplayX() - curPI.GetDisplayX();
447     int32_t durationY = firstPI.GetDisplayY() - curPI.GetDisplayY();
448 
449     return (durationX * durationX + durationY * durationY < doubleTapScaledSlop_);
450 }
451 } // namespace Accessibility
452 } // namespace OHOS
453