1 /*
2 * Copyright (C) 2023 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 <map>
17 #include "accessibility_screen_touch.h"
18 #include "accessibility_circle_drawing_manager.h"
19 #include "accessible_ability_manager_service.h"
20 #include "hilog_wrapper.h"
21
22 namespace OHOS {
23 namespace Accessibility {
24
25 constexpr int32_t POINTER_COUNT_1 = 1;
26 constexpr int32_t DISPLAY_WIDTH_RATIO = 150;
27
28 constexpr uint32_t CLICK_RESPONSE_DELAY_SHORT = 0;
29 constexpr uint32_t CLICK_RESPONSE_DELAY_MEDIUM = 1;
30 constexpr uint32_t CLICK_RESPONSE_DELAY_LONG = 2;
31
32 constexpr uint32_t CLICK_RESPONSE_TIME_SHORT = 0; // ms
33 constexpr uint32_t CLICK_RESPONSE_TIME_MEDIUM = 300; // ms
34 constexpr uint32_t CLICK_RESPONSE_TIME_LONG = 600; // ms
35
36 constexpr uint32_t IGNORE_REPEAT_CLICK_SHORTEST = 0;
37 constexpr uint32_t IGNORE_REPEAT_CLICK_SHORT = 1;
38 constexpr uint32_t IGNORE_REPEAT_CLICK_MEDIUM = 2;
39 constexpr uint32_t IGNORE_REPEAT_CLICK_LONG = 3;
40 constexpr uint32_t IGNORE_REPEAT_CLICK_LONGEST = 4;
41
42 constexpr uint32_t IGNORE_REPEAT_CLICK_TIME_SHORTEST = 100; // ms
43 constexpr uint32_t IGNORE_REPEAT_CLICK_TIME_SHORT = 400; // ms
44 constexpr uint32_t IGNORE_REPEAT_CLICK_TIME_MEDIUM = 700; // ms
45 constexpr uint32_t IGNORE_REPEAT_CLICK_TIME_LONG = 1000; // ms
46 constexpr uint32_t IGNORE_REPEAT_CLICK_TIME_LONGEST = 1300; // ms
47
48 constexpr uint32_t CIRCLE_ANGLE = 360;
49
50 constexpr uint32_t NUMBER_10 = 10;
51
52 constexpr uint64_t FOLD_SCREEN_ID = 5; // fold screen main screen id 5
53
54 const std::map<uint32_t, uint32_t> CLICK_RESPONSE_TIME_MAP = {
55 {CLICK_RESPONSE_DELAY_SHORT, CLICK_RESPONSE_TIME_SHORT},
56 {CLICK_RESPONSE_DELAY_MEDIUM, CLICK_RESPONSE_TIME_MEDIUM},
57 {CLICK_RESPONSE_DELAY_LONG, CLICK_RESPONSE_TIME_LONG}
58 };
59
60 const std::map<uint32_t, uint32_t> IGNORE_REPEAT_CLICK_TIME_MAP = {
61 {IGNORE_REPEAT_CLICK_SHORTEST, IGNORE_REPEAT_CLICK_TIME_SHORTEST},
62 {IGNORE_REPEAT_CLICK_SHORT, IGNORE_REPEAT_CLICK_TIME_SHORT},
63 {IGNORE_REPEAT_CLICK_MEDIUM, IGNORE_REPEAT_CLICK_TIME_MEDIUM},
64 {IGNORE_REPEAT_CLICK_LONG, IGNORE_REPEAT_CLICK_TIME_LONG},
65 {IGNORE_REPEAT_CLICK_LONGEST, IGNORE_REPEAT_CLICK_TIME_LONGEST}
66 };
67
68 int64_t AccessibilityScreenTouch::lastUpTime = 0; // global last up time
69
AccessibilityScreenTouch()70 AccessibilityScreenTouch::AccessibilityScreenTouch()
71 {
72 HILOG_DEBUG();
73 // get from account data directly
74 sptr<AccessibilityAccountData> accountData =
75 Singleton<AccessibleAbilityManagerService>::GetInstance().GetCurrentAccountData();
76 clickResponseTime_ = accountData->GetConfig()->GetClickResponseTime();
77 ignoreRepeatClickState_ = accountData->GetConfig()->GetIgnoreRepeatClickState();
78 ignoreRepeatClickTime_ = accountData->GetConfig()->GetIgnoreRepeatClickTime();
79
80 if (clickResponseTime_ > 0 && ignoreRepeatClickState_ == true) {
81 currentState_ = BOTH_RESPONSE_DELAY_IGNORE_REPEAT_CLICK;
82 } else if (clickResponseTime_ > 0) {
83 currentState_ = CLICK_RESPONSE_DELAY_STATE;
84 } else if (ignoreRepeatClickState_ == true) {
85 currentState_ = IGNORE_REPEAT_CLICK_STATE;
86 } else {
87 currentState_ = DEFAULT_STATE;
88 }
89
90 lastUpTime_ = lastUpTime;
91 #ifdef OHOS_BUILD_ENABLE_DISPLAY_MANAGER
92 AccessibilityDisplayManager &displayMgr = Singleton<AccessibilityDisplayManager>::GetInstance();
93 auto display = displayMgr.GetDefaultDisplay();
94 if (!display) {
95 HILOG_ERROR("get display is nullptr");
96 return;
97 }
98
99 threshold_ = CALCULATION_DIMENSION(display->GetWidth()) / DISPLAY_WIDTH_RATIO;
100 #else
101 HILOG_DEBUG("not support display manager");
102 threshold_ = static_cast<double>(CIRCLE_ANGLE) / DISPLAY_WIDTH_RATIO;
103 #endif
104 }
105
~AccessibilityScreenTouch()106 AccessibilityScreenTouch::~AccessibilityScreenTouch()
107 {
108 lastUpTime = lastUpTime_;
109 if (drawCircleThread_ && drawCircleThread_->joinable()) {
110 drawCircleThread_->join();
111 }
112 drawCircleThread_ = nullptr;
113 AccessibilityCircleDrawingManager::DeleteInstance();
114 }
115
GetRealClickResponseTime()116 uint32_t AccessibilityScreenTouch::GetRealClickResponseTime()
117 {
118 auto iter = CLICK_RESPONSE_TIME_MAP.find(clickResponseTime_);
119 if (iter != CLICK_RESPONSE_TIME_MAP.end()) {
120 return iter->second;
121 }
122
123 return CLICK_RESPONSE_TIME_MAP.at(CLICK_RESPONSE_DELAY_SHORT);
124 }
125
GetRealIgnoreRepeatClickTime()126 uint32_t AccessibilityScreenTouch::GetRealIgnoreRepeatClickTime()
127 {
128 auto iter = IGNORE_REPEAT_CLICK_TIME_MAP.find(ignoreRepeatClickTime_);
129 if (iter != IGNORE_REPEAT_CLICK_TIME_MAP.end()) {
130 return iter->second;
131 }
132
133 return IGNORE_REPEAT_CLICK_TIME_MAP.at(IGNORE_REPEAT_CLICK_SHORTEST);
134 }
135
GetRealIgnoreRepeatClickState()136 bool AccessibilityScreenTouch::GetRealIgnoreRepeatClickState()
137 {
138 return ignoreRepeatClickState_;
139 }
140
DrawCircleProgress()141 void AccessibilityScreenTouch::DrawCircleProgress()
142 {
143 HILOG_DEBUG();
144 AccessibilityDisplayManager &displayMgr = Singleton<AccessibilityDisplayManager>::GetInstance();
145 uint64_t screenId = displayMgr.GetDefaultDisplayId(); // default screenId 0
146 if (displayMgr.IsFoldable() == true && displayMgr.GetFoldDisplayMode() == Rosen::FoldDisplayMode::MAIN) {
147 HILOG_DEBUG("fold screen and main screen, screenId %{public}" PRIu64 "", FOLD_SCREEN_ID);
148 screenId = FOLD_SCREEN_ID;
149 }
150
151 AccessibilityCircleDrawingManager::GetInstance()->DrawPointer(circleCenterPhysicalX_,
152 circleCenterPhysicalY_, 0, screenId);
153 AccessibilityCircleDrawingManager::GetInstance()->UpdatePointerVisible(true);
154 uint32_t times = GetRealClickResponseTime() / NUMBER_10;
155 uint32_t step = CIRCLE_ANGLE / times;
156 uint32_t time = 0;
157 while (time < times && isStopDrawCircle_ == false) {
158 AccessibilityCircleDrawingManager::GetInstance()->DrawPointer(circleCenterPhysicalX_,
159 circleCenterPhysicalY_, step * time, screenId);
160 time++;
161 std::this_thread::yield();
162 std::this_thread::sleep_for(std::chrono::milliseconds(NUMBER_10));
163 }
164
165 AccessibilityCircleDrawingManager::GetInstance()->UpdatePointerVisible(false);
166 }
167
HandleResponseDelayStateInnerDown(MMI::PointerEvent & event)168 void AccessibilityScreenTouch::HandleResponseDelayStateInnerDown(MMI::PointerEvent &event)
169 {
170 HILOG_DEBUG();
171 MMI::PointerEvent::PointerItem pointerItem;
172 if (!event.GetPointerItem(event.GetPointerId(), pointerItem)) {
173 HILOG_WARN("get GetPointerItem %{public}d failed", event.GetPointerId());
174 }
175
176 startTime_ = event.GetActionTime();
177 startPointer_ = pointerItem;
178 isMoveBeyondThreshold_ = false;
179 isFirstDownEvent_ = true;
180
181 circleCenterPhysicalX_ = pointerItem.GetDisplayX();
182 circleCenterPhysicalY_ = pointerItem.GetDisplayY();
183 isStopDrawCircle_ = false;
184 if (drawCircleThread_ && drawCircleThread_->joinable()) {
185 drawCircleThread_->join();
186 }
187
188 drawCircleThread_ = nullptr;
189 drawCircleThread_ = std::make_shared<std::thread>(&AccessibilityScreenTouch::DrawCircleProgress, this);
190 if (drawCircleThread_ == nullptr) {
191 HILOG_ERROR("create draw circle progress fail");
192 }
193 }
194
HandleResponseDelayStateInnerMove(MMI::PointerEvent & event)195 void AccessibilityScreenTouch::HandleResponseDelayStateInnerMove(MMI::PointerEvent &event)
196 {
197 HILOG_DEBUG();
198 if (isMoveBeyondThreshold_ == true) {
199 EventTransmission::OnPointerEvent(event);
200 return;
201 }
202
203 MMI::PointerEvent::PointerItem pointerItem;
204 if (!event.GetPointerItem(event.GetPointerId(), pointerItem)) {
205 HILOG_WARN("get GetPointerItem %{public}d failed", event.GetPointerId());
206 }
207
208 float offsetX = startPointer_.GetDisplayX() - pointerItem.GetDisplayX();
209 float offsetY = startPointer_.GetDisplayY() - pointerItem.GetDisplayY();
210 double duration = hypot(offsetX, offsetY);
211 if (duration > threshold_) {
212 event.SetPointerAction(MMI::PointerEvent::POINTER_ACTION_DOWN);
213 EventTransmission::OnPointerEvent(event);
214 isMoveBeyondThreshold_ = true;
215 isStopDrawCircle_ = true;
216 return;
217 }
218
219 if ((startTime_ + static_cast<int64_t>(GetRealClickResponseTime()) * US_TO_MS) >
220 static_cast<int64_t>(event.GetActionTime())) {
221 circleCenterPhysicalX_ = pointerItem.GetDisplayX();
222 circleCenterPhysicalY_ = pointerItem.GetDisplayY();
223 return;
224 }
225
226 isStopDrawCircle_ = true;
227 if (isFirstDownEvent_ == true) {
228 event.SetPointerAction(MMI::PointerEvent::POINTER_ACTION_DOWN);
229 EventTransmission::OnPointerEvent(event);
230 isFirstDownEvent_ = false;
231 return;
232 }
233
234 EventTransmission::OnPointerEvent(event);
235 }
236
HandleResponseDelayStateInnerUp(MMI::PointerEvent & event)237 void AccessibilityScreenTouch::HandleResponseDelayStateInnerUp(MMI::PointerEvent &event)
238 {
239 HILOG_DEBUG();
240 isStopDrawCircle_ = true;
241 if (isMoveBeyondThreshold_ == true) {
242 EventTransmission::OnPointerEvent(event);
243 return;
244 }
245
246 if ((startTime_ + static_cast<int64_t>(GetRealClickResponseTime()) * US_TO_MS) >
247 static_cast<int64_t>(event.GetActionTime())) {
248 return;
249 }
250
251 if (isFirstDownEvent_ == true) {
252 return;
253 }
254
255 EventTransmission::OnPointerEvent(event);
256 }
257
HandleResponseDelayState(MMI::PointerEvent & event)258 void AccessibilityScreenTouch::HandleResponseDelayState(MMI::PointerEvent &event)
259 {
260 HILOG_DEBUG();
261 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
262 return;
263 }
264
265 switch (event.GetPointerAction()) {
266 case MMI::PointerEvent::POINTER_ACTION_DOWN:
267 HandleResponseDelayStateInnerDown(event);
268 break;
269 case MMI::PointerEvent::POINTER_ACTION_MOVE:
270 HandleResponseDelayStateInnerMove(event);
271 break;
272 case MMI::PointerEvent::POINTER_ACTION_UP:
273 HandleResponseDelayStateInnerUp(event);
274 break;
275 default:
276 break;
277 }
278 }
279
HandleIgnoreRepeatClickStateInnerDown(MMI::PointerEvent & event)280 void AccessibilityScreenTouch::HandleIgnoreRepeatClickStateInnerDown(MMI::PointerEvent &event)
281 {
282 HILOG_DEBUG();
283 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
284 return;
285 }
286
287 int64_t downTime = event.GetActionTime();
288 if ((downTime - lastUpTime_) / US_TO_MS < GetRealIgnoreRepeatClickTime()) {
289 isInterceptClick_ = true;
290 return;
291 }
292
293 EventTransmission::OnPointerEvent(event);
294 isInterceptClick_ = false;
295 }
296
HandleIgnoreRepeatClickStateInnerMove(MMI::PointerEvent & event)297 void AccessibilityScreenTouch::HandleIgnoreRepeatClickStateInnerMove(MMI::PointerEvent &event)
298 {
299 HILOG_DEBUG();
300 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
301 return;
302 }
303
304 if (isInterceptClick_ == false) {
305 EventTransmission::OnPointerEvent(event);
306 }
307 }
308
HandleIgnoreRepeatClickStateInnerUp(MMI::PointerEvent & event)309 void AccessibilityScreenTouch::HandleIgnoreRepeatClickStateInnerUp(MMI::PointerEvent &event)
310 {
311 HILOG_DEBUG();
312 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
313 return;
314 }
315
316 if (isInterceptClick_ == false) {
317 EventTransmission::OnPointerEvent(event);
318 lastUpTime_ = event.GetActionTime();
319 }
320 }
321
HandleIgnoreRepeatClickState(MMI::PointerEvent & event)322 void AccessibilityScreenTouch::HandleIgnoreRepeatClickState(MMI::PointerEvent &event)
323 {
324 HILOG_DEBUG();
325 switch (event.GetPointerAction()) {
326 case MMI::PointerEvent::POINTER_ACTION_DOWN:
327 HandleIgnoreRepeatClickStateInnerDown(event);
328 break;
329 case MMI::PointerEvent::POINTER_ACTION_MOVE:
330 HandleIgnoreRepeatClickStateInnerMove(event);
331 break;
332 case MMI::PointerEvent::POINTER_ACTION_UP:
333 HandleIgnoreRepeatClickStateInnerUp(event);
334 break;
335 default:
336 break;
337 }
338 }
339
HandleBothStateInnerDown(MMI::PointerEvent & event)340 void AccessibilityScreenTouch::HandleBothStateInnerDown(MMI::PointerEvent &event)
341 {
342 HILOG_DEBUG();
343 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
344 return;
345 }
346
347 int64_t downTime = event.GetActionTime();
348 if ((downTime - lastUpTime_) / US_TO_MS < GetRealIgnoreRepeatClickTime()) {
349 isInterceptClick_ = true;
350 return;
351 }
352
353 HandleResponseDelayStateInnerDown(event);
354 isInterceptClick_ = false;
355 }
356
HandleBothStateInnerMove(MMI::PointerEvent & event)357 void AccessibilityScreenTouch::HandleBothStateInnerMove(MMI::PointerEvent &event)
358 {
359 HILOG_DEBUG();
360 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
361 return;
362 }
363
364 if (isInterceptClick_ == true) {
365 return;
366 }
367
368 HandleResponseDelayStateInnerMove(event);
369 }
370
HandleBothStateInnerUp(MMI::PointerEvent & event)371 void AccessibilityScreenTouch::HandleBothStateInnerUp(MMI::PointerEvent &event)
372 {
373 HILOG_DEBUG();
374 if (event.GetPointerIds().size() > POINTER_COUNT_1) {
375 return;
376 }
377
378 if (isInterceptClick_ == true) {
379 return;
380 }
381
382 lastUpTime_ = event.GetActionTime();
383 HandleResponseDelayStateInnerUp(event);
384 }
385
HandleBothState(MMI::PointerEvent & event)386 void AccessibilityScreenTouch::HandleBothState(MMI::PointerEvent &event)
387 {
388 HILOG_DEBUG();
389 switch (event.GetPointerAction()) {
390 case MMI::PointerEvent::POINTER_ACTION_DOWN:
391 HandleBothStateInnerDown(event);
392 break;
393 case MMI::PointerEvent::POINTER_ACTION_MOVE:
394 HandleBothStateInnerMove(event);
395 break;
396 case MMI::PointerEvent::POINTER_ACTION_UP:
397 HandleBothStateInnerUp(event);
398 break;
399 default:
400 break;
401 }
402 }
403
Clear()404 void AccessibilityScreenTouch::Clear()
405 {
406 isMoveBeyondThreshold_ = false;
407 isInterceptClick_ = false;
408 startPointer_ = {};
409 }
410
OnPointerEvent(MMI::PointerEvent & event)411 bool AccessibilityScreenTouch::OnPointerEvent(MMI::PointerEvent &event)
412 {
413 HILOG_DEBUG();
414 if (event.GetSourceType() != MMI::PointerEvent::SOURCE_TYPE_TOUCHSCREEN) {
415 EventTransmission::OnPointerEvent(event);
416 return false;
417 }
418
419 if (event.GetPointerAction() == MMI::PointerEvent::POINTER_ACTION_CANCEL) {
420 Clear();
421 return true;
422 }
423
424 switch (currentState_) {
425 case ScreenTouchState::CLICK_RESPONSE_DELAY_STATE:
426 HandleResponseDelayState(event);
427 break;
428 case ScreenTouchState::IGNORE_REPEAT_CLICK_STATE:
429 HandleIgnoreRepeatClickState(event);
430 break;
431 case ScreenTouchState::BOTH_RESPONSE_DELAY_IGNORE_REPEAT_CLICK:
432 HandleBothState(event);
433 break;
434 case ScreenTouchState::DEFAULT_STATE:
435 default:
436 EventTransmission::OnPointerEvent(event);
437 }
438
439 return true;
440 }
441 } // namespace Accessibility
442 } // namespace OHOS