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,bool drag,const UiOpArgs & options)58 static void DecomposeComputeSwipe(PointerMatrix &recv, const Point &from, const Point &to, bool drag, 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 const auto steps = options.swipeStepsCounts_; 70 const uint32_t intervalMs = timeCostMs / steps + 1; 71 constexpr uint32_t fingers = 1; 72 PointerMatrix pointer(fingers, steps + 1); 73 74 pointer.PushAction(TouchEvent {ActionStage::DOWN, {from.px_, from.py_}, 0, intervalMs}); 75 for (uint16_t step = 1; step < steps; step++) { 76 const int32_t pointX = from.px_ + (distanceX * step) / steps; 77 const int32_t pointY = from.py_ + (distanceY * step) / steps; 78 const uint32_t timeOffsetMs = (timeCostMs * step) / steps; 79 pointer.PushAction(TouchEvent {ActionStage::MOVE, {pointX, pointY}, timeOffsetMs, intervalMs}); 80 } 81 82 pointer.PushAction(TouchEvent {ActionStage::UP, {to.px_, to.py_}, timeCostMs, intervalMs}); 83 if (drag) { 84 // drag needs longPressDown firstly 85 pointer.At(fingers - 1, 0).holdMs_ += options.longClickHoldMs_; 86 for (uint32_t idx = 1; idx < pointer.GetSize(); idx++) { 87 pointer.At(fingers - 1, idx).downTimeOffsetMs_ += options.longClickHoldMs_; 88 } 89 } 90 recv = move(pointer); 91 } 92 Decompose(PointerMatrix & recv,const UiOpArgs & options) const93 void GenericClick::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 94 { 95 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 96 switch (type_) { 97 case CLICK: 98 DecomposeClick(recv, point_, options); 99 break; 100 case LONG_CLICK: 101 DecomposeLongClick(recv, point_, options); 102 break; 103 case DOUBLE_CLICK_P: 104 DecomposeDoubleClick(recv, point_, options); 105 break; 106 default: 107 break; 108 } 109 for (uint32_t index = 0; index < recv.GetSize(); index++) { 110 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 111 } 112 } 113 Decompose(PointerMatrix & recv,const UiOpArgs & options) const114 void GenericSwipe::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 115 { 116 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::DRAG); 117 DecomposeComputeSwipe(recv, from_, to_, type_ == TouchOp::DRAG, options); 118 for (uint32_t index = 0; index < recv.GetSize(); index++) { 119 recv.At(recv.GetFingers() - 1, index).flags_ = type_; 120 } 121 } 122 Decompose(PointerMatrix & recv,const UiOpArgs & options) const123 void GenericPinch::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 124 { 125 const int32_t distanceX0 = abs(rect_.GetCenterX() - rect_.left_) * abs(scale_ - 1); 126 PointerMatrix pointer1; 127 PointerMatrix pointer2; 128 if (scale_ > 1) { 129 auto fromPoint0 = Point(rect_.GetCenterX() - options.pinchWidgetDeadZone_, rect_.GetCenterY()); 130 auto toPoint0 = Point((rect_.GetCenterX() - distanceX0), rect_.GetCenterY()); 131 auto fromPoint1 = Point(rect_.GetCenterX() + options.pinchWidgetDeadZone_, rect_.GetCenterY()); 132 auto toPoint1 = Point((rect_.GetCenterX() + distanceX0), rect_.GetCenterY()); 133 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, false, options); 134 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, false, options); 135 } else if (scale_ < 1) { 136 auto fromPoint0 = Point(rect_.left_ + options.pinchWidgetDeadZone_, rect_.GetCenterY()); 137 auto toPoint0 = Point((rect_.left_ + distanceX0), rect_.GetCenterY()); 138 auto fromPoint1 = Point(rect_.right_ - options.pinchWidgetDeadZone_, rect_.GetCenterY()); 139 auto toPoint1 = Point((rect_.right_ - distanceX0), rect_.GetCenterY()); 140 DecomposeComputeSwipe(pointer1, fromPoint0, toPoint0, false, options); 141 DecomposeComputeSwipe(pointer2, fromPoint1, toPoint1, false, options); 142 } 143 144 PointerMatrix pointer3(pointer1.GetFingers() + pointer2.GetFingers(), pointer1.GetSteps()); 145 for (uint32_t index = 0; index < pointer1.GetSize(); index++) { 146 pointer3.PushAction(pointer1.At(0, index)); 147 } 148 for (uint32_t index = 0; index < pointer2.GetSize(); index++) { 149 pointer3.PushAction(pointer2.At(0, index)); 150 } 151 recv = move(pointer3); 152 } 153 Decompose(PointerMatrix & recv,const UiOpArgs & options) const154 void MultiPointerAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 155 { 156 PointerMatrix matrix(pointers_.GetFingers(), pointers_.GetSteps() + 1); 157 constexpr int32_t flag = 0x10000; // set the low 16 bits of data as coordinates. 158 for (uint32_t finger = 0; finger < pointers_.GetFingers(); finger++) { 159 uint32_t timeOffsetMs = 0; 160 uint32_t intervalMs = 0; 161 constexpr uint32_t unitConversionConstant = 1000; 162 for (uint32_t step = 0; step < pointers_.GetSteps() - 1; step++) { 163 const int32_t pxTo = (pointers_.At(finger, step + 1).point_.px_) % flag; 164 const int32_t pxFrom = (pointers_.At(finger, step).point_.px_) % flag; 165 const int32_t distanceX = pxTo - pxFrom; 166 const int32_t pyTo = pointers_.At(finger, step + 1).point_.py_; 167 const int32_t pyFrom = pointers_.At(finger, step).point_.py_; 168 const int32_t distanceY = pyTo - pyFrom; 169 auto stayMs = (pointers_.At(finger, step).point_.px_) / flag; 170 const uint32_t distance = sqrt(distanceX * distanceX + distanceY * distanceY); 171 intervalMs = (distance * unitConversionConstant) / options.swipeVelocityPps_; 172 auto holdMs = (stayMs == 0) ? intervalMs : stayMs; 173 if (step == 0) { 174 matrix.PushAction(TouchEvent {ActionStage::DOWN, {pxFrom, pyFrom}, 0, holdMs}); 175 } else { 176 timeOffsetMs += intervalMs; 177 matrix.PushAction(TouchEvent {ActionStage::MOVE, {pxFrom, pyFrom}, timeOffsetMs, holdMs}); 178 } 179 } 180 auto endPx = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) % flag; 181 auto endPy = pointers_.At(finger, pointers_.GetSteps() - 1).point_.py_; 182 auto endTime = (pointers_.At(finger, pointers_.GetSteps() - 1).point_.px_) / flag; 183 auto endStayTime = (endTime == 0) ? intervalMs : endTime; 184 matrix.PushAction(TouchEvent {ActionStage::MOVE, {endPx, endPy}, timeOffsetMs, endStayTime}); 185 matrix.PushAction(TouchEvent {ActionStage::UP, {endPx, endPy}, timeOffsetMs, intervalMs}); 186 } 187 recv = move(matrix); 188 } 189 PointerMatrix()190 PointerMatrix::PointerMatrix() {}; 191 PointerMatrix(uint32_t fingersNum,uint32_t stepsNum)192 PointerMatrix::PointerMatrix(uint32_t fingersNum, uint32_t stepsNum) 193 { 194 this->fingerNum_ = fingersNum; 195 this->stepNum_ = stepsNum; 196 this->capacity_ = this->fingerNum_ * this->stepNum_; 197 this->size_ = 0; 198 this->data_ = std::make_unique<TouchEvent[]>(this->capacity_); 199 } 200 operator =(PointerMatrix && other)201 PointerMatrix& PointerMatrix::operator=(PointerMatrix&& other) 202 { 203 this->data_ = move(other.data_); 204 this->fingerNum_ = other.fingerNum_; 205 this->stepNum_ = other.stepNum_; 206 this->capacity_ = other.capacity_; 207 this->size_ = other.size_; 208 other.fingerNum_ = 0; 209 other.stepNum_ = 0; 210 other.capacity_ = 0; 211 other.size_ = 0; 212 return *this; 213 } 214 ~PointerMatrix()215 PointerMatrix::~PointerMatrix() {} 216 PushAction(const TouchEvent & ptr)217 void PointerMatrix::PushAction(const TouchEvent& ptr) 218 { 219 if (this->capacity_ == this->size_) { 220 return; 221 } 222 *(this->data_.get() + this->size_) = ptr; 223 this->size_++; 224 } 225 Empty() const226 bool PointerMatrix::Empty() const 227 { 228 if (this->size_ == 0) { 229 return true; 230 } 231 return false; 232 } 233 At(uint32_t fingerIndex,uint32_t stepIndex) const234 TouchEvent& PointerMatrix::At(uint32_t fingerIndex, uint32_t stepIndex) const 235 { 236 return *(this->data_.get() + (fingerIndex * this->stepNum_ + stepIndex)); 237 } 238 GetCapacity() const239 uint32_t PointerMatrix::GetCapacity() const 240 { 241 return this->capacity_; 242 } 243 GetSize() const244 uint32_t PointerMatrix::GetSize() const 245 { 246 return this->size_; 247 } 248 GetSteps() const249 uint32_t PointerMatrix::GetSteps() const 250 { 251 return this->stepNum_; 252 } 253 GetFingers() const254 uint32_t PointerMatrix::GetFingers() const 255 { 256 return this->fingerNum_; 257 } 258 ConvertToMouseEvents(vector<MouseEvent> & recv) const259 void PointerMatrix::ConvertToMouseEvents(vector<MouseEvent> &recv) const 260 { 261 for (auto finger = 0; finger < fingerNum_; finger++) { 262 for (auto step = 0; step < stepNum_; step++) { 263 auto touchEvent = At(finger, step); 264 recv.push_back(MouseEvent {touchEvent.stage_, touchEvent.point_, MouseButton::BUTTON_LEFT, {}, 265 touchEvent.holdMs_}); 266 } 267 } 268 } 269 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const270 void MouseMoveTo::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 271 { 272 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 273 } 274 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const275 void MouseSwipe::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 276 { 277 DCHECK(type_ >= TouchOp::SWIPE && type_ <= TouchOp::DRAG); 278 PointerMatrix touchEvents; 279 DecomposeComputeSwipe(touchEvents, from_, to_, type_ == TouchOp::DRAG, opt); 280 touchEvents.ConvertToMouseEvents(recv); 281 if (type_ == TouchOp::SWIPE) { 282 recv.front().stage_ = ActionStage::MOVE; 283 recv.back().stage_ = ActionStage::MOVE; 284 } 285 } 286 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const287 void MouseClick::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 288 { 289 DCHECK(type_ >= TouchOp::CLICK && type_ <= TouchOp::DOUBLE_CLICK_P); 290 PointerMatrix touchEvents; 291 switch (type_) { 292 case CLICK: 293 DecomposeClick(touchEvents, point_, opt); 294 break; 295 case LONG_CLICK: 296 DecomposeLongClick(touchEvents, point_, opt); 297 break; 298 case DOUBLE_CLICK_P: 299 DecomposeDoubleClick(touchEvents, point_, opt); 300 break; 301 default: 302 break; 303 } 304 touchEvents.ConvertToMouseEvents(recv); 305 for (auto index = 0; index < recv.size(); index++) { 306 recv[index].button_ = button_; 307 } 308 vector<KeyEvent> keyAction1; 309 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 310 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 311 auto keyDown = MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, keyAction1, opt.clickHoldMs_}; 312 recv.insert(recv.begin(), keyDown); 313 314 vector<KeyEvent> keyAction2; 315 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 316 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 317 auto keyUp = MouseEvent {ActionStage::UP, point_, MouseButton::BUTTON_NONE, keyAction2, 0}; 318 recv.push_back(keyUp); 319 } 320 Decompose(std::vector<MouseEvent> & recv,const UiOpArgs & opt) const321 void MouseScroll::Decompose(std::vector<MouseEvent> &recv, const UiOpArgs &opt) const 322 { 323 recv.push_back(MouseEvent {ActionStage::MOVE, point_, MouseButton::BUTTON_NONE, {}, 0}); 324 constexpr int32_t thousandMilliseconds = 1000; 325 auto focusTimeMs = thousandMilliseconds / speed_ / 2; 326 auto stage = (scrollValue_ > 0) ? AXIS_DOWN : AXIS_UP; 327 vector<KeyEvent> keyAction1; 328 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key1_, opt.keyHoldMs_}); 329 keyAction1.push_back(KeyEvent {ActionStage::DOWN, key2_, opt.keyHoldMs_}); 330 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, keyAction1, focusTimeMs}); 331 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 332 333 auto steps = abs(scrollValue_); 334 for (auto index = 1; index < steps - 1; index++) { 335 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 336 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 337 } 338 339 vector<KeyEvent> keyAction2; 340 keyAction2.push_back(KeyEvent {ActionStage::UP, key2_, opt.keyHoldMs_}); 341 keyAction2.push_back(KeyEvent {ActionStage::UP, key1_, opt.keyHoldMs_}); 342 if (steps > 1) { 343 recv.push_back(MouseEvent {stage, point_, MouseButton::BUTTON_NONE, {}, focusTimeMs}); 344 recv.push_back(MouseEvent {ActionStage::AXIS_STOP, point_, MouseButton::BUTTON_NONE, keyAction2, 345 focusTimeMs}); 346 } else { 347 recv.push_back(MouseEvent {ActionStage::NONE, point_, MouseButton::BUTTON_NONE, keyAction2, focusTimeMs}); 348 } 349 } 350 Decompose(PointerMatrix & recv,const UiOpArgs & options) const351 void GenericAtomicAction::Decompose(PointerMatrix &recv, const UiOpArgs &options) const 352 { 353 DCHECK(stage_ >= ActionStage::DOWN && stage_ <= ActionStage::UP); 354 constexpr uint32_t fingers = 1; 355 constexpr uint32_t steps = 1; 356 PointerMatrix pointer(fingers, steps); 357 pointer.PushAction(TouchEvent {stage_, point_, 0, 0, 0}); 358 recv = move(pointer); 359 } 360 } 361