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