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