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 "widget_operator.h" 17 18 namespace OHOS::uitest { 19 using namespace std; 20 using namespace nlohmann; 21 22 static constexpr float SCROLL_MOVE_FACTOR = 0.7; 23 IsScrolledToBorder(int oriDis,const std::vector<unique_ptr<Widget>> & allWidgets,const unique_ptr<Widget> & anchorLeafWidget)24 static bool IsScrolledToBorder(int oriDis, 25 const std::vector<unique_ptr<Widget>> &allWidgets, 26 const unique_ptr<Widget> &anchorLeafWidget) 27 { 28 if (oriDis < 0) { 29 return false; 30 } 31 size_t index = 0; 32 for (; index < allWidgets.size(); ++index) { 33 if (allWidgets.at(index)->GetAttr(UiAttr::ACCESSIBILITY_ID) == 34 anchorLeafWidget->GetAttr(UiAttr::ACCESSIBILITY_ID)) { 35 return std::abs(allWidgets.at(index)->GetBounds().top_ - anchorLeafWidget->GetBounds().top_) < 36 oriDis * SCROLL_MOVE_FACTOR; 37 } 38 } 39 return false; 40 } 41 ConstructNoFilterInWidgetSelector(WidgetSelector & scrollSelector,const std::string & hostApp,const std::string & hashCode)42 static void ConstructNoFilterInWidgetSelector(WidgetSelector &scrollSelector, 43 const std::string &hostApp, 44 const std::string &hashCode) 45 { 46 WidgetSelector parentStrategy; 47 WidgetMatchModel anchorModel2{UiAttr::HASHCODE, hashCode, ValueMatchPattern::EQ}; 48 parentStrategy.AddMatcher(anchorModel2); 49 scrollSelector.AddAppLocator(hostApp); 50 auto error = ApiCallErr(NO_ERROR); 51 scrollSelector.AddParentLocator(parentStrategy, error); 52 scrollSelector.SetWantMulti(true); 53 } 54 CalcFirstLeafWithInScroll(const std::vector<unique_ptr<Widget>> & widgetsInScroll)55 static int CalcFirstLeafWithInScroll(const std::vector<unique_ptr<Widget>>& widgetsInScroll) 56 { 57 std::string parentHie = "ROOT"; 58 int index = -1; 59 for (auto &tempWid : widgetsInScroll) { 60 const std::string &curHie = tempWid->GetHierarchy(); 61 if (curHie.find(parentHie) == std::string::npos) { 62 break; 63 } 64 parentHie = curHie; 65 ++index; 66 } 67 return index; 68 } 69 ConstructScrollFindSelector(const WidgetSelector & selector,const string & hashcode,const string & appName,ApiCallErr & error)70 static WidgetSelector ConstructScrollFindSelector(const WidgetSelector &selector, const string &hashcode, 71 const string &appName, ApiCallErr &error) 72 { 73 WidgetSelector newSelector = selector; 74 WidgetMatchModel anchorModel{UiAttr::HASHCODE, hashcode, ValueMatchPattern::EQ}; 75 WidgetSelector parentSelector{}; 76 parentSelector.AddMatcher(anchorModel); 77 newSelector.AddParentLocator(parentSelector, error); 78 newSelector.AddAppLocator(appName); 79 newSelector.SetWantMulti(false); 80 return newSelector; 81 } 82 WidgetOperator(UiDriver & driver,const Widget & widget,const UiOpArgs & options)83 WidgetOperator::WidgetOperator(UiDriver &driver, const Widget &widget, const UiOpArgs &options) 84 : driver_(driver), widget_(widget), options_(options) 85 { 86 } 87 GenericClick(TouchOp op,ApiCallErr & error) const88 void WidgetOperator::GenericClick(TouchOp op, ApiCallErr &error) const 89 { 90 DCHECK(op >= TouchOp::CLICK && op <= TouchOp::DOUBLE_CLICK_P); 91 auto retrieved = driver_.RetrieveWidget(widget_, error, true); 92 if (error.code_ != NO_ERROR) { 93 return; 94 } 95 const auto center = Point(retrieved->GetBounds().GetCenterX(), retrieved->GetBounds().GetCenterY()); 96 auto touch = OHOS::uitest::GenericClick(op, center); 97 driver_.PerformTouch(touch, options_, error); 98 } 99 ScrollToEnd(bool toTop,ApiCallErr & error) const100 void WidgetOperator::ScrollToEnd(bool toTop, ApiCallErr &error) const 101 { 102 int turnDis = -1; 103 std::unique_ptr<Widget> lastTopLeafWidget = nullptr; 104 std::unique_ptr<Widget> lastBottomLeafWidget = nullptr; 105 while (true) { 106 auto hostApp = driver_.GetHostApp(widget_); 107 WidgetSelector selector{}; 108 ConstructNoFilterInWidgetSelector(selector, hostApp, widget_.GetAttr(UiAttr::HASHCODE)); 109 std::vector<unique_ptr<Widget>> widgetsInScroll; 110 driver_.FindWidgets(selector, widgetsInScroll, error, true); 111 if (error.code_ != NO_ERROR) { 112 LOG_E("There is error when ScrollToEnd, msg is %{public}s", error.message_.c_str()); 113 return; 114 } 115 if (widgetsInScroll.empty()) { 116 LOG_I("There is no child when ScrollToEnd"); 117 return; 118 } 119 if (toTop && IsScrolledToBorder(turnDis, widgetsInScroll, lastTopLeafWidget)) { 120 return; 121 } 122 if (!toTop && IsScrolledToBorder(turnDis, widgetsInScroll, lastBottomLeafWidget)) { 123 return; 124 } 125 if (toTop) { 126 int index = CalcFirstLeafWithInScroll(widgetsInScroll); 127 if (index < 0) { 128 LOG_E("There is error when Find Widget's fist leaf"); 129 return; 130 } 131 lastTopLeafWidget = std::move(widgetsInScroll.at(index)); 132 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1)); 133 } else { 134 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1)); 135 } 136 TurnPage(toTop, turnDis, error); 137 } 138 } 139 DragIntoWidget(const Widget & another,ApiCallErr & error) const140 void WidgetOperator::DragIntoWidget(const Widget &another, ApiCallErr &error) const 141 { 142 auto widgetFrom = driver_.RetrieveWidget(widget_, error); 143 if (widgetFrom == nullptr || error.code_ != NO_ERROR) { 144 return; 145 } 146 auto boundsFrom = widgetFrom->GetBounds(); 147 auto widgetTo = driver_.RetrieveWidget(another, error, false); 148 if (widgetTo == nullptr || error.code_ != NO_ERROR) { 149 return; 150 } 151 auto boundsTo = widgetTo->GetBounds(); 152 auto centerFrom = Point(boundsFrom.GetCenterX(), boundsFrom.GetCenterY()); 153 auto centerTo = Point(boundsTo.GetCenterX(), boundsTo.GetCenterY()); 154 auto touch = GenericSwipe(TouchOp::DRAG, centerFrom, centerTo); 155 driver_.PerformTouch(touch, options_, error); 156 } 157 PinchWidget(float_t scale,ApiCallErr & error) const158 void WidgetOperator::PinchWidget(float_t scale, ApiCallErr &error) const 159 { 160 auto retrieved = driver_.RetrieveWidget(widget_, error); 161 if (retrieved == nullptr || error.code_ != NO_ERROR) { 162 return; 163 } 164 auto rectBound = widget_.GetBounds(); 165 if (scale < 0) { 166 error = ApiCallErr(ERR_INVALID_INPUT, "Please input the correct scale"); 167 return; 168 } 169 auto touch = GenericPinch(rectBound, scale); 170 driver_.PerformTouch(touch, options_, error); 171 } 172 InputText(string_view text,ApiCallErr & error) const173 void WidgetOperator::InputText(string_view text, ApiCallErr &error) const 174 { 175 auto retrieved = driver_.RetrieveWidget(widget_, error); 176 if (retrieved == nullptr || error.code_ != NO_ERROR) { 177 return; 178 } 179 auto origText = retrieved->GetAttr(UiAttr::TEXT); 180 if (origText.empty() && text.empty()) { 181 return; 182 } 183 static constexpr uint32_t focusTimeMs = 500; 184 static constexpr uint32_t typeCharTimeMs = 50; 185 vector<KeyEvent> events; 186 if (!origText.empty()) { 187 for (size_t index = 0; index < origText.size(); index++) { 188 events.emplace_back(KeyEvent{ActionStage::DOWN, KEYCODE_DPAD_RIGHT, typeCharTimeMs}); 189 events.emplace_back(KeyEvent{ActionStage::UP, KEYCODE_DPAD_RIGHT, 0}); 190 events.emplace_back(KeyEvent{ActionStage::DOWN, KEYCODE_DEL, typeCharTimeMs}); 191 events.emplace_back(KeyEvent{ActionStage::UP, KEYCODE_DEL, 0}); 192 } 193 } 194 const auto center = Point(retrieved->GetBounds().GetCenterX(), retrieved->GetBounds().GetCenterY()); 195 auto touch = OHOS::uitest::GenericClick(TouchOp::CLICK, center); 196 driver_.PerformTouch(touch, options_, error); 197 driver_.DelayMs(focusTimeMs); // short delay to ensure focus gaining 198 auto keyActionForDelete = KeysForwarder(events); 199 driver_.TriggerKey(keyActionForDelete, options_, error); 200 driver_.DelayMs(focusTimeMs); 201 auto retrievedAfterClear = driver_.RetrieveWidget(widget_, error); 202 if (retrievedAfterClear == nullptr || error.code_ != NO_ERROR) { 203 return; 204 } 205 const auto centerAfterClear = Point(retrievedAfterClear->GetBounds().GetCenterX(), 206 retrievedAfterClear->GetBounds().GetCenterY()); 207 driver_.InputText(text, centerAfterClear, error); 208 } 209 ScrollFindWidget(const WidgetSelector & selector,ApiCallErr & error) const210 unique_ptr<Widget> WidgetOperator::ScrollFindWidget(const WidgetSelector &selector, ApiCallErr &error) const 211 { 212 bool scrollingUp = true; 213 int turnDis = -1; 214 std::unique_ptr<Widget> lastTopLeafWidget = nullptr; 215 std::unique_ptr<Widget> lastBottomLeafWidget = nullptr; 216 auto hostApp = driver_.GetHostApp(widget_); 217 auto newSelector = ConstructScrollFindSelector(selector, widget_.GetAttr(UiAttr::HASHCODE), hostApp, error); 218 while (true) { 219 std::vector<unique_ptr<Widget>> targetsInScroll; 220 driver_.FindWidgets(newSelector, targetsInScroll, error, true); 221 if (!targetsInScroll.empty()) { 222 return std::move(targetsInScroll.at(0)); 223 } 224 WidgetSelector scrollSelector; 225 ConstructNoFilterInWidgetSelector(scrollSelector, hostApp, widget_.GetAttr(UiAttr::HASHCODE)); 226 std::vector<unique_ptr<Widget>> widgetsInScroll; 227 driver_.FindWidgets(scrollSelector, widgetsInScroll, error, false); 228 if (error.code_ != NO_ERROR) { 229 LOG_E("There is error when Find Widget's subwidget, msg is %{public}s", error.message_.c_str()); 230 return nullptr; 231 } 232 if (widgetsInScroll.empty()) { 233 LOG_I("There is no child when Find Widget's subwidget"); 234 return nullptr; 235 } 236 if (scrollingUp && IsScrolledToBorder(turnDis, widgetsInScroll, lastTopLeafWidget)) { 237 scrollingUp = false; 238 } else if (IsScrolledToBorder(turnDis, widgetsInScroll, lastBottomLeafWidget)) { 239 LOG_W("Scroll search widget failed: %{public}s", selector.Describe().data()); 240 return nullptr; 241 } 242 if (scrollingUp) { 243 int index = CalcFirstLeafWithInScroll(widgetsInScroll); 244 if (index < 0) { 245 LOG_E("There is error when Find Widget's fist leaf"); 246 return nullptr; 247 } 248 lastTopLeafWidget = std::move(widgetsInScroll.at(index)); 249 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1)); 250 } else { 251 lastBottomLeafWidget = std::move(widgetsInScroll.at(widgetsInScroll.size() - 1)); 252 } 253 TurnPage(scrollingUp, turnDis, error); 254 } 255 } 256 TurnPage(bool toTop,int & oriDistance,ApiCallErr & error) const257 void WidgetOperator::TurnPage(bool toTop, int &oriDistance, ApiCallErr &error) const 258 { 259 auto bounds = widget_.GetBounds(); 260 Point topPoint(bounds.GetCenterX(), bounds.top_); 261 Point bottomPoint(bounds.GetCenterX(), bounds.bottom_); 262 if (options_.scrollWidgetDeadZone_ > 0) { 263 topPoint.py_ += options_.scrollWidgetDeadZone_; 264 bottomPoint.py_ -= options_.scrollWidgetDeadZone_; 265 } 266 auto screenSize = driver_.GetDisplaySize(error); 267 auto gestureZone = screenSize.py_ / 20; 268 if (screenSize.py_ - bounds.bottom_ <= gestureZone) { 269 bottomPoint.py_ = bottomPoint.py_ - gestureZone; 270 } 271 auto touch = (toTop) ? GenericSwipe(TouchOp::SWIPE, topPoint, bottomPoint) 272 : GenericSwipe(TouchOp::SWIPE, bottomPoint, topPoint); 273 driver_.PerformTouch(touch, options_, error); 274 oriDistance = std::abs(topPoint.py_ - bottomPoint.py_); 275 if (toTop) { 276 LOG_I("turn page from %{public}d to %{public}d", topPoint.py_, bottomPoint.py_); 277 } else { 278 LOG_I("turn page from %{public}d to %{public}d", bottomPoint.py_, topPoint.py_); 279 } 280 } 281 } // namespace OHOS::uitest 282