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