基于python+uiautomator2,2020.12月最新库的使用方法,更新watcher使用方法(二)

简介: Basic API UsagesThis part showcases how to perform common device operations

Basic API Usages

This part showcases how to perform common device operations:


Shell commands

Run a short-lived shell command with a timeout protection. (Default timeout 60s)


Note: timeout support require atx-agent >=0.3.3


adb_shell function is deprecated. Use shell instead.


Simple usage

output, exit_code = d.shell("pwd", timeout=60) # timeout 60s (Default)
# output: "/\n", exit_code: 0
# Similar to command: adb shell pwd
# Since `shell` function return type is `namedtuple("ShellResponse", ("output", "exit_code"))`
# so we can do some tricks
output = d.shell("pwd").output
exit_code = d.shell("pwd").exit_code

The first argument can be list. for example

output, exit_code = d.shell(["ls", "-l"])
# output: "/....", exit_code: 0

This returns a string for stdout merged with stderr. 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).


  • Run a long-running shell command


add stream=True will return requests.models.Response object. More info see requests stream

r = d.shell("logcat", stream=True)
# r: requests.models.Response
deadline = time.time() + 10 # run maxium 10s
try:
    for line in r.iter_lines(): # r.iter_lines(chunk_size=512, decode_unicode=None, delimiter=None)
        if time.time() > deadline:
            break
        print("Read:", line.decode('utf-8'))
finally:
    r.close() # this method must be called

Command will be terminated when r.close() called.

Session

Session represent an app lifecycle. Can be used to start app, detect app crash.

  • Launch and close app
sess = d.session("com.netease.cloudmusic") # start 网易云音乐
sess.close() # 停止网易云音乐
sess.restart() # 冷启动网易云音乐
  • Use python with to launch and close app
with d.session("com.netease.cloudmusic") as sess:
    sess(text="Play").click()
  • Attach to the running app
# launch app if not running, skip launch if already running
sess = d.session("com.netease.cloudmusic", attach=True)
# raise SessionBrokenError if not running
sess = d.session("com.netease.cloudmusic", attach=True, strict=True)
  • Detect app crash
# When app is still running
sess(text="Music").click() # operation goes normal
# If app crash or quit
sess(text="Music").click() # raise SessionBrokenError
# other function calls under session will raise SessionBrokenError too
# check if session is ok.
# Warning: function name may change in the future
sess.running() # True or False

Retrieve the device info

Get basic information

d.info

Below is a possible output:

{ 
    u'displayRotation': 0,
    u'displaySizeDpY': 640,
    u'displaySizeDpX': 360,
    u'currentPackageName': u'com.android.launcher',
    u'productName': u'takju',
    u'displayWidth': 720,
    u'sdkInt': 18,
    u'displayHeight': 1184,
    u'naturalOrientation': True
}

Get window size

print(d.window_size())
# device upright output example: (1080, 1920)
# device horizontal output example: (1920, 1080)

Get current app info. For some android devices, the output could be empty (see Output example 3)

print(d.app_current())
# Output example 1: {'activity': '.Client', 'package': 'com.netease.example', 'pid': 23710}
# Output example 2: {'activity': '.Client', 'package': 'com.netease.example'}
# Output example 3: {'activity': None, 'package': None}

Wait activity

d.wait_activity(".ApiDemos", timeout=10) # default timeout 10.0 seconds
# Output: true of false

Get device serial number

print(d.serial)
# output example: 74aAEDR428Z9

Get WLAN ip

print(d.wlan_ip)
# output example: 10.0.0.1

Get detailed device info

print(d.device_info)

Below is a possible output:

{'udid': '3578298f-b4:0b:44:e6:1f:90-OD103',
 'version': '7.1.1',
 'serial': '3578298f',
 'brand': 'SMARTISAN',
 'model': 'OD103',
 'hwaddr': 'b4:0b:44:e6:1f:90',
 'port': 7912,
 'sdk': 25,
 'agentVersion': 'dev',
 'display': {'width': 1080, 'height': 1920},
 'battery': {'acPowered': False,
  'usbPowered': False,
  'wirelessPowered': False,
  'status': 3,
  'health': 0,
  'present': True,
  'level': 99,
  'scale': 100,
  'voltage': 4316,
  'temperature': 272,
  'technology': 'Li-ion'},
 'memory': {'total': 3690280, 'around': '4 GB'},
 'cpu': {'cores': 8, 'hardware': 'Qualcomm Technologies, Inc MSM8953Pro'},
 'presenceChangedAt': '0001-01-01T00:00:00Z',
 'usingBeganAt': '0001-01-01T00:00:00Z'}

Clipboard

Get of set clipboard content

设置粘贴板内容或获取内容 (目前已知问题是9.0之后的后台程序无法获取剪贴板的内容)

  • clipboard/set_clipboard
d.set_clipboard('text', 'label')
print(d.clipboard)

Key Events

  • Turn on/off screen
d.screen_on() # turn on the screen
d.screen_off() # turn off the screen
  • Get current screen status
d.info.get('screenOn') # require Android >= 4.4
  • Press hard/soft key
d.press("home") # press the home key, with key name
d.press("back") # press the back key, with key name
d.press(0x07, 0x02) # press keycode 0x07('0') with META ALT(0x02)

These key names are currently supported:


  • home
  • back
  • left
  • right
  • up
  • down
  • center
  • menu
  • search
  • enter
  • delete ( or del)
  • recent (recent apps)
  • volume_up
  • volume_down
  • volume_mute
  • camera
  • power

You can find all key code definitions at Android KeyEvnet


  • Unlock screen
d.unlock()
# This is equivalent to
# 1. launch activity: com.github.uiautomator.ACTION_IDENTIFY
# 2. press the "home" key

Gesture interaction with the device

  • Click on the screen
d.click(x, y)
  • Double click
d.double_click(x, y)
d.double_click(x, y, 0.1) # default duration between two click is 0.1s
  • Long click on the screen
d.long_click(x, y)
d.long_click(x, y, 0.5) # long click 0.5s (default)
  • Swipe
d.swipe(sx, sy, ex, ey)
d.swipe(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
  • SwipeExt 扩展功能
d.swipe_ext("right") # 手指右滑,4选1 "left", "right", "up", "down"
d.swipe_ext("right", scale=0.9) # 默认0.9, 滑动距离为屏幕宽度的90%
d.swipe_ext("right", box=(0, 0, 100, 100)) # 在 (0,0) -> (100, 100) 这个区域做滑动
# 实践发现上滑或下滑的时候,从中点开始滑动成功率会高一些
d.swipe_ext("up", scale=0.8) # 代码会vkk
# 还可以使用Direction作为参数
from uiautomator2 import Direction
d.swipe_ext(Direction.FORWARD) # 页面下翻, 等价于 d.swipe_ext("up"), 只是更好理解
d.swipe_ext(Direction.BACKWARD) # 页面上翻
d.swipe_ext(Direction.HORIZ_FORWARD) # 页面水平右翻
d.swipe_ext(Direction.HORIZ_BACKWARD) # 页面水平左翻
  • Drag
d.drag(sx, sy, ex, ey)
d.drag(sx, sy, ex, ey, 0.5) # swipe for 0.5s(default)
  • Swipe points
# swipe from point(x0, y0) to point(x1, y1) then to point(x2, y2)
# time will speed 0.2s bwtween two points
d.swipe_points([(x0, y0), (x1, y1), (x2, y2)], 0.2))

多用于九宫格解锁,提前获取到每个点的相对坐标(这里支持百分比), 更详细的使用参考这个帖子 使用u2实现九宫图案解锁

  • Touch and drap (Beta)

这个接口属于比较底层的原始接口,感觉并不完善,不过凑合能用。注:这个地方并不支持百分比

d.touch.down(10, 10) # 模拟按下
time.sleep(.01) # down 和 move 之间的延迟,自己控制
d.touch.move(15, 15) # 模拟移动
d.touch.up() # 模拟抬起

Note: click, swipe, drag operations support percentage position values. Example:


d.long_click(0.5, 0.5) means long click center of screen


Screen-related

  • Retrieve/Set device orientation


The possible orientations:


natural or n

left or l

right or r

upsidedown or u (can not be set)

# retrieve orientation. the output could be "natural" or "left" or "right" or "upsidedown"
orientation = d.orientation
# WARNING: not pass testing in my TT-M1
# set orientation and freeze rotation.
# notes: setting "upsidedown" requires Android>=4.3.
d.set_orientation('l') # or "left"
d.set_orientation("l") # or "left"
d.set_orientation("r") # or "right"
d.set_orientation("n") # or "natural"
  • Freeze/Un-freeze rotation
# freeze rotation
d.freeze_rotation()
# un-freeze rotation
d.freeze_rotation(False)
  • Take screenshot
# take screenshot and save to a file on the computer, require Android>=4.2.
d.screenshot("home.jpg")
# get PIL.Image formatted images. Naturally, you need pillow installed first
image = d.screenshot() # default format="pillow"
image.save("home.jpg") # or home.png. Currently, only png and jpg are supported
# get opencv formatted images. Naturally, you need numpy and cv2 installed first
import cv2
image = d.screenshot(format='opencv')
cv2.imwrite('home.jpg', image)
# get raw jpeg data
imagebin = d.screenshot(format='raw')
open("some.jpg", "wb").write(imagebin)
  • Dump UI hierarchy
# get the UI hierarchy dump content (unicoded).
xml = d.dump_hierarchy()
  • Open notification or quick settings
d.open_notification()
d.open_quick_settings()

Selector

Selector is a handy mechanism to identify a specific UI object in the current window.

# Select the object with text 'Clock' and its className is 'android.widget.TextView'
d(text='Clock', className='android.widget.TextView')

Selector supports below parameters. Refer to UiSelector Java doc for detailed information.

  • text, textContains, textMatches, textStartsWith
  • className, classNameMatches
  • description, descriptionContains, descriptionMatches, descriptionStartsWith
  • checkable, checked, clickable, longClickable
  • scrollable, enabled,focusable, focused, selected
  • packageName, packageNameMatches
  • resourceId, resourceIdMatches
  • index, instance

Children and siblings

  • children
# get the children or grandchildren
d(className="android.widget.ListView").child(text="Bluetooth")
  • siblings
# get siblings
d(text="Google").sibling(className="android.widget.ImageView")
  • children by text or description or instance
# get the child matching the condition className="android.widget.LinearLayout"
# and also its children or grandchildren with text "Bluetooth"
d(className="android.widget.ListView", resourceId="android:id/list") \
 .child_by_text("Bluetooth", className="android.widget.LinearLayout")
# get children by allowing scroll search
d(className="android.widget.ListView", resourceId="android:id/list") \
 .child_by_text(
    "Bluetooth",
    allow_scroll_search=True,
    className="android.widget.LinearLayout"
  )
  • child_by_description is to find children whose grandchildren have the specified description, other parameters being similar to child_by_text.
  • child_by_instance is to find children with has a child UI element anywhere within its sub hierarchy that is at the instance specified. It is performed on visible views without scrolling.

See below links for detailed information:


  • UiScrollable, getChildByDescription, getChildByText, getChildByInstance
  • UiCollection, getChildByDescription, getChildByText, getChildByInstance

Above methods support chained invoking, e.g. for below hierarchy

<node index="0" text="" resource-id="android:id/list" class="android.widget.ListView" ...>
  <node index="0" text="WIRELESS & NETWORKS" resource-id="" class="android.widget.TextView" .../>
  <node index="1" text="" resource-id="" class="android.widget.LinearLayout" ...>
    <node index="1" text="" resource-id="" class="android.widget.RelativeLayout" ...>
      <node index="0" text="Wi‑Fi" resource-id="android:id/title" class="android.widget.TextView" .../>
    </node>
    <node index="2" text="ON" resource-id="com.android.settings:id/switchWidget" class="android.widget.Switch" .../>
  </node>
  ...
</node>

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 widgets exist and have almost the same properties. Selecting by className will not work. Alternatively, the below selecting strategy would help:

d(className="android.widget.ListView", resourceId="android:id/list") \
  .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \
  .child(className="android.widget.Switch") \
  .click()
  • relative positioning


Also we can use the relative positioning methods to get the view: left, right, top, bottom.


  • d(A).left(B), selects B on the left side of A.
  • d(A).right(B), selects B on the right side of A.
  • d(A).up(B), selects B above A.
  • d(A).down(B), selects B under A.
  • So for above cases, we can alternatively select it with:
## select "switch" on the right side of "Wi‑Fi"
d(text="Wi‑Fi").right(className="android.widget.Switch").click()
  • Multiple instances


Sometimes the screen may contain multiple views with the same properties, e.g. text, then you will have to use the “instance” property in the selector to pick one of qualifying instances, like below:

d(text="Add new", instance=0)  # which means the first instance with text "Add new"

In addition, uiautomator2 provides a list-like API (similar to jQuery):

# get the count of views with text "Add new" on current screen
d(text="Add new").count
# same as count property
len(d(text="Add new"))
# get the instance via index
d(text="Add new")[0]
d(text="Add new")[1]
...
# iterator
for view in d(text="Add new"):
    view.info  # ...

Notes: when using selectors in a code block that walk through the result list, you must ensure that the UI elements on the screen keep unchanged. Otherwise, when Element-Not-Found error could occur when iterating through the list.


Get the selected ui object status and its information

Check if the specific UI object exists

d(text="Settings").exists # True if exists, else False
d.exists(text="Settings") # alias of above property.
# advanced usage
d(text="Settings").exists(timeout=3) # wait Settings appear in 3s, same as .wait(3)

Retrieve the info of the specific UI object

d(text="Settings").info

Below is a possible output:

{ u'contentDescription': u'',
u'checked': False,
u'scrollable': False,
u'text': u'Settings',
u'packageName': u'com.android.launcher',
u'selected': False,
u'enabled': True,
u'bounds': {u'top': 385,
            u'right': 360,
            u'bottom': 585,
            u'left': 200},
u'className': u'android.widget.TextView',
u'focused': False,
u'focusable': True,
u'clickable': True,
u'chileCount': 0,
u'longClickable': True,
u'visibleBounds': {u'top': 385,
                    u'right': 360,
                    u'bottom': 585,
                    u'left': 200},
u'checkable': False
}

Get/Set/Clear text of an editable field (e.g., EditText widgets)

d(text="Settings").get_text()  # get widget text
d(text="Settings").set_text("My text...")  # set the text
d(text="Settings").clear_text()  # clear the text

Get Widget center point

x, y = d(text="Settings").center()
# x, y = d(text="Settings").center(offset=(0, 0)) # left-top x, y

Take screenshot of widget

im = d(text="Settings").screenshot()
im.save("settings.jpg")

Perform the click action on the selected UI object

  • Perform click on the specific object
# click on the center of the specific ui object
d(text="Settings").click()
# wait element to appear for at most 10 seconds and then click
d(text="Settings").click(timeout=10)
# click with offset(x_offset, y_offset)
# click_x = x_offset * width + x_left_top
# click_y = y_offset * height + y_left_top
d(text="Settings").click(offset=(0.5, 0.5)) # Default center
d(text="Settings").click(offset=(0, 0)) # click left-top
d(text="Settings").click(offset=(1, 1)) # click right-bottom
# click when exists in 10s, default timeout 0s
clicked = d(text='Skip').click_exists(timeout=10.0)
# click until element gone, return bool
is_gone = d(text="Skip").click_gone(maxretry=10, interval=1.0) # maxretry default 10, interval default 1.0
  • Perform long click on the specific UI object
# long click on the center of the specific UI object
d(text="Settings").long_click()

Gesture actions for the specific UI object

  • Drag the UI object towards another point or another UI object
# notes : drag can not be used for Android<4.3.
# drag the UI object to a screen point (x, y), in 0.5 second
d(text="Settings").drag_to(x, y, duration=0.5)
# drag the UI object to (the center position of) another UI object, in 0.25 second
d(text="Settings").drag_to(text="Clock", duration=0.25)
  • Swipe from the center of the UI object to its edge

Swipe supports 4 directions:

  • left
  • right
  • top
  • bottom
d(text="Settings").swipe("right")
d(text="Settings").swipe("left", steps=10)
d(text="Settings").swipe("up", steps=20) # 1 steps is about 5ms, so 20 steps is about 0.1s
d(text="Settings").swipe("down", steps=20)
  • Two-point gesture from one point to another
d(text="Settings").gesture((sx1, sy1), (sx2, sy2), (ex1, ey1), (ex2, ey2))
  • Two-point gesture on the specific UI object

Supports two gestures:

  • In, from edge to center
  • Out, from center to edge
# notes : pinch can not be set until Android 4.3.
# from edge to center. here is "In" not "in"
d(text="Settings").pinch_in(percent=100, steps=10)
# from center to edge
d(text="Settings").pinch_out()

The default timeout is 20s. see global settings for more details


  • Perform fling on the specific ui object(scrollable)


Possible properties:


horiz or vert

forward or backward or toBeginning or toEnd

# fling forward(default) vertically(default) 
d(scrollable=True).fling()
# fling forward horizontally
d(scrollable=True).fling.horiz.forward()
# fling backward vertically
d(scrollable=True).fling.vert.backward()
# fling to beginning horizontally
d(scrollable=True).fling.horiz.toBeginning(max_swipes=1000)
# fling to end vertically
d(scrollable=True).fling.toEnd()
  • Perform scroll on the specific ui object(scrollable)

Possible properties:

  • horiz or vert
  • forward or backward or toBeginning or toEnd, or to
# scroll forward(default) vertically(default)
d(scrollable=True).scroll(steps=10)
# scroll forward horizontally
d(scrollable=True).scroll.horiz.forward(steps=100)
# scroll backward vertically
d(scrollable=True).scroll.vert.backward()
# scroll to beginning horizontally
d(scrollable=True).scroll.horiz.toBeginning(steps=100, max_swipes=1000)
# scroll to end vertically
d(scrollable=True).scroll.toEnd()
# scroll forward vertically until specific ui object appears
d(scrollable=True).scroll.to(text="Security")
相关文章
|
2天前
|
人工智能 搜索推荐 API
使用 Python holidays 库获取中国节日
使用 Python holidays 库获取中国节日
16 2
|
3天前
|
Shell Python
jabita-python库劫持提权-suid
jabita-python库劫持提权-suid
13 3
|
4天前
|
Python
pip批量安装Python库 requirement.txt 离线环境无互联网环境下pip安装Python库
pip批量安装Python库 requirement.txt 离线环境无互联网环境下pip安装Python库
23 3
|
5天前
|
Rust 监控 编译器
解密 Python 如何调用 Rust 编译生成的动态链接库(一)
解密 Python 如何调用 Rust 编译生成的动态链接库(一)
17 2
|
1天前
|
SQL Rust Go
Python通过C动态链接库调用C语言函数
Python通过C动态链接库调用C语言函数
|
1天前
|
Rust Go C语言
Python通过C动态链接库调用Go语言函数
Python通过C动态链接库调用Go语言函数
|
3天前
|
存储 程序员 数据库
【Python】标准库的使用
【Python】标准库的使用
15 0
|
3天前
|
Python
告别阻塞,拥抱未来!Python 异步编程 asyncio 库实战指南!
高效处理并发任务对提升程序性能至关重要,Python 的 `asyncio` 库提供了强大的异步编程支持。通过 `async/await` 关键字,可以在等待操作完成时不阻塞程序执行,显著提高效率和响应性。`asyncio` 支持定义异步函数、创建任务、等待多个任务完成等功能,并能结合第三方库如 `aiohttp` 实现异步网络请求。此外,它还支持异常处理,确保异步代码的健壮性。借助 `asyncio`,您可以轻松构建高性能、响应迅速的应用程序。
8 0
|
3天前
|
Shell 网络安全 数据安全/隐私保护
suuk-s.php.jpg-python 库劫持
suuk-s.php.jpg-python 库劫持
14 0
|
4天前
|
开发者 Python
Python 时间处理与时区转换:深入探究 datetime、time 模块与 pytz 库的功能与应用
Python 时间处理与时区转换:深入探究 datetime、time 模块与 pytz 库的功能与应用
8 0