基于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")
相关文章
|
10天前
|
XML JSON 数据库
Python的标准库
Python的标准库
123 77
|
24天前
|
机器学习/深度学习 算法 数据挖掘
数据分析的 10 个最佳 Python 库
数据分析的 10 个最佳 Python 库
73 4
数据分析的 10 个最佳 Python 库
|
11天前
|
XML JSON 数据库
Python的标准库
Python的标准库
39 11
|
24天前
|
人工智能 API 开发工具
aisuite:吴恩达发布开源Python库,一个接口调用多个大模型
吴恩达发布的开源Python库aisuite,提供了一个统一的接口来调用多个大型语言模型(LLM)服务。支持包括OpenAI、Anthropic、Azure等在内的11个模型平台,简化了多模型管理和测试的工作,促进了人工智能技术的应用和发展。
96 1
aisuite:吴恩达发布开源Python库,一个接口调用多个大模型
|
11天前
|
数据可视化 Python
以下是一些常用的图表类型及其Python代码示例,使用Matplotlib和Seaborn库。
通过这些思维导图和分析说明表,您可以更直观地理解和选择适合的数据可视化图表类型,帮助更有效地展示和分析数据。
52 8
|
1月前
|
XML 存储 数据库
Python中的xmltodict库
xmltodict是Python中用于处理XML数据的强大库,可将XML数据与Python字典相互转换,适用于Web服务、配置文件读取及数据转换等场景。通过`parse`和`unparse`函数,轻松实现XML与字典间的转换,支持复杂结构和属性处理,并能有效管理错误。此外,还提供了实战案例,展示如何从XML配置文件中读取数据库连接信息并使用。
Python中的xmltodict库
|
1月前
|
存储 人工智能 搜索推荐
Memoripy:支持 AI 应用上下文感知的记忆管理 Python 库
Memoripy 是一个 Python 库,用于管理 AI 应用中的上下文感知记忆,支持短期和长期存储,兼容 OpenAI 和 Ollama API。
92 6
Memoripy:支持 AI 应用上下文感知的记忆管理 Python 库
|
19天前
|
安全 API 文件存储
Yagmail邮件发送库:如何用Python实现自动化邮件营销?
本文详细介绍了如何使用Yagmail库实现自动化邮件营销。Yagmail是一个简洁强大的Python库,能简化邮件发送流程,支持文本、HTML邮件及附件发送,适用于数字营销场景。文章涵盖了Yagmail的基本使用、高级功能、案例分析及最佳实践,帮助读者轻松上手。
29 4
|
1月前
|
数据采集 数据可视化 数据处理
Python数据科学:Pandas库入门与实践
Python数据科学:Pandas库入门与实践
|
27天前
|
测试技术 Python
Python中的异步编程与`asyncio`库
Python中的异步编程与`asyncio`库
下一篇
DataWorks