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