1 /* 2 * Copyright (c) 2021-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 <cmath> 17 #include "ui_action.h" 18 19 namespace OHOS::uitest { 20 using namespace std; 21 using namespace nlohmann; 22 DecomposeClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options)23 static void DecomposeClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 24 { 25 constexpr uint32_t fingers = 1; 26 constexpr uint32_t steps = 2; 27 PointerMatrix pointer(fingers, steps); 28 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 29 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, 0}); 30 recv = move(pointer); 31 } 32 DecomposeLongClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options)33 static void DecomposeLongClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 34 { 35 // should sleep after touch-down to make long-click duration 36 constexpr uint32_t fingers = 1; 37 constexpr uint32_t steps = 2; 38 PointerMatrix pointer(fingers, steps); 39 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.longClickHoldMs_}); 40 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.longClickHoldMs_, 0}); 41 recv = move(pointer); 42 } 43 DecomposeDoubleClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options)44 static void DecomposeDoubleClick(PointerMatrix &recv, const Point &point, const UiOpArgs &options) 45 { 46 const auto msInterval = options.doubleClickIntervalMs_; 47 constexpr uint32_t fingers = 1; 48 constexpr uint32_t steps = 4; 49 PointerMatrix pointer(fingers, steps); 50 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 51 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, msInterval}); 52 53 pointer.PushAction(TouchEvent {ActionStage::DOWN, point, 0, options.clickHoldMs_}); 54 pointer.PushAction(TouchEvent {ActionStage::UP, point, options.clickHoldMs_, 0}); 55 recv = move(pointer); 56 } 57 DecomposeComputeSwipe(PointerMatrix &recv, const Point &from, const Point &to, TouchOp type, const UiOpArgs &options)58 static void DecomposeComputeSwipe(PointerMatrix &recv, const Point &from, const Point &to, TouchOp type, 59 const UiOpArgs &options) 60 { 61 const int32_t distanceX = to.px_ - from.px_; 62 const int32_t distanceY = to.py_ - from.py_; 63 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 64 const uint32_t timeCostMs = (distance * 1000) / options.swipeVelocityPps_; 65 if (distance < 1) { 66 // do not need to execute swipe 67 return; 68 } 69 uint32_t steps = options.swipeStepsCounts_; 70 uint32_t intervalMs = timeCostMs / steps + 1; 71 constexpr uint32_t fingers = 1; 72 constexpr uint32_t intervalMsInSwipe = 5; 73 if (type != TouchOp::FLING) { 74 steps = timeCostMs / intervalMsInSwipe; 75 intervalMs = intervalMsInSwipe; 76 } 77 PointerMatrix pointer(fingers, steps + 1); 78 79 pointer.PushAction(TouchEvent {ActionStage::DOWN, {from.px_, from.py_}, 0, intervalMs}); 80 float stepLengthX = static_cast<double>(distanceX) / static_cast<double>(steps); 81 float stepLengthY = static_cast<double>(distanceY) / static_cast<double>(steps); 82 83 for (uint32_t step = 1; step < steps; step++) { 84 const int32_t pointX = from.px_ + stepLengthX * step; 85 const int32_t pointY = from.py_ + stepLengthY * step; 86 const uint32_t timeOffsetMs = (timeCostMs * step) / steps; 87 pointer.PushAction(TouchEvent {ActionStage::MOVE, {pointX, pointY}, timeOffsetMs, intervalMs}); 88 } 89 90 pointer.PushAction(TouchEvent {ActionStage::UP, {to.px_, to.py_}, timeCostMs, intervalMs}); 91 if (type == TouchOp::DRAG) { 92 // drag needs longPressDown firstly 93 pointer.At(fingers - 1, 0).holdMs_ += options.longClickHoldMs_; 94 for (uint32_t idx = 1; idx < pointer.GetSize(); idx++) { 95 pointer.At(fingers - 1, idx).downTimeOffsetMs_ += options.longClickHoldMs_; 96 } 97 } 98 recv = move(pointer); 99 } 100 Decompose(PointerMatrix &recv, const UiOpArgs &options) const101 void GenericClick::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 102 { 103 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 104 switch (type_) { 105 case CLICK: 106 DecomposeClick(recv, point_, options); 107 break; 108 case LONG_CLICK: 109 DecomposeLongClick(recv, point_, options); 110 break; 111 case DOUBLE_CLICK_P: 112 DecomposeDoubleClick(recv, point_, options); 113 break; 114 default: 115 break; 116 } 117 for (uint32_t index = 0; index < recv.GetSize(); index++) { 118 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 119 } 120 } 121 Decompose(PointerMatrix &recv, const UiOpArgs &options) const122 void GenericSwipe::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 123 { 124 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::FLING); 125 DecomposeComputeSwipe(recv, from_, to_, type_, options); 126 for (uint32_t index = 0; index < recv.GetSize(); index++) { 127 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 128 } 129 } 130 Decompose(PointerMatrix &recv, const UiOpArgs &options) const131 void GenericPinch::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 132 { 133 const int32_t distanceX0 = abs(rect_.GetCenterX() - rect_.left_) * abs(scale_ - 1); 134 PointerMatrix pointer1; 135 PointerMatrix pointer2; 136 if (scale_ > 1) { 137 auto fromPoint0 = Point(rect_.GetCenterX() - options.pinchWidgetDeadZone_, rect_.GetCenterY()); 138 auto toPoint0 = Point((fromPoint0.px_ - distanceX0), rect_.GetCenterY()); 139 auto fromPoint1 = Point(rect_.GetCenterX() + options.pinchWidgetDeadZone_, rect_.GetCenterY()); 140 auto toPoint1 = Point((fromPoint1.px_ + distanceX0), rect_.GetCenterY()); 141 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, TouchOp::SWIPE, options); 142 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, TouchOp::SWIPE, options); 143 } else if (scale_ < 1) { 144 auto fromPoint0 = Point(rect_.left_ + options.pinchWidgetDeadZone_, rect_.GetCenterY()); 145 auto toPoint0 = Point((fromPoint0.px_ + distanceX0), rect_.GetCenterY()); 146 auto fromPoint1 = Point(rect_.right_ - options.pinchWidgetDeadZone_, rect_.GetCenterY()); 147 auto toPoint1 = Point((fromPoint1.px_- distanceX0), rect_.GetCenterY()); 148 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, TouchOp::SWIPE, options); 149 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, TouchOp::SWIPE, options); 150 } 151 152 PointerMatrix pointer3(pointer1.GetFingers() + pointer2.GetFingers(), pointer1.GetSteps()); 153 for (uint32_t index = 0; index < pointer1.GetSize(); index++) { 154 pointer3.PushAction(pointer1.At(0, index)); 155 } 156 for (uint32_t index = 0; index < pointer2.GetSize(); index++) { 157 pointer3.PushAction(pointer2.At(0, index)); 158 } 159 recv = move(pointer3); 160 } 161 Decompose(PointerMatrix &recv, const UiOpArgs &options) const162 void MultiPointerAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 163 { 164 PointerMatrix matrix(pointers_.GetFingers(), pointers_.GetSteps() + 1); 165 constexpr int32_t flag = 0x10000; // set the low 16 bits of data as coordinates. 166 for (uint32_t finger = 0; finger < pointers_.GetFingers(); finger++) { 167 uint32_t timeOffsetMs = 0; 168 uint32_t intervalMs = 0; 169 constexpr uint32_t unitConversionConstant = 1000; 170 for (uint32_t step = 0; step < pointers_.GetSteps() - 1; step++) { 171 const int32_t pxTo = (pointers_.At(finger, step + 1).point_.px_) % flag; 172 const int32_t pxFrom = (pointers_.At(finger, step).point_.px_) % flag; 173 const int32_t distanceX = pxTo - pxFrom; 174 const int32_t pyTo = pointers_.At(finger, step + 1).point_.py_; 175 const int32_t pyFrom = pointers_.At(finger, step).point_.py_; 176 const int32_t distanceY = pyTo - pyFrom; 177 auto stayMs = (pointers_.At(finger, step).point_.px_) / flag; 178 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 179 intervalMs = (distance * unitConversionConstant) / options.swipeVelocityPps_; 180 auto holdMs = (stayMs == 0) ? intervalMs : stayMs; 181 if (step == 0) { 182 matrix.PushAction(TouchEvent {ActionStage::DOWN, {pxFrom, pyFrom}, 0, holdMs}); 183 } else { 184 timeOffsetMs += intervalMs; 185 matrix.PushAction(TouchEvent {ActionStage::MOVE, {pxFrom, pyFrom}, timeOffsetMs, holdMs}); 186 } 187 } 188 auto endPx = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) % flag; 189 auto endPy = pointers_.At(finger, pointers_.GetSteps() - 1).point_.py_; 190 auto endTime = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) / flag; 191 auto endStayTime = (endTime == 0) ? intervalMs : endTime; 192 matrix.PushAction(TouchEvent {ActionStage::MOVE, {endPx, endPy}, timeOffsetMs, endStayTime}); 193 matrix.PushAction(TouchEvent {ActionStage::UP, {endPx, endPy}, timeOffsetMs, intervalMs}); 194 } 195 recv = move(matrix); 196 } 197 PointerMatrix()198 PointerMatrix::PointerMatrix() {}; 199 PointerMatrix(uint32_t fingersNum, uint32_t stepsNum)200 PointerMatrix::PointerMatrix(uint32_t fingersNum, uint32_t stepsNum) 201 { 202 this->fingerNum_ = fingersNum; 203 this->stepNum_ = stepsNum; 204 this->capacity_ = this->fingerNum_ * this->stepNum_; 205 this->size_ = 0; 206 this->data_ = std::make_unique<TouchEvent[]>(this->capacity_); 207 } 208 operator =(PointerMatrix&& other)209 PointerMatrix& PointerMatrix::operator=(PointerMatrix&& other) 210 { 211 this->data_ = move(other.data_); 212 this->fingerNum_ = other.fingerNum_; 213 this->stepNum_ = other.stepNum_; 214 this->capacity_ = other.capacity_; 215 this->size_ = other.size_; 216 other.fingerNum_ = 0; 217 other.stepNum_ = 0; 218 other.capacity_ = 0; 219 other.size_ = 0; 220 return *this; 221 } 222 ~PointerMatrix()223 PointerMatrix::~PointerMatrix() {} 224 PushAction(const TouchEvent& ptr)225 void PointerMatrix::PushAction(const TouchEvent& ptr) 226 { 227 if (this->capacity_ == this->size_) { 228 return; 229 } 230 *(this->data_.get() + this->size_) = ptr; 231 this->size_++; 232 } 233 Empty() const234 bool PointerMatrix::Empty() const 235 { 236 if (this->size_ == 0) { 237 return true; 238 } 239 return false; 240 } 241 At(uint32_t fingerIndex, uint32_t stepIndex) const242 TouchEvent& PointerMatrix::At(uint32_t fingerIndex, uint32_t stepIndex) const 243 { 244 return *(this->data_.get() + (fingerIndex * this->stepNum_ + stepIndex)); 245 } 246 GetCapacity() const247 uint32_t PointerMatrix::GetCapacity() const 248 { 249 return this->capacity_; 250 } 251 GetSize() const252 uint32_t PointerMatrix::GetSize() const 253 { 254 return this->size_; 255 } 256 GetSteps() const257 uint32_t PointerMatrix::GetSteps() const 258 { 259 return this->stepNum_; 260 } 261 GetFingers() const262 uint32_t PointerMatrix::GetFingers() const 263 { 264 return this->fingerNum_; 265 } 266 ConvertToMouseEvents(vector<MouseEvent> &recv) const267 void PointerMatrix::ConvertToMouseEvents(vector<MouseEvent> &recv) const 268 { 269 for (uint32_t finger = 0; finger < fingerNum_; finger++) { 270 for (uint32_t step = 0; step < stepNum_; step++) { 271 auto touchEvent = At(finger, step); 272 recv.push_back(MouseEvent {touchEvent.stage_, touchEvent.point_, MouseButton::BUTTON_LEFT, {}, 273 touchEvent.holdMs_}); 274 } 275 } 276 } 277 Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const278 void MouseMoveTo::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 279 { 280 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 281 } 282 Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const283 void MouseSwipe::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 284 { 285 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::DRAG); 286 PointerMatrix touchEvents; 287 DecomposeComputeSwipe(touchEvents, from_, to_, type_, opt); 288 touchEvents.ConvertToMouseEvents(recv); 289 if (type_ == TouchOp::SWIPE) { 290 recv.front().stage_ = ActionStage::MOVE; 291 recv.back().stage_ = ActionStage::MOVE; 292 } 293 } 294 Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const295 void MouseClick::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 296 { 297 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 298 PointerMatrix touchEvents; 299 switch (type_) { 300 case CLICK: 301 DecomposeClick(touchEvents, point_, opt); 302 break; 303 case LONG_CLICK: 304 DecomposeLongClick(touchEvents, point_, opt); 305 break; 306 case DOUBLE_CLICK_P: 307 DecomposeDoubleClick(touchEvents, point_, opt); 308 break; 309 default: 310 break; 311 } 312 touchEvents.ConvertToMouseEvents(recv); 313 for (size_t index = 0; index < recv.size(); index++) { 314 recv[index].button_ = button_; 315 } 316 vector<KeyEvent> keyAction1; 317 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 318 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 319 auto keyDown = MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, keyAction1, opt.clickHoldMs_}; 320 recv.insert(recv.begin(), keyDown); 321 322 vector<KeyEvent> keyAction2; 323 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 324 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 325 auto keyUp = MouseEvent {ActionStage::UP, point_, MouseButton::BUTTON_NONE, keyAction2, 0}; 326 recv.push_back(keyUp); 327 } 328 Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const329 void MouseScroll::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 330 { 331 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 332 constexpr int32_t thousandMilliseconds = 1000; 333 auto focusTimeMs = thousandMilliseconds / speed_ / 2; 334 auto stage = (scrollValue_ > 0) ? AXIS_DOWN : AXIS_UP; 335 vector<KeyEvent> keyAction1; 336 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 337 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 338 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, keyAction1, focusTimeMs}); 339 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 340 341 auto steps = abs(scrollValue_); 342 for (auto index = 1; index < steps - 1; index++) { 343 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 344 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 345 } 346 347 vector<KeyEvent> keyAction2; 348 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 349 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 350 if (steps > 1) { 351 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 352 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, keyAction2, 353 focusTimeMs}); 354 } else { 355 recv.push_back(MouseEvent {ActionStage::NONE, point_, MouseButton::BUTTON_NONE, keyAction2, focusTimeMs}); 356 } 357 } 358 Decompose(PointerMatrix &recv, const UiOpArgs &options) const359 void GenericAtomicAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 360 { 361 DCHECK(stage_ >= ActionStage::DOWN && stage_ <= ActionStage::UP); 362 constexpr uint32_t fingers = 1; 363 constexpr uint32_t steps = 1; 364 PointerMatrix pointer(fingers, steps); 365 pointer.PushAction(TouchEvent {stage_, point_, 0, 0, 0}); 366 recv = move(pointer); 367 } 368 } 369