1 import os, importlib 2 import uiautomator2 as u2 3 import traceback 4 5 from android import AndroidDevice 6 from log import * 7 8 ANDROID_MIXIN_BASE_LIST = [] 9 ANDROID_VERSION_LIST = [] 10 android_device_root_path = os.path.join(os.path.dirname(os.path.abspath(__file__))) 11 for root,dirs,files in os.walk(android_device_root_path): 12 for f in files: 13 if f.endswith('_mixin.py'): 14 mixin_package = f.replace('.py', '') 15 mixin_class_name = ''.join([x.capitalize() for x in mixin_package.split('_')]) 16 #mixin_package = '.join'(['android', mixin_package, mixin_class_name]) 17 if mixin_package not in [m[0] for m in ANDROID_MIXIN_BASE_LIST]: 18 ANDROID_MIXIN_BASE_LIST.append([mixin_package, mixin_class_name]) 19 for dir in dirs: 20 if dir.isdigit(): 21 ANDROID_VERSION_LIST.append(int(dir)) 22 elif dir.replace('_','',1).isdigit() and dir.count('_') < 2: 23 ANDROID_VERSION_LIST.append(float(dir.replace('_', '.'))) 24 ANDROID_VERSION_LIST.sort(reverse=True) 25 26 class AndroidUiDevice(AndroidDevice): 27 """Android Device library for Android OS functions automation 28 29 This library is based on AndroidDevice library 30 31 Pre-requirements 32 install uiautomator2 on host. 33 34 Using Library 35 -------------- 36 37 Author: 38 tonglinx.ding@intel.com 39 shuang.zheng@intel.com 40 41 """ 42 def __init__(self, version=None, uart_handle=None, **kwargs): 43 ''' __init__ for android device Library. 44 45 ''' 46 self._version = version 47 self._d = None 48 super().__init__(version, uart_handle, **kwargs) 49 50 mixin_class_list = self._get_mixin_classes(self._version) 51 # log_info(mixin_class_list) 52 AndroidUiDevice.__bases__ = tuple(mixin_class_list) + (AndroidDevice, object,) 53 54 def init(self, serial=None, ip=None, port=':5555', log_path=None): 55 ''' connect androide device. 56 57 ''' 58 if log_path is not None: 59 self._log_path = log_path 60 self._ip = ip + port 61 self._serial = serial 62 self._adb = f'adb -s {self._ip if self._ip is not None else self._serial} ' 63 self.connect() 64 65 def is_connected(self): 66 rmsg = self._run_command(f'{self._adb}shell pwd', timeout=10) 67 if '/' in rmsg: 68 log_info(f'The device can be used normally!') 69 return True 70 else: 71 return False 72 73 def connect(self): 74 if self._ip is not None: 75 self._run_command('adb connect ' + self._ip) 76 time.sleep(2) 77 self._d = u2.connect(self._ip) 78 elif self._serial is not None: 79 self._d = u2.connect(self._serial) 80 else: 81 raise Exception('No device IP or serial number was received!') 82 return self.is_connected() 83 84 def disconnect(self): 85 if self._ip is not None: 86 self._run_command('adb disconnect ' + self._ip) 87 elif self._serial is not None: 88 pass 89 else: 90 raise Exception('No device IP or serial number was received!') 91 # return (not self.is_connected()) 92 return None 93 94 def rerun_uiautomator(func): 95 def wrapper(self, **kwargs): 96 try: 97 if self._d is None: 98 raise Exception('No u2 object was created!') 99 return func(self, **kwargs) 100 except Exception: 101 try: 102 os.system(f'python3 -m uiautomator2 init {self._serial if self._serial is not None else self._ip}') 103 except Exception: 104 os.system(f'python -m uiautomator2 init {self._serial if self._serial is not None else self._ip}') 105 self._d.uiautomator.stop() 106 log_info('Stopping uiautomator2!') 107 time.sleep(3) 108 self._d.uiautomator.start() 109 log_info('Starting uiautomator2!') 110 time.sleep(6) 111 try: 112 return func(self, **kwargs) 113 except Exception as e: 114 raise Exception(e) 115 return wrapper 116 117 @rerun_uiautomator 118 def scroll(self, **kwargs): 119 ''' 120 :param scroll_type: vertical or horizontal 121 :param direction: toBeginning or toEnd or forward or to or backward 122 :param index: The first few scrollbars 123 :param steps: The step size of each roll 124 :param for example resourceId='xxxx' multiple parameters can be passed 125 :type scroll_type: str 126 :type direction: str 127 :type index: int 128 :type steps: int, float 129 ''' 130 scroll_type = kwargs.pop('scroll_type', 'vertical') 131 direction = kwargs.pop('direction', 'toBeginning') 132 index = kwargs.pop('index', 0) 133 steps = kwargs.pop('steps', 20) 134 try: 135 scroll_obj = self._d(scrollable=True) 136 if not len(scroll_obj) or index > len(scroll_obj) - 1: 137 raise Exception("", "Scrollbar is missing or out of index, please check before calling") 138 log_info("===scrollbar count:{}====".format(len(scroll_obj))) 139 if scroll_type == "vertical": 140 if not direction or direction == "forward": 141 scroll_obj[index].scroll(steps=steps) 142 if direction == 'to': 143 if kwargs != {}: 144 scroll_obj[index].scroll.to(**kwargs) 145 log_info(f'Successfully vertical to {kwargs}') 146 else: 147 raise Exception('The specified location was not passed in!') 148 if direction == "backward": 149 scroll_obj[index].scroll.vert.backward() 150 log_info('Successfully vertical backward') 151 if direction == "toEnd": 152 scroll_obj[index].scroll.toEnd() 153 log_info('Successfully vertical toEnd') 154 if direction == "toBeginning": 155 scroll_obj[index].scroll.toBeginning() 156 log_info('Successfully vertical toBeginning') 157 elif scroll_type == "horizontal": 158 if not direction or direction == "forward": 159 scroll_obj[index].scroll.horiz.forward() 160 log_info('Successfully horizontal forward') 161 if direction == 'to': 162 if kwargs != {}: 163 scroll_obj[index].scroll.horiz.to(**kwargs) 164 log_info(f'Successfully horizontal to {kwargs}') 165 else: 166 raise Exception('The specified location was not passed in!') 167 if direction == "toEnd": 168 scroll_obj[index].scroll.horiz.toEnd() 169 log_info(f'Successfully horizontal toEnd') 170 if direction == "toBeginning": 171 scroll_obj[index].scroll.horiz.toBeginning() 172 log_info(f'Successfully horizontal toBeginning') 173 except Exception: 174 log_error(traceback.format_exc()) 175 raise 176 177 @console_log 178 def run_adb_command(self, func, return_output=True, timeout=30, **kwargs): 179 ''' 180 Please pay attention to the case of func yourself 181 ''' 182 if func in AndroidUiDevice().__dir__(): 183 if func == 'device_info': 184 out = eval(f'self.{func}()') 185 else: 186 out = eval(f'self.{func}(**kwargs)') 187 else: 188 out = super(AndroidUiDevice, self)._run_command(self._adb + func, timeout=timeout) 189 return out if return_output is True else None 190 191 @console_log 192 def poweroff(self): 193 out = self.run_adb_command("reboot -p") 194 return out 195 196 @rerun_uiautomator 197 def device_info(self): 198 return self._d.device_info 199 200 @rerun_uiautomator 201 def click(self, **kwargs): 202 ''' 203 If you're going to use xpath 204 :param xpath: xpath statement 205 :param timeout: Timeout period 206 If you want to use elements 207 :param for example resourceId='xxxx' multiple parameters can be passed 208 :param timeout: Wait for the timeout period 209 :param index: 210 If you want to use coordinates 211 :param x: The x-coordinate that needs to be clicked 212 :param y: The y-coordinate that needs to be clicked 213 :type xpath: str 214 :type timeout: int, float 215 :type x: int, float 216 :type y: int, float 217 ''' 218 timeout = kwargs.pop('timeout', 0.5) 219 if 'xpath' in kwargs: 220 try: 221 self._d.xpath(kwargs.get('xpath')).click(timeout=timeout) 222 log_info(f'Successfully click {kwargs.get("xpath")}') 223 except Exception: 224 raise Exception(f'Failed click {kwargs.get("xpath")}') 225 elif 'x' in kwargs and 'y' in kwargs: 226 try: 227 self._d.click(**kwargs) 228 log_info(f'Successfully Click Coordinates {kwargs.get("x")} x {kwargs.get("y")}') 229 except Exception: 230 raise Exception(f'The coordinates are not clicked {kwargs.get("x")} x {kwargs.get("y")}') 231 else: 232 if len(self._d(**kwargs)) == 0: 233 raise Exception(f'No elements found, {kwargs}') 234 self._d(**kwargs).click(timeout=timeout) 235 log_info(f'Successfully click {kwargs}') 236 237 @rerun_uiautomator 238 def long_click(self, **kwargs): 239 ''' 240 If you're going to use xpath 241 :param xpath: str 242 :param timeout: seconds wait element show up 243 :param duration: seconds of pressed 244 If you want to use elements 245 :param for example resourceId='xxxx' multiple parameters can be passed 246 :param timeout: seconds wait element show up 247 :param duration: seconds of pressed 248 If you want to use coordinates 249 :param x: The x-coordinate that needs to be clicked 250 :param y: The y-coordinate that needs to be clicked 251 :param duration: seconds of pressed 252 :type xpath: str 253 :type timeout: int, float 254 :type duration: int, float 255 :type x: int, float 256 :type y: int, float 257 ''' 258 duration = kwargs.pop('duration', 0.5) 259 timeout = kwargs.pop('timeout', 0.5) 260 if 'xpath' in kwargs: 261 try: 262 self._d.xpath(kwargs.get('xpath')).long_click(duration=duration, timeout=timeout) 263 log_info(f'Successfully long click {kwargs.get("xpath")}') 264 except Exception: 265 raise Exception(f'Failed long click {kwargs.get("xpath")}') 266 elif 'x' in kwargs and 'y' in kwargs: 267 try: 268 self._d.long_click(**kwargs, duration=duration) 269 log_info(f'Successfully long click coordinates {kwargs.get("x")} x {kwargs.get("y")}') 270 except Exception: 271 raise Exception(f'The coordinates are not long clicked {kwargs.get("x")} x {kwargs.get("y")}') 272 else: 273 if len(self._d(**kwargs)) == 0: 274 raise Exception(f'No elements found, {kwargs}') 275 self._d(**kwargs).long_click(duration=duration, timeout=timeout) 276 log_info(f'Successfully long click {kwargs}') 277 278 @rerun_uiautomator 279 def double_click(self, **kwargs): 280 ''' 281 :param x: The x-coordinate that needs to be clicked 282 :param y: The y-coordinate that needs to be clicked 283 :param duration: The interval between double-clicks 284 :type x: int, float 285 :type y: int, float 286 :type duration: int, float 287 ''' 288 duration = kwargs.pop('duration', 0.1) 289 try: 290 self._d.double_click(**kwargs, duration=duration) 291 log_info(f'Successfully double click {kwargs.get("x")} x {kwargs.get("y")}') 292 except Exception: 293 raise Exception(f'Failed double click {kwargs.get("x")} x {kwargs.get("y")}') 294 295 @rerun_uiautomator 296 def drag(self, **kwargs): 297 ''' 298 :param sx: original x-coordinates 299 :param sy: original y-coordinates 300 :param ex: destination x-coordinates 301 :param ey: destination y-coordinates 302 :param duration: The speed at which the drag 303 :type sx: int, float 304 :type sy: int, float 305 :type ex: int, float 306 :type ey: int, float 307 :type duration: int, float 308 ''' 309 duration = kwargs.pop('duration', 0.5) 310 try: 311 self._d.drag(**kwargs, duration=duration) 312 log_info(f'Successfully drag {kwargs.get("sx")} x {kwargs.get("sy")} to {kwargs.get("ex")} x {kwargs.get("ey")}') 313 except Exception: 314 raise Exception(f'failed drag {kwargs.get("sx")} x {kwargs.get("sy")} to {kwargs.get("ex")} x {kwargs.get("ey")}') 315 316 @rerun_uiautomator 317 def drag_to(self, **kwargs): 318 ''' 319 If you're going to use xpath 320 :param xpath: xpath statement 321 :param ex: destination x-coordinates 322 :param ey: destination y-coordinates 323 :param duration: The speed at which the drag 324 If you want to use elements 325 :param for example resourceId='xxxx' multiple parameters can be passed 326 :param ex: destination x-coordinates 327 :param ey: destination y-coordinates 328 :param duration: The speed at which the drag 329 :type xpath: str 330 :type ex: int, float 331 :type ey: int, float 332 :type duration: int, float 333 ''' 334 ex = kwargs.pop('ex') 335 ey = kwargs.pop('ey') 336 duration = kwargs.pop('duration', 0.5) 337 if 'xpath' in kwargs: 338 try: 339 self._d.xpath(**kwargs).drag_to(ex, ey, duration) 340 log_info(f'Successfully drag {kwargs} to {ex} x {ey}') 341 except Exception: 342 raise Exception(f'failed drag {kwargs} to {ex} x {ey}') 343 else: 344 if len(self._d(**kwargs)) == 0: 345 raise Exception(f'No elements found, {kwargs}') 346 self._d(**kwargs).drag_to(ex, ey, duration) 347 log_info(f'Successfully drag {kwargs} to {ex} x {ey}') 348 349 @rerun_uiautomator 350 def swipe(self, **kwargs): 351 ''' 352 If you're going to use xpath 353 :param xpath: xpath statement 354 :param direction: one of ("left", "right", "up", "down") 355 :param steps: move steps, one step is about 5ms 356 If you want to use elements 357 :param for example resourceId='xxxx' multiple parameters can be passed 358 :param direction: one of ("left", "right", "up", "down") 359 :param steps: move steps, one step is about 5ms 360 If you want to use coordinates 361 :param fx: original x-coordinates 362 :param fy: original y-coordinates 363 :param tx: destination x-coordinates 364 :param ty: destination y-coordinates 365 :param duration: duration 366 :param steps: 1 steps is about 5ms, if set, duration will be ignore 367 :type xpath: str 368 :type direction: str, float 369 :type steps: int, float 370 :type fx: int, float 371 :type fy: int, float 372 :type tx: int, float 373 :type ty: int, float 374 :type duration: int, float 375 ''' 376 direction = kwargs.pop('direction', None) 377 steps = kwargs.pop('steps', None) 378 duration = kwargs.pop('duration', None) 379 if 'xpath' in kwargs: 380 try: 381 self._d.xpath(kwargs.get('xpath')).swipe(direction=direction, steps=steps) 382 log_info(f'Successfully swipe {kwargs.get("xpath")} to {direction}') 383 except Exception: 384 raise Exception(f'Failed swipe {kwargs.get("xpath")}') 385 elif 'fx' and 'fy' and 'tx' and 'ty' in kwargs: 386 try: 387 self._d.swipe(**kwargs, duration=duration, steps=steps) 388 log_info(f'Successfully swipe {kwargs.get("fx")} x {kwargs.get("fy")} to {kwargs.get("tx")} x {kwargs.get("ty")}') 389 except Exception: 390 raise Exception(f'Failed swipe {kwargs.get("fx")} x {kwargs.get("fy")} to {kwargs.get("tx")} x {kwargs.get("ty")}') 391 else: 392 if len(self._d(**kwargs)) == 0: 393 raise Exception(f'No elements found, {kwargs}') 394 self._d(**kwargs).swipe(direction=direction, steps=steps) 395 log_info(f'Successfully swipe to {direction}') 396 397 @rerun_uiautomator 398 def screenshot(self, **kwargs): 399 ''' 400 :param filename: The name of the screenshot file 401 :param displayid: Screen ID 402 :param log_path: log file path 403 :type filename: str 404 :type displayid: str 405 :type log_path: str 406 ''' 407 self._take_screenshot(**kwargs) 408 409 @rerun_uiautomator 410 def press(self, **kwargs): 411 ''' 412 :param key: press key via name or key code. 413 :type key: int, str 414 ''' 415 try: 416 self._d.press(**kwargs) 417 log_info(f'Successfully press {kwargs}') 418 except Exception: 419 raise Exception(f'Failed press {kwargs}') 420 421 @rerun_uiautomator 422 def send_keys(self, **kwargs): 423 ''' 424 If you're going to use xpath: 425 :param text: the text entered 426 :param xpath: xpath statement 427 If you want to use elements 428 :param for example resourceId='xxxx' multiple parameters can be passed 429 :param text: the text entered 430 :type text: str 431 :type xpath: str 432 ''' 433 text = kwargs.pop('text', '') 434 if 'xpath' in kwargs: 435 self._d.xpath(kwargs.get('xpath')).send_keys(text=text) 436 if text == self.get_text(xpath=kwargs.get('xpath')): 437 log_info(f'Successfully to write text: {text} for {self._d.xpath(kwargs.get("xpath"))}') 438 else: 439 raise Exception(f'Failed to write text: {text} for {self._d.xpath(kwargs.get("xpath"))}') 440 else: 441 self._d(**kwargs).send_keys(text=text) 442 if text == self.get_text(**kwargs): 443 log_info(f'Successfully to write text: {text} for {kwargs}') 444 else: 445 raise Exception(f'Failed to write text: {text} for {kwargs}') 446 447 @rerun_uiautomator 448 def info(self): 449 return self._d.info 450 451 @rerun_uiautomator 452 def center(self, **kwargs): 453 ''' 454 If you're going to use xpath: 455 :param xpath: xpath statement 456 If you want to use elements 457 :param for example resourceId='xxxx' multiple parameters can be passed 458 :type xpath: str 459 ''' 460 if 'xpath' in kwargs: 461 try: 462 out = self._d.xpath(kwargs.get('xpath')).center() 463 log_info(f'The center point is {out}') 464 except: 465 raise Exception('The center was not found!') 466 else: 467 try: 468 out = self._d(**kwargs).center() 469 log_info(f'The center point is {out}') 470 except Exception: 471 raise Exception('The center was not found!') 472 return out 473 474 @rerun_uiautomator 475 def bounds(self, **kwargs): 476 ''' 477 If you're going to use xpath: 478 :param xpath: xpath statement 479 If you want to use elements 480 :param for example resourceId='xxxx' multiple parameters can be passed 481 :type xpath: str 482 ''' 483 if 'xpath' in kwargs: 484 try: 485 out = self._d.xpath(kwargs.get('xpath')).bounds() 486 log_info(f'The bounds point is {out}') 487 except: 488 raise Exception('The bounds was not found!') 489 else: 490 try: 491 out = self._d(**kwargs).bounds() 492 log_info(f'The bounds point is {out}') 493 except Exception: 494 raise Exception('The bounds was not found!') 495 return out 496 497 @rerun_uiautomator 498 def get_text(self, **kwargs): 499 ''' 500 If you're going to use xpath: 501 :param xpath: xpath statement 502 If you want to use elements 503 :param for example resourceId='xxxx' multiple parameters can be passed 504 :type xpath: str 505 ''' 506 if 'xpath' in kwargs: 507 try: 508 out = self._d.xpath(kwargs.get('xpath')).get_text() 509 log_info(f'The text is {out}') 510 except Exception: 511 raise Exception('Text not obtained') 512 else: 513 try: 514 out = self._d(**kwargs).get_text() 515 log_info(f'The text is {out}') 516 except Exception: 517 raise Exception('Text not obtained') 518 return out 519 520 @rerun_uiautomator 521 def wait_gone(self, **kwargs): 522 ''' 523 If you're going to use xpath: 524 :param xpath: xpath statement 525 :param timeout: wait for the element timeout 526 If you want to use elements 527 :param for example resourceId='xxxx' multiple parameters can be passed 528 :param timeout: wait for the element timeout 529 :type xpath: str 530 :type timeout: float, int 531 ''' 532 timeout = kwargs.pop('timeout', 3) 533 if 'xpath' in kwargs: 534 try: 535 self._d.xpath(kwargs.get('xpath')).wait_gone(timeout=timeout) 536 log_info(f'Successfully wait gone {self._d.xpath(kwargs.get("xpath"))}') 537 except Exception: 538 raise Exception(f'Failed wait gone {self._d.xpath(kwargs.get("xpath"))}') 539 else: 540 if len(self._d(**kwargs)) == 0: 541 raise Exception(f'No elements found, {kwargs}') 542 self._d(**kwargs).wait_gone(timeout=timeout) 543 log_info(f'Successfully wait gone {kwargs}') 544 545 @rerun_uiautomator 546 def wait(self, **kwargs): 547 ''' 548 If you're going to use xpath: 549 :param xpath: xpath statement 550 :param timeout: wait for the element timeout 551 If you want to use elements 552 :param for example resourceId='xxxx' multiple parameters can be passed 553 :param timeout: wait for the element timeout 554 :type xpath: str 555 :type timeout: float, int 556 ''' 557 timeout = kwargs.pop('timeout', 3) 558 if 'xpath' in kwargs: 559 try: 560 self._d.xpath(kwargs.get('xpath')).wait(timeout=timeout) 561 log_info(f'Successfully wait {self._d.xpath(kwargs.get("xpath"))}') 562 except Exception: 563 raise Exception(f'Failed wait {self._d.xpath(kwargs.get("xpath"))}') 564 else: 565 if len(self._d(**kwargs)) == 0: 566 raise Exception(f'No elements found, {kwargs}') 567 self._d(**kwargs).wait(timeout=timeout) 568 log_info(f'Successfully wait {kwargs}') 569 570 @rerun_uiautomator 571 def exists(self, **kwargs): 572 ''' 573 If you're going to use xpath: 574 :param xpath: xpath statement 575 :param timeout: wait for the element timeout 576 If you want to use elements 577 :param for example resourceId='xxxx' multiple parameters can be passed 578 :param timeout: wait for the element timeout 579 :type xpath: str 580 :type timeout: float, int 581 ''' 582 timeout = kwargs.pop('timeout', 3) 583 if 'xpath' in kwargs: 584 try: 585 self._d.xpath(kwargs.get('xpath')).exists(timeout=timeout) 586 log_info(f'Successfully exists {self._d.xpath(kwargs.get("xpath"))}') 587 except Exception: 588 raise Exception(f'Failed exists {self._d.xpath(kwargs.get("xpath"))}') 589 else: 590 if len(self._d(**kwargs)) == 0: 591 raise Exception(f'No elements found, {kwargs}') 592 self._d(**kwargs).exists(timeout=timeout) 593 log_info(f'Successfully exists {kwargs}') 594 595 @rerun_uiautomator 596 def app_start(self, **kwargs): 597 ''' 598 :param package_name: The name of the package that launches the app 599 :param activity: The name of the activity that launches the app 600 :param wait: wait until app started. default False 601 :param stop: Stop app before starting the activity. (require activity) 602 :param use_monkey: use monkey command to start app when activity is not given 603 :type package_name: str 604 :type activity: str 605 :type wait: bool 606 :type stop: bool 607 :type use_monkey: bool 608 ''' 609 self._d.app_start(**kwargs) 610 data = self.app_current() 611 if not kwargs.get('activity'): 612 if data.get('package') == kwargs.get('package_name'): 613 log_info(f'The app was successfully launched: {kwargs.get("package_name")}') 614 else: 615 raise Exception(f'The app was failed launched: {kwargs.get("package_name")}') 616 else: 617 if data.get('package') == kwargs.get('package_name') and data.get('activity') == kwargs.get('activity'): 618 log_info(f'The app was successfully launched: {kwargs.get("package_name") + "/" + kwargs.get("activity")}') 619 else: 620 raise Exception(f'The app was failed launched: {kwargs.get("package_name") + "/" + kwargs.get("activity")}') 621 622 @rerun_uiautomator 623 def app_stop(self, **kwargs): 624 ''' 625 :param package_name: The name of the package that stops the app 626 :type package_name: str 627 ''' 628 self._d.app_stop(**kwargs) 629 data = self.app_current() 630 if data.get('package') != kwargs.get('package_name'): 631 log_info(f'The app: {kwargs.get("package_name")} was stopped successfully!') 632 else: 633 raise Exception(f'The app: {kwargs.get("package_name")} was stopped failed!') 634 635 @rerun_uiautomator 636 def app_current(self): 637 return self._d.app_current() 638 639 def clickBy(self, **kwargs): 640 self.click(**kwargs) 641 642 def pressBack(self): 643 self.press(key='back') 644 645 def pressHome(self): 646 self.press(key='home') 647 648 def pressEnter(self): 649 self.press(key='enter') 650 651 def pressDpadUp(self): 652 self.press(key='up') 653 654 def pressDpadDown(self): 655 self.press(key='down') 656 657 def pressDpadLeft(self): 658 self.press(key='left') 659 660 def pressDpadRight(self): 661 self.press(key='right') 662 663 def pressDpadCenter(self): 664 self.press(key='center') 665 666 def pressMenu(self): 667 self.press(key='menu') 668 669 def pressSearch(self): 670 self.press(key='search') 671 672 def longClickBy(self, **kwargs): 673 self.long_click(**kwargs) 674 675 def existBy(self, **kwargs): 676 self.exists(**kwargs) 677 678 def getTextBy(self, **kwargs): 679 self.get_text(**kwargs) 680 681 def _get_mixin_classes(self, version): 682 android_version = None 683 platform = None 684 if version: 685 if version.count('_') == 0: 686 android_version = self._version 687 elif version.count('_') == 1: 688 platform = self._version.split('_')[0] 689 android_version = self._versin.split('_')[1] 690 else: 691 raise Exception('unsupported android device version: {}, only support [PLATFORM]_[ANDROID VERSION]'.format(self._version)) 692 if android_version and android_version.isdigit(): 693 android_version = int(android_version) 694 elif android_version and (android_version.replace('.','',1).isdigit() and android_version.count('.') < 2): 695 android_version = float(android_version) 696 elif android_version: 697 raise Exception('unsupported android version: '+ self._version) 698 # log_info('android_version '+str(android_version)) 699 # log_info('platform '+ str(platform)) 700 post_mixin_package = None 701 702 def get_platform_mixin_cls(base_package, platform, module, class_name): 703 if platform: 704 m = '.'.join([base_package, platform, module]) 705 else: 706 m = '.'.join([base_package, module]) 707 # log_info(m) 708 try: 709 mod = importlib.import_module(m) 710 cls = getattr(mod, class_name) 711 return cls 712 except: 713 return None 714 715 mixin_class_list = [] 716 for mixin_info in ANDROID_MIXIN_BASE_LIST: 717 m = mixin_info[0] 718 c = mixin_info[1] 719 cls = None 720 if android_version: 721 start = 0 722 for i in range(len(ANDROID_VERSION_LIST)): 723 if android_version < ANDROID_VERSION_LIST[i]: 724 start = i + 1 725 else: 726 break 727 if start >= len(ANDROID_VERSION_LIST): 728 cls = get_platform_mixin_cls('android', platform, m, c) 729 else: 730 for i in range(start, len(ANDROID_VERSION_LIST)): 731 base_package = 'android.'+str(ANDROID_VERSION_LIST[i]).replace('.', '_') 732 cls = get_platform_mixin_cls(base_package, platform, m, c) 733 if cls: 734 break 735 else: 736 continue 737 else: 738 # log_info('no android version') 739 base_package = 'android' 740 cls = get_platform_mixin_cls(base_package, platform, m, c) 741 742 if not cls: 743 log_warn('failed to import mixin class: {} in {}'.format(c, 'LinuxDevice')) 744 else: 745 mixin_class_list.append(cls) 746 # log_info(cls) 747 748 return mixin_class_list
uiautomar2 的使用
参考:https://blog.csdn.net/Xsk215/article/details/131871938
1 import uiautomator2 as u2 2 3 d = u2.connect('10.239.81.47') 4 print(d.info) 5 d(resourceId="android:id/edit").send_keys('123') 6 x1,y2 = d.xpath('//*[@resource-id="com.android.systemui:id/grid_nav"]').center() 7 d.double_click(x1, y2) 8 d.xpath('//*[@resource-id="com.android.car.carlauncher:id/apps_grid"]/android.widget.LinearLayout[11]/android.widget.ImageView[1]').click() 9 d.xpath('//*[@resource-id="com.android.car.settings:id/top_level_recycler_view"]/androidx.recyclerview.widget.RecyclerView[1]/android.widget.RelativeLayout[13]/android.widget.LinearLayout[1]').click() 10 11 12 d(resourceId="android:id/title", text="About").click() 13 data = d(resourceId="android:id/title", text="IP address").get_text() 14 print(data) 15 data2 = d(resourceId="android:id/summary", text="10.239.81.47").get_text() 16 print(data2)
1. adb devices:查看当前连接的设备列表。
2. adb shell:进入模拟器或设备的shell 模式。可以在里面执行Linux 命令。
3. adb push < 源文件 > < 目标文件 > :将文件拷贝到模拟器或设备中。
4. adb pull < 源文件 > < 目标文件 > : 从模拟器或设备中将文件拷回电脑中。
5. adb install <apk 文件> 安装 apk 应用到设备上。
6. adb uninstall < 应用包名称 > :从设备中卸载应用。
7. adb logcat :查看 LogCat 的日志。
8. adb start-server:启动 Adb server 服务,用于与设备连接。
9. adb kill-server:终止 Adb server 服务。
10. adb reboot:重启模拟器或设备。
11. adb root:重新启动Adb 为Root 权限。
12. adb remount:重新挂载系统,允许写入更改到系统分区。
13. adb backup :备份设备数据。
14. adb restore:恢复之前备份的设备数据。
15. adb help:查看Adb 命令的使用说明。
16. adb shell pm list packages:列出当前正在运行的应用的包名
称。
17. adb shell pm path <应用包名称>:查看特定应用的包路径。
18. adb shell am start <应用包名称>:启动特定应用。
19. adb shell pm clear <应用包名称>:清除特定应用的缓存数据。
20. adb shell input text <文本>:向当前活动窗口输入文本