{"id":13461699,"url":"https://github.com/openatx/uiautomator2","last_synced_at":"2025-05-12T05:32:41.647Z","repository":{"id":37336421,"uuid":"103826539","full_name":"openatx/uiautomator2","owner":"openatx","description":"Android Uiautomator2 Python Wrapper","archived":false,"fork":false,"pushed_at":"2025-05-09T23:05:16.000Z","size":1666,"stargazers_count":7107,"open_issues_count":175,"forks_count":1461,"subscribers_count":189,"default_branch":"master","last_synced_at":"2025-05-12T02:45:42.158Z","etag":null,"topics":["python","test","uiautomator"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openatx.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-09-17T12:20:42.000Z","updated_at":"2025-05-11T09:44:13.000Z","dependencies_parsed_at":"2022-07-12T12:05:05.952Z","dependency_job_id":"352598e3-3bb3-47e7-96f5-6d11994156f6","html_url":"https://github.com/openatx/uiautomator2","commit_stats":{"total_commits":643,"total_committers":38,"mean_commits":16.92105263157895,"dds":"0.12130637636080865","last_synced_commit":"9282deab2eff4324edb476bcce6bf6b331af0266"},"previous_names":[],"tags_count":170,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openatx%2Fuiautomator2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openatx%2Fuiautomator2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openatx%2Fuiautomator2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openatx%2Fuiautomator2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openatx","download_url":"https://codeload.github.com/openatx/uiautomator2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253672733,"owners_count":21945482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["python","test","uiautomator"],"created_at":"2024-07-31T11:00:53.051Z","updated_at":"2025-05-12T05:32:41.594Z","avatar_url":"https://github.com/openatx.png","language":"Python","funding_links":[],"categories":["Python","自动化工具","自动化测试"],"sub_categories":["AppUI自动化"],"readme":"# uiautomator2\n\n[📖 阅读中文版](README_CN.md)\n\n[![PyPI](https://img.shields.io/pypi/v/uiautomator2.svg)](https://pypi.python.org/pypi/uiautomator2)\n![PyPI](https://img.shields.io/pypi/pyversions/uiautomator2.svg)\n[![codecov](https://codecov.io/gh/openatx/uiautomator2/graph/badge.svg?token=d0ZLkqorBu)](https://codecov.io/gh/openatx/uiautomator2)\n\nQQ group: **815453846**\nDiscord: \u003chttps://discord.gg/PbJhnZJKDd\u003e\n\n\u003e I haven't maintained this project for a while (maybe two years), but recently I needed to research Android native automation again for work. Of course, I also investigated Appium. Comparing the two, I found that the uiautomator2 project runs really fast, from detecting elements to clicking, all in milliseconds, and the code is relatively easy to understand. I never expected to have written such a magical project before. How can such a good project be left to gather dust? It needs to be properly maintained, and some garbage code needs to be cleaned up. So the project version has been upgraded from 2.x.x to 3.x.x.\n\nUsers still using version 2.x.x can first check [2to3](docs/2to3.md) to decide whether to upgrade to 3.x.x (I personally highly recommend upgrading).\n\nSince this is a major version upgrade from 2 to 3, many functions have been removed. First, the atx-agent has been removed, followed by a bunch of atx-agent related functions. Deprecated features like init have also been removed.\n\nVarious dependency library version numbers\n\n- [![PyPI](https://img.shields.io/pypi/v/uiautomator2.svg?label=uiautomator2)](https://pypi.python.org/pypi/uiautomator2)\n- [![PyPI](https://img.shields.io/pypi/v/adbutils.svg?label=adbutils)](https://github.com/openatx/adbutils)\n- [![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/openatx/android-uiautomator-server.svg?label=android-uiautomator-server)](https://github.com/openatx/android-uiautomator-server)\n- ~~[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/openatx/atx-agent.svg?label=atx-agent)](https://github.com/openatx/atx-agent)~~\n\n[UiAutomator](https://developer.android.com/training/testing/ui-automator.html) is a Java library provided by Google for Android automation testing, based on the Accessibility service. It is very powerful and can test third-party apps, obtain any control properties of any app on the screen, and perform any operation on them. However, it has two drawbacks: 1. Test scripts can only be written in Java. 2. Test scripts need to be packaged into jar or apk files and uploaded to the device to run.\n\nWe hope that the test logic can be written in Python and can control the phone while running on the computer. Here, we must thank Xiaocong He ([@xiaocong][]), who realized this idea (see [xiaocong/uiautomator](https://github.com/xiaocong/uiautomator)). The principle is to run an HTTP RPC service on the phone, open up the functions in uiautomator, and then encapsulate these HTTP interfaces into a Python library.\nSince the `xiaocong/uiautomator` library has not been updated for a long time, we directly forked a version. To make it easier to distinguish, we added a 2 at the end [openatx/uiautomator2](https://github.com/openatx/uiautomator2). I also forked a corresponding Android package source code [openatx/android-uiautomator-server](https://github.com/openatx/android-uiautomator-server).\n\nIn addition to fixing bugs in the original library, we have added many new features. The main parts are as follows:\n\n* ~~The device and the development machine can be connected via WiFi without a data cable (based on [atx-agent](https://github.com/openatx/atx-agent))~~\n* ~~Integrated [openstf/minicap](https://github.com/openstf/minicap) for real-time screen projection and real-time screenshots~~\n* ~~Integrated [openstf/minitouch](https://github.com/openstf/minitouch) for precise real-time device control~~\n* Fixed the frequent exit issue of [xiaocong/uiautomator](https://github.com/xiaocong/uiautomator)\n* Refactored and streamlined the code for easier maintenance\n* Implemented a device management platform (also supports iOS) [atxserver2](https://github.com/openatx/atxserver2) (Note: currently not well maintained)\n* Expanded the functionality of toast retrieval and display (requires manual enabling of ATX's floating window permission) Seems to have a bug and is unusable\n\n\u003e Here I need to clarify, because many people often ask, openatx/uiautomator2 does not support iOS testing. For iOS automation testing, you can switch to this library [openatx/facebook-wda](https://github.com/openatx/facebook-wda).\n\n\u003e PS: This library ~~\u003chttps://github.com/NeteaseGame/ATX\u003e~~ is no longer maintained, please switch as soon as possible.\n\nHere is a quick reference for those who are already familiar with it [QUICK REFERENCE GUIDE](QUICK_REFERENCE.md). Feel free to provide feedback.\n\n## Requirements\n- Android version 4.4+\n- Python 3.8+\n\n## QUICK START\nFirst, prepare an Android phone (not two) with `Developer Options` enabled, connect it to the computer, and make sure you can see the connected device by running `adb devices`.\n\nRun `pip3 install -U uiautomator2` to install uiautomator2.\n\nRun `python` in the command line to open the Python interactive window. Then enter the following commands into the window.\n\n```python\nimport uiautomator2 as u2\n\nd = u2.connect() # connect to device\nprint(d.info)\n```\n\nIf you see output similar to the following, you can officially start using this library. Since this library has many features, there is still a lot of content to cover later...\n\n```\n{'currentPackageName': 'net.oneplus.launcher', 'displayHeight': 1920, 'displayRotation': 0, 'displaySizeDpX': 411, 'displaySizeDpY': 731, 'displayWidth': 1080, 'productName': 'OnePlus5', 'screenOn': True, 'sdkInt': 27, 'naturalOrientation': True}\n```\n\nAdditionally, to maintain stability, you need to enable the floating window permission for the `yellow car`. Refer to the article [py-uiautomator2 keeps the service available for a long time through the floating window](https://zhuanlan.zhihu.com/p/688009468).\n\nIn general, it will succeed, but there may be unexpected situations. You can join the QQ group to report issues (group number at the top), and there are many experts in the group who can help you solve problems.\n\n## Sponsors\nThank you to all our sponsors! ✨🍰✨\n\n### Gold Sponsor\nEmpty\n\n# Article Recommended\nExcellent articles recommended (feel free to @ me in the QQ group for feedback)\n\n- [How to deploy uiautomator2 in termux](https://www.cnblogs.com/ze-yan/p/12242383.html) by `Chengdu - Tester who knows a little`\n\n## Related Projects\n- A library for interacting with Android via the adb protocol [adbutils](https://github.com/openatx/adbutils)\n- [uiauto.dev](https://uiauto.dev) for viewing UI hierarchy, similar to uiautomatorviewer (used to replace the previously written weditor), for viewing UI hierarchy\n- Device management platform, useful when there are many devices [atxserver2](https://github.com/openatx/atxserver2) (looking for project maintainers)\n- ~~[atx-agent](https://github.com/openatx/atx-agent) A resident program running on the device, developed in Go, used to keep related services on the device alive~~\n- ~~[weditor](https://github.com/openatx/weditor) Similar to uiautomatorviewer, a dedicated editor developed for this project (currently not maintained)~~\n\n**[Installation](#installation)**\n\n**[Connect to a device](#connect-to-a-device)**\n\n**[Command line](#command-line)**\n\n**[Global settings](#global-settings)**\n  - **[Debug HTTP requests](#debug-http-requests)**\n  - **[Implicit wait](#implicit-wait)**\n\n**[App management](#app-management)**\n  - **[Install an app](#install-an-app)**\n  - **[Launch an app](#launch-an-app)**\n  - **[Stop an app](#stop-an-app)**\n  - **[Stop all running apps](#stop-all-running-apps)**\n  - **[Push and pull files](#push-and-pull-files)**\n  - **[Other app operations](#other-app-operations)**\n\n**[UI automation](#basic-api-usages)**\n  - **[Shell commands](#shell-commands)**\n  - **[Session](#session)**\n  - **[Retrieve the device info](#retrieve-the-device-info)**\n  - **[Key Events](#key-events)**\n  - **[Gesture interaction with the device](#gesture-interaction-with-the-device)**\n  - **[Screen-related](#screen-related)**\n  - **[Selector](#selector)**\n  - **[Watcher](#watcher)**\n  - **[Global settings](#global-settings)**\n  - **[Input method](#input-method)**\n  - **[Toast](#toast)**\n  - **[XPath](#xpath)**\n  - **[Screenrecord](#screenrecord)**\n  - **[Image match](#image-match) Removed**\n\n\n**[Contributors](#contributors)**\n\n**[LICENSE](#license)**\n\n\n# Installation\n1. Install uiautomator2\n\n    ```bash\n    pip install -U uiautomator2\n    ```\n    \n    Test if the installation is successful `uiautomator2 --help`\n    \n2. UI Inspector\n\n    ```bash\n    pip install uiautodev\n    # Start\n    uiauto.dev\n    ```\n\n    Open the browser and go to https://uiauto.dev to view the current device's interface structure.\n\n    **uiauto.dev**\n\n    [uiauto.dev](https://github.com/codeskyblue/uiauto.dev) is a project independent of uiautomator2, used to view the layer structure. It is a refactored version of the old project [weditor](https://github.com/openatx/weditor). It may be charged in the future (the price will definitely be worth it) to support the continued maintenance of this project. If you are interested, you can join the group for discussion (including making requests) QQ group 536481989\n\n# Connect to a device\nUse serial number to connect to the device, e.g., `123456f` (seen from `adb devices`)\n\n```python\nimport uiautomator2 as u2\n\nd = u2.connect('123456f') # alias for u2.connect_usb('123456f')\nprint(d.info)\n```\n\nSerial can be passed through env-var `ANDROID_SERIAL`\n\n```python\n# export ANDROID_SERIAL=123456f\nd = u2.connect()\n```\n\n# Command line\n`$device_ip` represents the device's IP address\n\nTo specify a device, pass in `--serial` like `python3 -m uiautomator2 --serial bff1234 \u003cSubCommand\u003e`, where SubCommand is a subcommand (screenshot, current, etc.)\n\n\u003e 1.0.3 Added: `python3 -m uiautomator2` equals to `uiautomator2`\n\n- screenshot: Take a screenshot\n\n    ```bash\n    $ uiautomator2 screenshot screenshot.jpg\n    ```\n\n- current: Get the current package name and activity\n\n    ```bash\n    $ uiautomator2 current\n    {\n        \"package\": \"com.android.browser\",\n        \"activity\": \"com.uc.browser.InnerUCMobile\",\n        \"pid\": 28478\n    }\n    ```\n    \n- uninstall: Uninstall app\n\n    ```bash\n    $ uiautomator2 uninstall \u003cpackage-name\u003e # Uninstall a package\n    $ uiautomator2 uninstall \u003cpackage-name-1\u003e \u003cpackage-name-2\u003e # Uninstall multiple packages\n    $ uiautomator2 uninstall --all # Uninstall all\n    ```\n\n- stop: Stop app\n\n    ```bash\n    $ uiautomator2 stop com.example.app # Stop an app\n    $ uiautomator2 stop --all # Stop all apps\n    ```\n\n- doctor:\n\n    ```bash\n    $ uiautomator2 doctor\n    [I 2024-04-25 19:53:36,288 __main__:101 pid:15596] uiautomator2 is OK\n    ```\n    \n# API Documents\n\n### New command timeout (Removed)\nWhen python quits, the UiAutomation service also quits.\n\u003c!-- How long (in seconds) will wait for a new command from the client before assuming the client quit and ending the uiautomator service (Default 3 minutes)\n\nConfigure the maximum idle time for the accessibility service. It will automatically release after the timeout. The default is 3 minutes.\n\n```python\nd.set_new_command_timeout(300) # change to 5 minutes, unit seconds\n``` --\u003e\n\n### Debug HTTP requests\nPrint out the HTTP request information behind the code\n\n```python\n\u003e\u003e\u003e d.debug = True\n\u003e\u003e\u003e d.info\n12:32:47.182 $ curl -X POST -d '{\"jsonrpc\": \"2.0\", \"id\": \"b80d3a488580be1f3e9cb3e926175310\", \"method\": \"deviceInfo\", \"params\": {}}' 'http://127.0.0.1:54179/jsonrpc/0'\n12:32:47.225 Response \u003e\u003e\u003e\n{\"jsonrpc\":\"2.0\",\"id\":\"b80d3a488580be1f3e9cb3e926175310\",\"result\":{\"currentPackageName\":\"com.android.mms\",\"displayHeight\":1920,\"displayRotation\":0,\"displaySizeDpX\":360,\"displaySizeDpY\":640,\"displayWidth\":1080,\"productName\":\"odin\",\"screenOn\":true,\"sdkInt\":25,\"naturalOrientation\":true}}\n\u003c\u003c\u003c END\n```\n\n### Implicit wait\nSet the element search wait time (default 20s)\n\n```python\nd.implicitly_wait(10.0) # can also be modified by d.settings['wait_timeout'] = 10.0\nd(text=\"Settings\").click() # if the Settings button does not appear in 10s, UiObjectNotFoundError will be raised\n\nprint(\"wait timeout\", d.implicitly_wait()) # get default implicit wait\n```\n\nThis function will have an influence on `click`, `long_click`, `drag_to`, `get_text`, `set_text`, `clear_text`, etc.\n\n## App management\nThis part showcases how to perform app management\n\n### Install an app\nWe only support installing an APK from a URL\n\n```python\nd.app_install('http://some-domain.com/some.apk')\n```\n\n### Launch an app\n```python\n# The default method is to parse the mainActivity of the apk package through atx-agent, and then call am start -n $package/$activity to start\nd.app_start(\"com.example.hello_world\")\n\n# Use monkey -p com.example.hello_world -c android.intent.category.LAUNCHER 1 to start\n# This method has a side effect, it will automatically turn off the phone's rotation lock\nd.app_start(\"com.example.hello_world\", use_monkey=True) # start with package name\n\n# Start the application by specifying the main activity, equivalent to calling am start -n com.example.hello_world/.MainActivity\nd.app_start(\"com.example.hello_world\", \".MainActivity\")\n```\n\n### Stop an app\n```python\n# equivalent to `am force-stop`, thus you could lose data\nd.app_stop(\"com.example.hello_world\") \n# equivalent to `pm clear`\nd.app_clear('com.example.hello_world')\n```\n\n### Stop all running apps\n```python\n# stop all\nd.app_stop_all()\n# stop all apps except for com.examples.demo\nd.app_stop_all(excludes=['com.examples.demo'])\n```\n\n### Get app info\n```python\nd.app_info(\"com.examples.demo\")\n# expect output\n#{\n#    \"mainActivity\": \"com.github.uiautomator.MainActivity\",\n#    \"label\": \"ATX\",\n#    \"versionName\": \"1.1.7\",\n#    \"versionCode\": 1001007,\n#    \"size\":1760809\n#}\n\n# save app icon\nimg = d.app_icon(\"com.examples.demo\")\nimg.save(\"icon.png\")\n```\n\n### List all running apps\n```python\nd.app_list_running()\n# expect output\n# [\"com.xxxx.xxxx\", \"com.github.uiautomator\", \"xxxx\"]\n```\n\n### Wait until app running\n```python\npid = d.app_wait(\"com.example.android\") # wait for the app to run, return pid(int)\nif not pid:\n    print(\"com.example.android is not running\")\nelse:\n    print(\"com.example.android pid is %d\" % pid)\n\nd.app_wait(\"com.example.android\", front=True) # wait for the app to run in the foreground\nd.app_wait(\"com.example.android\", timeout=20.0) # maximum wait time 20s (default)\n```\n\n\u003e Added in version 1.2.0\n\n### Push and pull files\n* push a file to the device\n\n    ```python\n    # push to a folder\n    d.push(\"foo.txt\", \"/sdcard/\")\n    # push and rename\n    d.push(\"foo.txt\", \"/sdcard/bar.txt\")\n    # push fileobj\n    with open(\"foo.txt\", 'rb') as f:\n        d.push(f, \"/sdcard/\")\n    # push and change file access mode\n    d.push(\"foo.sh\", \"/data/local/tmp/\", mode=0o755)\n    ```\n\n* pull a file from the device\n\n    ```python\n    d.pull(\"/sdcard/tmp.txt\", \"tmp.txt\")\n\n    # FileNotFoundError will raise if the file is not found on the device\n    d.pull(\"/sdcard/some-file-not-exists.txt\", \"tmp.txt\")\n    ```\n\n### Other app operations\n\n```python\n# grant all the permissions\nd.app_auto_grant_permissions(\"io.appium.android.apis\")\n\n# open scheme\nd.open_url(\"appname://appnamehost\")\n# same as\n# adb shell am start -a android.intent.action.VIEW -d \"appname://appnamehost\"\n```\n\n## Basic API Usages\nThis part showcases how to perform common device operations:\n\n### Shell commands\n* Run a short-lived shell command with a timeout protection. (Default timeout 60s)\n\n    Note: timeout support requires `atx-agent \u003e=0.3.3`\n\n    `adb_shell` function is deprecated. Use `shell` instead.\n\n    Simple usage\n\n    ```python\n    output, exit_code = d.shell(\"pwd\", timeout=60) # timeout 60s (Default)\n    # output: \"/\\n\", exit_code: 0\n    # Similar to command: adb shell pwd\n\n    # Since `shell` function return type is `namedtuple(\"ShellResponse\", (\"output\", \"exit_code\"))`\n    # so we can do some tricks\n    output = d.shell(\"pwd\").output\n    exit_code = d.shell(\"pwd\").exit_code\n    ```\n\n    The first argument can be a list. for example\n\n    ```python\n    output, exit_code = d.shell([\"ls\", \"-l\"])\n    # output: \"/....\", exit_code: 0\n    ```\n\n   This returns a string for stdout merged with stderr.\n   If the command is a blocking command, `shell` will also block until the command is completed or the timeout kicks in. No partial output will be received during the execution of the command. This API is not suitable for long-running commands. The shell command given runs in a similar environment of `adb shell`, which has a Linux permission level of `adb` or `shell` (higher than an app permission).\n\n* Run a long-running shell command (Removed)\n\u003c!-- \n    add stream=True will return `requests.models.Response` object. More info see [requests stream](http://docs.python-requests.org/zh_CN/latest/user/quickstart.html#id5)\n\n    ```python\n    r = d.shell(\"logcat\", stream=True)\n    # r: requests.models.Response\n    deadline = time.time() + 10 # run maximum 10s\n    try:\n        for line in r.iter_lines(): # r.iter_lines(chunk_size=512, decode_unicode=None, delimiter=None)\n            if time.time() \u003e deadline:\n                break\n            print(\"Read:\", line.decode('utf-8'))\n    finally:\n        r.close() # this method must be called\n    ```\n\n    Command will be terminated when `r.close()` is called. --\u003e\n    \n### Session\nSession represents an app lifecycle. Can be used to start the app, detect app crash.\n\n* Launch and close app\n\n    ```python\n    sess = d.session(\"com.netease.cloudmusic\") # start NetEase Cloud Music\n    sess.close() # stop NetEase Cloud Music\n    sess.restart() # cold start NetEase Cloud Music\n    ```\n\n* Use python `with` to launch and close app\n\n    ```python\n    with d.session(\"com.netease.cloudmusic\") as sess:\n        sess(text=\"Play\").click()\n    ```\n\n* Attach to the running app\n\n    ```python\n    # launch app if not running, skip launch if already running\n    sess = d.session(\"com.netease.cloudmusic\", attach=True)\n    ```\n\n* Detect app crash\n\n    ```python\n    # When the app is still running\n    sess(text=\"Music\").click() # operation goes normal\n\n    # If the app crashes or quits\n    sess(text=\"Music\").click() # raise SessionBrokenError\n    # other function calls under session will raise SessionBrokenError too\n    ```\n\n    ```python\n    # check if the session is ok.\n    # Warning: function name may change in the future\n    sess.running() # True or False\n    ```\n\n\n### Retrieve the device info\n\nGet basic information\n\n```python\nd.info\n```\n\nBelow is a possible output:\n\n```\n{'currentPackageName': 'com.android.systemui',\n 'displayHeight': 1560,\n 'displayRotation': 0,\n 'displaySizeDpX': 360,\n 'displaySizeDpY': 780,\n 'displayWidth': 720,\n 'naturalOrientation': True,\n 'productName': 'ELE-AL00',\n 'screenOn': True,\n 'sdkInt': 29}\n```\n\nGet window size\n\n```python\nprint(d.window_size())\n# device upright output example: (1080, 1920)\n# device horizontal output example: (1920, 1080)\n```\n\nGet current app info. For some android devices, the output could be empty (see *Output example 3*)\n\n```python\nprint(d.app_current())\n# Output example 1: {'activity': '.Client', 'package': 'com.netease.example', 'pid': 23710}\n# Output example 2: {'activity': '.Client', 'package': 'com.netease.example'}\n# Output example 3: {'activity': None, 'package': None}\n```\n\nWait activity\n\n```python\nd.wait_activity(\".ApiDemos\", timeout=10) # default timeout 10.0 seconds\n# Output: true or false\n```\n\nGet device serial number\n\n```python\nprint(d.serial)\n# output example: 74aAEDR428Z9\n```\n\nGet WLAN IP\n\n```python\nprint(d.wlan_ip)\n# output example: 10.0.0.1 or None\n```\n\n\n~~Get detailed device info~~ `d.device_info`\n\ndevice_info\n\n```python\nprint(d.device_info)\n```\n\nBelow is a possible output:\n\n```\n{'arch': 'arm64-v8a',\n 'brand': 'google',\n 'model': 'sdk_gphone64_arm64',\n 'sdk': 34,\n 'serial': 'EMULATOR34X1X19X0',\n 'version': 14}\n```\n\n### Clipboard\nGet or set clipboard content\n\nSet clipboard content or get content\n\n* clipboard/set_clipboard\n\n    ```python\n    d.clipboard = 'hello-world'\n    # or\n    d.set_clipboard('hello-world', 'label')\n\n    ```\n\nGet clipboard content\n\n\u003e  get clipboard requires IME(com.github.uiautomator/.AdbKeyboard) call `d.set_input_ime()` before using it.\n\n    ```python\n    \n    # get clipboard content\n    print(d.clipboard)\n    ```\n\n### Key Events\n\n* Turn on/off screen\n\n    ```python\n    d.screen_on() # turn on the screen\n    d.screen_off() # turn off the screen\n    ```\n\n* Get current screen status\n\n    ```python\n    d.info.get('screenOn') # require Android \u003e= 4.4\n    ```\n\n* Press hard/soft key\n\n    ```python\n    d.press(\"home\") # press the home key, with key name\n    d.press(\"back\") # press the back key, with key name\n    d.press(0x07, 0x02) # press keycode 0x07('0') with META ALT(0x02)\n    ```\n\n* These key names are currently supported:\n\n    - home\n    - back\n    - left\n    - right\n    - up\n    - down\n    - center\n    - menu\n    - search\n    - enter\n    - delete (or del)\n    - recent (recent apps)\n    - volume_up\n    - volume_down\n    - volume_mute\n    - camera\n    - power\n\nYou can find all key code definitions at [Android KeyEvent](https://developer.android.com/reference/android/view/KeyEvent.html)\n\n* Unlock screen\n\n    ```python\n    d.unlock()\n    # This is equivalent to\n    # 1. press(\"power\")\n    # 2. swipe from left-bottom to right-top\n    ```\n\n### Gesture interaction with the device\n* Click on the screen\n\n    ```python\n    d.click(x, y)\n    ```\n\n* Double click\n\n    ```python\n    d.double_click(x, y)\n    d.double_click(x, y, 0.1) # default duration between two clicks is 0.1s\n    ```\n\n* Long click on the screen\n\n    ```python\n    d.long_click(x, y)\n    d.long_click(x, y, 0.5) # long click 0.5s (default)\n    ```\n\n* Swipe\n\n    ```python\n    d.swipe(sx, sy, ex, ey)\n    d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s (default)\n    ```\n\n* SwipeExt extended function\n\n    ```python\n    d.swipe_ext(\"right\") # swipe right, 4 options \"left\", \"right\", \"up\", \"down\"\n    d.swipe_ext(\"right\", scale=0.9) # default 0.9, swipe distance is 90% of the screen width\n    d.swipe_ext(\"right\", box=(0, 0, 100, 100)) # swipe in the area (0,0) -\u003e (100, 100)\n\n    # Practice found that when swiping up or down, starting from the midpoint has a higher success rate\n    d.swipe_ext(\"up\", scale=0.8) # The code will vkk\n\n    # You can also use Direction as a parameter\n    from uiautomator2 import Direction\n    \n    d.swipe_ext(Direction.FORWARD) # Page down, equivalent to d.swipe_ext(\"up\"), just easier to understand\n    d.swipe_ext(Direction.BACKWARD) # Page up\n    d.swipe_ext(Direction.HORIZ_FORWARD) # Page horizontally right\n    d.swipe_ext(Direction.HORIZ_BACKWARD) # Page horizontally left\n    ```\n\n* Drag\n\n    ```python\n    d.drag(sx, sy, ex, ey)\n    d.drag(sx, sy, ex, ey, 0.5) # swipe for 0.5s (default)\n\n* Swipe points\n\n    ```python\n    # swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2)\n    # time will speed 0.2s between two points\n    d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2))\n    ```\n\n    Mostly used for nine-grid unlock, get the relative coordinates of each point in advance (percentage is supported here),\n    For more detailed usage, refer to this post [Using u2 to achieve nine-grid pattern unlock](https://testerhome.com/topics/11034)\n\n* Touch and drag (Beta)\n\n    This interface belongs to a relatively low-level original interface, which feels imperfect, but it can be used. Note: this place does not support percentages\n\n    ```python\n    d.touch.down(10, 10) # Simulate press\n    time.sleep(.01) # Delay between down and move, control it yourself\n    d.touch.move(15, 15) # Simulate move\n    d.touch.up(10, 10) # Simulate release\n    ```\n\nNote: click, swipe, drag operations support percentage position values. Example:\n\n`d.long_click(0.5, 0.5)` means long click center of the screen\n\n### Screen-related\n* Retrieve/Set device orientation\n\n    The possible orientations:\n\n    -   `natural` or `n`\n    -   `left` or `l`\n    -   `right` or `r`\n    -   `upsidedown` or `u` (cannot be set)\n\n    ```python\n    # retrieve orientation. the output could be \"natural\" or \"left\" or \"right\" or \"upsidedown\"\n    orientation = d.orientation\n\n    # WARNING: not pass testing in my TT-M1\n    # set orientation and freeze rotation.\n    # notes: setting \"upsidedown\" requires Android\u003e=4.3.\n    d.set_orientation('l') # or \"left\"\n    d.set_orientation(\"l\") # or \"left\"\n    d.set_orientation(\"r\") # or \"right\"\n    d.set_orientation(\"n\") # or \"natural\"\n    ```\n\n* Freeze/Un-freeze rotation\n\n    ```python\n    # freeze rotation\n    d.freeze_rotation()\n    # un-freeze rotation\n    d.freeze_rotation(False)\n    ```\n\n* Take screenshot\n\n    ```python\n    # take screenshot and save to a file on the computer, require Android\u003e=4.2.\n    d.screenshot(\"home.jpg\")\n    \n    # get PIL.Image formatted images. Naturally, you need pillow installed first\n    image = d.screenshot() # default format=\"pillow\"\n    image.save(\"home.jpg\") # or home.png. Currently, only png and jpg are supported\n\n    # get opencv formatted images. Naturally, you need numpy and cv2 installed first\n    import cv2\n    image = d.screenshot(format='opencv')\n    cv2.imwrite('home.jpg', image)\n\n    # get raw jpeg data\n    imagebin = d.screenshot(format='raw')\n    open(\"some.jpg\", \"wb\").write(imagebin)\n    ```\n\n* Dump UI hierarchy\n\n    ```python\n    # get the UI hierarchy dump content\n    xml = d.dump_hierarchy()\n\n    # compressed=True: include not important nodes\n    # pretty: format xml\n    # max_depth: limit xml depth, default 50\n    xml = d.dump_hierarchy(compressed=False, pretty=False, max_depth=50)\n    ```\n\n* Open notification or quick settings\n\n    ```python\n    d.open_notification()\n    d.open_quick_settings()\n    ```\n\n### Selector\n\nSelector is a handy mechanism to identify a specific UI object in the current window.\n\n```python\n# Select the object with text 'Clock' and its className is 'android.widget.TextView'\nd(text='Clock', className='android.widget.TextView')\n```\n\nSelector supports below parameters. Refer to [UiSelector Java doc](http://developer.android.com/tools/help/uiautomator/UiSelector.html) for detailed information.\n\n*  `text`, `textContains`, `textMatches`, `textStartsWith`\n*  `className`, `classNameMatches`\n*  `description`, `descriptionContains`, `descriptionMatches`, `descriptionStartsWith`\n*  `checkable`, `checked`, `clickable`, `longClickable`\n*  `scrollable`, `enabled`,`focusable`, `focused`, `selected`\n*  `packageName`, `packageNameMatches`\n*  `resourceId`, `resourceIdMatches`\n*  `index`, `instance`\n\n#### Children and siblings\n\n* children\n\n  ```python\n  # get the children or grandchildren\n  d(className=\"android.widget.ListView\").child(text=\"Bluetooth\")\n  ```\n\n* siblings\n\n  ```python\n  # get siblings\n  d(text=\"Google\").sibling(className=\"android.widget.ImageView\")\n  ```\n\n* children by text or description or instance\n\n  ```python\n  # get the child matching the condition className=\"android.widget.LinearLayout\"\n  # and also its children or grandchildren with text \"Bluetooth\"\n  d(className=\"android.widget.ListView\", resourceId=\"android:id/list\") \\\n   .child_by_text(\"Bluetooth\", className=\"android.widget.LinearLayout\")\n\n  # get children by allowing scroll search\n  d(className=\"android.widget.ListView\", resourceId=\"android:id/list\") \\\n   .child_by_text(\n      \"Bluetooth\",\n      allow_scroll_search=True,\n      className=\"android.widget.LinearLayout\"\n    )\n  ```\n\n  - `child_by_description` is to find children whose grandchildren have\n      the specified description, other parameters being similar to `child_by_text`.\n\n  - `child_by_instance` is to find children with has a child UI element anywhere\n      within its sub hierarchy that is at the instance specified. It is performed\n      on visible views **without scrolling**.\n\n  See below links for detailed information:\n\n  -   [UiScrollable](http://developer.android.com/tools/help/uiautomator/UiScrollable.html), `getChildByDescription`, `getChildByText`, `getChildByInstance`\n  -   [UiCollection](http://developer.android.com/tools/help/uiautomator/UiCollection.html), `getChildByDescription`, `getChildByText`, `getChildByInstance`\n\n  Above methods support chained invoking, e.g. for below hierarchy\n\n  ```xml\n  \u003cnode index=\"0\" text=\"\" resource-id=\"android:id/list\" class=\"android.widget.ListView\" ...\u003e\n    \u003cnode index=\"0\" text=\"WIRELESS \u0026 NETWORKS\" resource-id=\"\" class=\"android.widget.TextView\" .../\u003e\n    \u003cnode index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.LinearLayout\" ...\u003e\n      \u003cnode index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" ...\u003e\n        \u003cnode index=\"0\" text=\"Wi‑Fi\" resource-id=\"android:id/title\" class=\"android.widget.TextView\" .../\u003e\n      \u003c/node\u003e\n      \u003cnode index=\"2\" text=\"ON\" resource-id=\"com.android.settings:id/switchWidget\" class=\"android.widget.Switch\" .../\u003e\n    \u003c/node\u003e\n    ...\n  \u003c/node\u003e\n  ```\n  ![settings](https://raw.github.com/xiaocong/uiautomator/master/docs/img/settings.png)\n\n  To click the switch widget right to the TextView 'Wi‑Fi', we need to select the switch widgets first. However, according to the UI hierarchy, more than one switch widget exists and has almost the same properties. Selecting by className will not work. Alternatively, the below selecting strategy would help:\n\n  ```python\n  d(className=\"android.widget.ListView\", resourceId=\"android:id/list\") \\\n    .child_by_text(\"Wi‑Fi\", className=\"android.widget.LinearLayout\") \\\n    .child(className=\"android.widget.Switch\") \\\n    .click()\n  ```\n\n* relative positioning\n\n  Also, we can use the relative positioning methods to get the view: `left`, `right`, `top`, `bottom`.\n\n  -   `d(A).left(B)`, selects B on the left side of A.\n  -   `d(A).right(B)`, selects B on the right side of A.\n  -   `d(A).up(B)`, selects B above A.\n  -   `d(A).down(B)`, selects B under A.\n\n  So for above cases, we can alternatively select it with:\n\n  ```python\n  ## select \"switch\" on the right side of \"Wi‑Fi\"\n  d(text=\"Wi‑Fi\").right(className=\"android.widget.Switch\").click()\n  ```\n\n* Multiple instances\n\n  Sometimes the screen may contain multiple views with the same properties, e.g., text, then you will\n  have to use the \"instance\" property in the selector to pick one of the qualifying instances, like below:\n\n  ```python\n  d(text=\"Add new\", instance=0)  # which means the first instance with text \"Add new\"\n  ```\n\n  In addition, uiautomator2 provides a list-like API (similar to jQuery):\n\n  ```python\n  # get the count of views with text \"Add new\" on the current screen\n  d(text=\"Add new\").count\n\n  # same as count property\n  len(d(text=\"Add new\"))\n\n  # get the instance via index\n  d(text=\"Add new\")[0]\n  d(text=\"Add new\")[1]\n  ...\n\n  # iterator\n  for view in d(text=\"Add new\"):\n      view.info  # ...\n  ```\n\n  **Notes**: when using selectors in a code block that walks through the result list, you must ensure that the UI elements on the screen\n  keep unchanged. Otherwise, when Element-Not-Found error could occur when iterating through the list.\n\n#### Get the selected UI object status and its information\n* Check if the specific UI object exists\n\n    ```python\n    d(text=\"Settings\").exists # True if exists, else False\n    d.exists(text=\"Settings\") # alias of above property.\n\n    # advanced usage\n    d(text=\"Settings\").exists(timeout=3) # wait for Settings to appear in 3s, same as .wait(3)\n    ```\n\n* Retrieve the info of the specific UI object\n\n    ```python\n    d(text=\"Settings\").info\n    ```\n\n    Below is a possible output:\n\n    ```\n    { u'contentDescription': u'',\n    u'checked': False,\n    u'scrollable': False,\n    u'text': u'Settings',\n    u'packageName': u'com.android.launcher',\n    u'selected': False,\n    u'enabled': True,\n    u'bounds': {u'top': 385,\n                u'right': 360,\n                u'bottom': 585,\n                u'left': 200},\n    u'className': u'android.widget.TextView',\n    u'focused': False,\n    u'focusable': True,\n    u'clickable': True,\n    u'chileCount': 0,\n    u'longClickable': True,\n    u'visibleBounds': {u'top': 385,\n                        u'right': 360,\n                        u'bottom': 585,\n                        u'left': 200},\n    u'checkable': False\n    }\n    ```\n\n* Get/Set/Clear text of an editable field (e.g., EditText widgets)\n\n    ```python\n    d(text=\"Settings\").get_text()  # get widget text\n    d(text=\"Settings\").set_text(\"My text...\")  # set the text\n    d(text=\"Settings\").clear_text()  # clear the text\n    ```\n\n* Get Widget center point\n\n    ```python\n    x, y = d(text=\"Settings\").center()\n    # x, y = d(text=\"Settings\").center(offset=(0, 0)) # left-top x, y\n    ```\n    \n* Take screenshot of widget\n\n    ```python\n    im = d(text=\"Settings\").screenshot()\n    im.save(\"settings.jpg\")\n    ```\n\n#### Perform the click action on the selected UI object\n* Perform click on the specific object\n\n    ```python\n    # click on the center of the specific UI object\n    d(text=\"Settings\").click()\n    \n    # wait for the element to appear for at most 10 seconds and then click\n    d(text=\"Settings\").click(timeout=10)\n    \n    # click with offset(x_offset, y_offset)\n    # click_x = x_offset * width + x_left_top\n    # click_y = y_offset * height + y_left_top\n    d(text=\"Settings\").click(offset=(0.5, 0.5)) # Default center\n    d(text=\"Settings\").click(offset=(0, 0)) # click left-top\n    d(text=\"Settings\").click(offset=(1, 1)) # click right-bottom\n\n    # click when exists in 10s, default timeout 0s\n    clicked = d(text='Skip').click_exists(timeout=10.0)\n    \n    # click until the element is gone, return bool\n    is_gone = d(text=\"Skip\").click_gone(maxretry=10, interval=1.0) # maxretry default 10, interval default 1.0\n    ```\n\n* Perform long click on the specific UI object\n\n    ```python\n    # long click on the center of the specific UI object\n    d(text=\"Settings\").long_click()\n    ```\n\n#### Gesture actions for the specific UI object\n* Drag the UI object towards another point or another UI object \n\n    ```python\n    # notes: drag cannot be used for Android\u003c4.3.\n    # drag the UI object to a screen point (x, y), in 0.5 seconds\n    d(text=\"Settings\").drag_to(x, y, duration=0.5)\n    # drag the UI object to (the center position of) another UI object, in 0.25 seconds\n    d(text=\"Settings\").drag_to(text=\"Clock\", duration=0.25)\n    ```\n\n* Swipe from the center of the UI object to its edge\n\n    Swipe supports 4 directions:\n\n    - left\n    - right\n    - top\n    - bottom\n\n    ```python\n    d(text=\"Settings\").swipe(\"right\")\n    d(text=\"Settings\").swipe(\"left\", steps=10)\n    d(text=\"Settings\").swipe(\"up\", steps=20) # 1 step is about 5ms, so 20 steps is about 0.1s\n    d(text=\"Settings\").swipe(\"down\", steps=20)\n    ```\n\n* Two-point gesture from one point to another\n\n  ```python\n  d(text=\"Settings\").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2))\n  ```\n\n* Two-point gesture on the specific UI object\n\n  Supports two gestures:\n  - `In`, from edge to center\n  - `Out`, from center to edge\n\n  ```python\n  # notes: pinch cannot be set until Android 4.3.\n  # from edge to center. here is \"In\" not \"in\"\n  d(text=\"Settings\").pinch_in(percent=100, steps=10)\n  # from center to edge\n  d(text=\"Settings\").pinch_out()\n  ```\n\n* Wait until the specific UI appears or disappears\n    \n    ```python\n    # wait until the UI object appears\n    d(text=\"Settings\").wait(timeout=3.0) # return bool\n    # wait until the UI object is gone\n    d(text=\"Settings\").wait_gone(timeout=1.0)\n    ```\n\n    The default timeout is 20s. see **global settings** for more details\n\n* Perform fling on the specific UI object (scrollable)\n\n  Possible properties:\n  - `horiz` or `vert`\n  - `forward` or `backward` or `toBeginning` or `toEnd`\n\n  ```python\n  # fling forward (default) vertically (default) \n  d(scrollable=True).fling()\n  # fling forward horizontally\n  d(scrollable=True).fling.horiz.forward()\n  # fling backward vertically\n  d(scrollable=True).fling.vert.backward()\n  # fling to beginning horizontally\n  d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)\n  # fling to end vertically\n  d(scrollable=True).fling.toEnd()\n  ```\n\n* Perform scroll on the specific UI object (scrollable)\n\n  Possible properties:\n  - `horiz` or `vert`\n  - `forward` or `backward` or `toBeginning` or `toEnd`, or `to`\n\n  ```python\n  # scroll forward (default) vertically (default)\n  d(scrollable=True).scroll(steps=10)\n  # scroll forward horizontally\n  d(scrollable=True).scroll.horiz.forward(steps=100)\n  # scroll backward vertically\n  d(scrollable=True).scroll.vert.backward()\n  # scroll to beginning horizontally\n  d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)\n  # scroll to end vertically\n  d(scrollable=True).scroll.toEnd()\n  # scroll forward vertically until specific UI object appears\n  d(scrollable=True).scroll.to(text=\"Security\")\n  ```\n\n### WatchContext\nCurrently, this watch_context is started with threading and checks every 2s.\nCurrently, only the click trigger operation is available.\n\n```python\nwith d.watch_context() as ctx:\n    # When both (Download Now or Update Now) and Cancel buttons appear, click Cancel\n    ctx.when(\"^Download Now|Update Now\").when(\"Cancel\").click() \n    ctx.when(\"Agree\").click()\n    ctx.when(\"OK\").click()\n    # The above three lines of code are executed immediately, without any waiting\n    \n    ctx.wait_stable() # Start popup monitoring and wait for the interface to stabilize (no popups within two popup check cycles means stable)\n\n    # Use the call function to trigger function callbacks\n    # call supports two parameters, d and el, regardless of parameter position, can be omitted, if passed, the variable name cannot be wrong\n    # eg: When an element matches Midsummer Night, click the back button\n    ctx.when(\"Midsummer Night\").call(lambda d: d.press(\"back\"))\n    ctx.when(\"OK\").call(lambda el: el.click())\n\n    # Other operations\n\n# For convenience, you can also use the default popup monitoring logic in the code\n# Below is the current built-in default logic, you can @ the group owner in the group to add new logic, or directly submit a PR\n    # when(\"Continue to use\").click()\n    # when(\"Move to control\").when(\"Cancel\").click()\n    # when(\"^Download Now|Update Now\").when(\"Cancel\").click()\n    # when(\"Agree\").click()\n    # when(\"^(OK|Confirm)\").click()\nwith d.watch_context(builtin=True) as ctx:\n    # Add on top of the existing logic\n    ctx.when(\"@tb:id/jview_view\").when('//*[@content-desc=\"Image\"]').click()\n\n    # Other script logic\n```\n\nAnother way to write it\n\n```python\nctx = d.watch_context()\nctx.when(\"Settings\").click()\nctx.wait_stable() # Wait for the interface to no longer have popups\n\nctx.close()\n```\n\n### Watcher\n**WatchContext is more recommended** The writing is more concise\n\n~~You can register [watchers](http://developer.android.com/tools/help/uiautomator/UiWatcher.html) to perform some actions when a selector does not find a match.~~\n\nBefore version 2.0.0, the [Watcher]((http://developer.android.com/tools/help/uiautomator/UiWatcher.html) method provided by the uiautomator-jar library was used, but in practice, it was found that once the uiautomator connection failed and restarted, all watcher configurations were lost, which is definitely unacceptable.\n\nSo currently, a method of running a thread in the background (depending on the threading library) is used, and then the hierarchy is dumped every once in a while. When an element is matched, the corresponding operation is performed.\n\nUsage example\n\nRegister monitoring\n\n```python\n# Common writing, register anonymous monitoring\nd.watcher.when(\"Install\").click()\n\n# Register monitoring named ANR, when ANR and Force Close appear, click Force Close\nd.watcher(\"ANR\").when(xpath=\"ANR\").when(\"Force Close\").click()\n\n# Other callback examples\nd.watcher.when(\"Grab red envelope\").press(\"back\")\nd.watcher.when(\"//*[@text = 'Out of memory']\").call(lambda d: d.shell('am force-stop com.im.qq'))\n\n# Callback description\ndef click_callback(d: u2.Device):\n    d.xpath(\"OK\").click() # Calling in the callback will not trigger the watcher again\n\nd.xpath(\"Continue\").click() # When using d.xpath to check elements, the watcher will be triggered (currently up to 5 times)\n\n# Start background monitoring\nd.watcher.start()\n```\n\nMonitoring operations\n\n```python\n# Remove ANR monitoring\nd.watcher.remove(\"ANR\")\n\n# Remove all monitoring\nd.watcher.remove()\n\n# Start background monitoring\nd.watcher.start()\nd.watcher.start(2.0) # Default monitoring interval 2.0s\n\n# Force run all monitoring\nd.watcher.run()\n\n# Stop monitoring\nd.watcher.stop()\n\n# Stop and remove all monitoring, commonly used for initialization\nd.watcher.reset()\n```\n\nIn addition, there are still many documents not written, it is recommended to directly look at the source code [watcher.py](uiautomator2/watcher.py)\n\n### Global settings\n\n```python\nu2.HTTP_TIMEOUT = 60 # Default value 60s, default HTTP request timeout\n```\n\nOther configurations are currently mostly concentrated in `d.settings`, and configurations may be added or removed based on future needs.\n\n```python\nprint(d.settings)\n{'operation_delay': (0, 0),\n 'operation_delay_methods': ['click', 'swipe'],\n 'wait_timeout': 20.0}\n\n# Configure delay before click 0.5s, delay after click 1s\nd.settings['operation_delay'] = (.5, 1)\n\n# Modify the methods for which the delay takes effect\n# Among them, double_click, long_click all correspond to click\nd.settings['operation_delay_methods'] = ['click', 'swipe', 'drag', 'press']\nd.settings['wait_timeout'] = 20.0 # Default control wait time (native operation, xpath plugin wait time)\n\nd.settings['max_depth'] = 50 # Default 50, limit the element level returned by dump_hierarchy\n```\n\nFor deprecated configurations with version upgrades, a Deprecated warning will be given, but no exception will be thrown.\n\n```bash\n\u003e\u003e\u003e d.settings['click_before_delay'] = 1  \n[W 200514 14:55:59 settings:72] d.settings[click_before_delay] deprecated: Use operation_delay instead\n```\n\n**uiautomator recovery method settings**\n\nCareful you may have noticed that there are actually two APKs installed on the phone, one visible in the foreground (yellow car). One package name is `com.github.uiautomator.test` in the background and invisible. These two apps are signed with the same certificate.\nThe invisible app is actually a test package that contains all the test code, and the core test service is also started through it.\nHowever, when running, the system needs the yellow car app to keep running (it can also run in the background). Once the yellow car app is killed, the background running test service will also be killed soon. Even if you do nothing, the app will be quickly reclaimed by the system when running in the background. (Here I hope experts can point out how to not rely on the yellow car app, it feels theoretically possible, but I don't know how to do it yet).\n\n~~There are two ways to keep the yellow car running in the background, one is to start the app and put it in the background (default). Another way is to start a background service through `am startservice`.~~\n\n~~You can adjust this behavior through `d.settings[\"uiautomator_runtest_app_background\"] = True`. True means starting the app, False means starting the service.~~\n\nUiAutomator timeout settings (hidden method)\n\n```python\n\u003e\u003e d.jsonrpc.getConfigurator() \n{'actionAcknowledgmentTimeout': 500,\n 'keyInjectionDelay': 0,\n 'scrollAcknowledgmentTimeout': 200,\n 'waitForIdleTimeout': 0,\n 'waitForSelectorTimeout': 0}\n\n\u003e\u003e d.jsonrpc.setConfigurator({\"waitForIdleTimeout\": 100})\n{'actionAcknowledgmentTimeout': 500,\n 'keyInjectionDelay': 0,\n 'scrollAcknowledgmentTimeout': 200,\n 'waitForIdleTimeout': 100,\n 'waitForSelectorTimeout': 0}\n```\n\nTo prevent the client program from responding timeout, `waitForIdleTimeout` and `waitForSelectorTimeout` are currently set to `0`\n\nRefs: [Google uiautomator Configurator](https://developer.android.com/reference/android/support/test/uiautomator/Configurator)\n\n### Input method\nThis method is usually used for input when the control is unknown.\n\n```python\n# Currently using the method of pasting from the clipboard\nd.send_keys(\"Hello123abcEFG\")\nd.send_keys(\"Hello123abcEFG\", clear=True)\n\nd.clear_text() # Clear all content in the input box\n\nd.send_action() # Automatically execute commands such as enter, search, etc. according to the needs of the input box, Added in version 3.1\n# You can also specify the input method action to send, eg: d.send_action(\"search\") supports go, search, send, next, done, previous\n```\n\n\n\n```python\nprint(d.current_ime()) # Get the current input method ID\n\n```\n\n\u003e More reference: [IME_ACTION_CODE](https://developer.android.com/reference/android/view/inputmethod/EditorInfo)\n\n### Toast\n```python\nprint(d.last_toast) # get last toast, if not toast return None\nd.clear_toast()\n```\n\n\u003e Fixed in version 3.2.0\n\n### XPath\nJava uiautomator does not support xpath by default, so this is an extended feature. The speed is not that fast.\n\nFor example: The content of one of the nodes\n\n```xml\n\u003candroid.widget.TextView\n  index=\"2\"\n  text=\"05:19\"\n  resource-id=\"com.netease.cloudmusic:id/qf\"\n  package=\"com.netease.cloudmusic\"\n  content-desc=\"\"\n  checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\"\n  scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" visible-to-user=\"true\"\n  bounds=\"[957,1602][1020,1636]\" /\u003e\n```\n\nxpath positioning and usage\n\nSome attribute names have been modified, pay attention\n\n```\ndescription -\u003e content-desc\nresourceId -\u003e resource-id\n```\n\nCommon usage\n\n```python\n# wait exists 10s\nd.xpath(\"//android.widget.TextView\").wait(10.0)\n# find and click\nd.xpath(\"//*[@content-desc='Share']\").click()\n# check exists\nif d.xpath(\"//android.widget.TextView[contains(@text, 'Se')]\").exists:\n    print(\"exists\")\n# get all text-view text, attrib and center point\nfor elem in d.xpath(\"//android.widget.TextView\").all():\n    print(\"Text:\", elem.text)\n    # Dictionary eg: \n    # {'index': '1', 'text': '999+', 'resource-id': 'com.netease.cloudmusic:id/qb', 'package': 'com.netease.cloudmusic', 'content-desc': '', 'checkable': 'false', 'checked': 'false', 'clickable': 'false', 'enabled': 'true', 'focusable': 'false', 'focused': 'false','scrollable': 'false', 'long-clickable': 'false', 'password': 'false', 'selected': 'false', 'visible-to-user': 'true', 'bounds': '[661,1444][718,1478]'}\n    print(\"Attrib:\", elem.attrib)\n    # Coordinate eg: (100, 200)\n    print(\"Position:\", elem.center())\n```\n\nClick to view [Other common XPath usage](XPATH.md)\n\n### Screenrecord (Deprecated)\nVideo recording (deprecated), use [scrcpy](https://github.com/Genymobile/scrcpy) instead\n\nHere, the screenrecord command built into the phone is not used, but the method of obtaining phone images and synthesizing videos is used, so some other dependencies need to be installed, such as imageio, imageio-ffmpeg, numpy, etc.\nBecause some dependencies are relatively large, it is recommended to install them using a mirror. Just run the following command.\n\n```bash\npip3 install -U \"uiautomator2[image]\" -i https://pypi.doubanio.com/simple\n```\n\nUsage\n\n```\nd.screenrecord('output.mp4')\n\ntime.sleep(10)\n# or do something else\n\nd.screenrecord.stop() # After stopping the recording, the output.mp4 file can be opened\n```\n\nWhen recording, you can also specify fps (currently 20), this value is lower than the speed of minicap outputting images, it feels very good, it is not recommended to modify it.\n\n# Enable uiautomator2 logger\n\n```python\nfrom uiautomator2 import enable_pretty_logging\nenable_pretty_logging()\n```\n\nOr\n\n```\nlogger = logging.getLogger(\"uiautomator2\")\n# setup logger\n```\n\n## Stop UiAutomator\nWhen the Python program exits, UiAutomation also exits.\nHowever, the service can also be stopped through the interface method\n\n```python\nd.stop_uiautomator()\n```\n\n## Differences between Google UiAutomator 2.0 and 1.x\nhttps://www.cnblogs.com/insist8089/p/6898181.html\n\n- New interfaces: UiObject2, Until, By, BySelector\n- Import method: In 2.0, the com.android.uiautomator.core.* import method is deprecated. Changed to android.support.test.uiautomator\n- Build system: Maven and/or Ant (1.x); Gradle (2.0)\n- The form of the generated test package: from zip/jar (1.x) to apk (2.0)\n- The difference in the way to run UiAutomator tests locally with adb commands:\n  adb shell uiautomator runtest UiTest.jar -c package.name.ClassName (1.x)\n  adb shell am instrument -e class com.example.app.MyTest \n  com.example.app.test/android.support.test.runner.AndroidJUnitRunner (2.0)\n- Can Android services and interfaces be used? 1.x~No; 2.0~Yes.\n- Log output? Use System.out.print output stream to echo to the execution end (1.x); Output to Logcat (2.0)\n- Execution? Test cases do not need to inherit any parent class, method names are not limited, use annotations for testing (2.0); Need to inherit UiAutomatorTestCase, test methods need to start with test (1.x) \n\n\n## Dependent projects\n- uiautomator jsonrpc server \u003chttps://github.com/openatx/android-uiautomator-server/\u003e\n- ~~uiautomator daemon \u003chttps://github.com/openatx/atx-agent\u003e~~\n\n# Contributors\n- codeskyblue ([@codeskyblue][])\n- Xiaocong He ([@xiaocong][])\n- Yuanyuan Zou ([@yuanyuan][])\n- Qian Jin ([@QianJin2013][])\n- Xu Jingjie ([@xiscoxu][])\n- Xia Mingyuan ([@mingyuan-xia][])\n- Artem Iglikov, Google Inc. ([@artikz][])\n\n[@codeskyblue]: https://github.com/codeskyblue\n[@xiaocong]: https://github.com/xiaocong\n[@yuanyuan]: https://github.com/yuanyuanzou\n[@QianJin2013]: https://github.com/QianJin2013\n[@xiscoxu]: https://github.com/xiscoxu\n[@mingyuan-xia]: https://github.com/mingyuan-xia\n[@artikz]: https://github.com/artikz\n\nOther [contributors](../../graphs/contributors)\n\n## Other excellent projects\n- https://github.com/atinfo/awesome-test-automation A collection of all excellent testing frameworks, all-encompassing\n- [google/mobly](https://github.com/google/mobly) Google's internal testing framework, although I don't quite understand it, it feels very useful\n- https://github.com/zhangzhao4444/Maxim Based on Uiautomator's monkey\n- http://www.sikulix.com/ An automated testing framework based on image recognition, very old\n- http://airtest.netease.com/ The predecessor of this project, later taken over and continued to be optimized by the NetEase Guangzhou team. It has a good IDE\n\nThe ranking is in order, welcome to add\n\n# LICENSE\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenatx%2Fuiautomator2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenatx%2Fuiautomator2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenatx%2Fuiautomator2/lists"}