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