Skip to content

Commit

Permalink
update toast api, move _archived to other directory
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Jul 11, 2024
1 parent cabc5ef commit 1af2d4c
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 81 deletions.
48 changes: 47 additions & 1 deletion QUICK_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ import uiautomator2 as u2
d = u2.connect("--serial-here--") # 只有一个设备也可以省略参数
d = u2.connect() # 一个设备时, read env-var ANDROID_SERIAL

# 信息获取
print(d.info)
print(d.device_info)
width, height = d.window_size()
print(d.wlan_ip)
print(d.serial)

## 截图
d.screenshot() # Pillow.Image.Image格式
d.screenshot().save("current_screen.jpg")

# 获取hierarchy
d.dump_hierarchy() # str

# 设置查找元素等待时间,单位秒
d.implicitly_wait(10)

d.app_current() # 获取前台应用 packageName, activity
d.app_start("io.appium.android.apis") # 启动应用
d.app_start("io.appium.android.apis", stop=True) # 启动应用前停止应用
Expand All @@ -18,29 +35,58 @@ app.click(10, 20) # 坐标点击

# 无session状态下操作
d.click(10, 20) # 坐标点击
d.long_click(10, 10)
d.double_click(10, 20)

d.swipe(10, 20, 80, 90) # 从(10, 20)滑动到(80, 90)
d.swipe_ext("right") # 整个屏幕右滑动
d.swipe_ext("right", scale=0.9) # 屏幕右滑,滑动距离为屏幕宽度的90%
d.drag(10, 10, 80, 80)

d.press("back") # 模拟点击返回键
d.press("home") # 模拟Home键
d.long_press("volume_up")

d.send_keys("hello world") # 模拟输入,需要光标已经在输入框中才可以
d.clear_text() # 清空输入框

d.screen_on() # wakeUp
d.screen_off() # sleep screen

print(d.orientation) # left|right|natural|upsidedown
d.orientation = 'natural'
d.freeze_rotation(True)

print(d.last_toast) # 获取显示的toast文本
d.clear_last_toast() # 重置一下

d.open_notification()
d.open_quick_settings()

d.open_url("https://www.baidu.com")
d.keyevent("HOME") # same as: input keyevent HOME

# 执行shell命令
output, exit_code = d.shell("ps -A", timeout=60) # 执行shell命令,获取输出和exitCode
output = d.shell("pwd").output # 这样也可以
exit_code = d.shell("pwd").exit_code # 这样也可以

# Selector操作
sel = d(text="Gmail")
sel.wait()
sel.click()

```

```python
# XPath操作
# 元素操作
d.xpath("立即开户").wait() # 等待元素,最长等10s(默认)
d.xpath("立即开户").wait(timeout=10) # 修改默认等待时间

# 常用配置
d.settings['wait_timeout'] = 20 # 控件查找默认等待时间(默认20s)

# xpath操作
d.xpath("立即开户").click() # 包含查找等待+点击操作,匹配text或者description等于立即开户的按钮
d.xpath("//*[@text='私人FM']/../android.widget.ImageView").click()

Expand Down
25 changes: 4 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1292,30 +1292,13 @@ print(d.current_ime()) # 获取当前输入法ID
> 更多参考: [IME_ACTION_CODE](https://developer.android.com/reference/android/view/inputmethod/EditorInfo)
### Toast (2.2版本之后有添加回来)
Show Toast (好像有点bug)
### Toast
```python
d.toast.show("Hello world")
d.toast.show("Hello world", 1.0) # show for 1.0s, default 1.0s
print(d.last_toast) # get last toast, if not toast return None
d.clear_last_toast()
```
Get Toast
```python
# [Args]
# 5.0: max wait timeout. Default 10.0
# 10.0: cache time. return cache toast if already toast already show up in recent 10 seconds. Default 10.0 (Maybe change in the furture)
# "default message": return if no toast finally get. Default None
d.toast.get_message(5.0, 10.0, "default message")
# common usage
assert "Short message" in d.toast.get_message(5.0, default="")
# clear cached toast
d.toast.reset()
# Now d.toast.get_message(0) is None
```
> Fixed in version 3.2.0
### XPath
Java uiautoamtor中默认是不支持xpath的,所以这里属于扩展的一个功能。速度不是这么的快。
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 9 additions & 1 deletion docs/2to3.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
- Remove function connect_adb_wifi(str) -> Device, 直接用connect就行了
- Remove function set_new_command_timeout(timeout: int), 用不着了
- Remove function open_identify(), 打开一个比较明显的界面,这个函数出了点毛病,先下掉了
- Remove function toast.show(text, duration), 用的不多而且稳定性不好

XPath (d.xpath) methods
- remove dump_hierarchy
Expand Down Expand Up @@ -179,4 +180,11 @@ print(d.device_info)

### current_ime
- 2.x return (ime_method_name, bool), e.g. ("com.github.uiautomator/.FastInputIME", True)
- 3.x return ime_method_name, e.g. "com.github.uiautomator/.FastInputIME"
- 3.x return ime_method_name, e.g. "com.github.uiautomator/.FastInputIME"

### toast
- 2.x d.toast.get_message(5.0, default="")
- 3.x d.last_toast (property)

- 2.x d.toast.reset()
- 3.x d.clear_last_toast()
27 changes: 27 additions & 0 deletions mobile_tests/test_toast.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# coding: utf-8
# author: codeskyblue

import time
import pytest
import uiautomator2 as u2


@pytest.fixture(scope='function')
def dev():
d = u2.connect_usb()
d.debug = True
package = 'com.example.u2testdemo'
d.app_start(package, stop=True)
yield d
# d.app_stop(package)


def test_toast(dev: u2.Device):
dev.clear_toast()
assert dev.last_toast is None

dev(text="Toast").click()
dev(text="Show Toast").click()
time.sleep(.1)
assert dev.last_toast == 'Button Clicked!'

115 changes: 61 additions & 54 deletions uiautomator2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import time
import warnings
from functools import cached_property
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional, Tuple, Union

import adbutils
from lxml import etree
Expand Down Expand Up @@ -199,7 +199,6 @@ def reset_uiautomator(self):
# https://developer.android.google.cn/training/monitoring-device-state/doze-standby
# 让uiautomator进程不进入doze模式
# help: dumpsys deviceidle help
self.shell("dumpsys deviceidle whitelist +com.github.uiautomator; dumpsys deviceidle whitelist +com.github.uiautomator.test")
self.stop_uiautomator()
self.start_uiautomator()

Expand Down Expand Up @@ -448,7 +447,7 @@ def swipe(self, fx, fy, tx, ty, duration: Optional[float] = None, steps: Optiona
with self._operation_delay("swipe"):
return self.jsonrpc.swipe(fx, fy, tx, ty, steps)

def swipe_points(self, points, duration: float = 0.5):
def swipe_points(self, points: List[Tuple[int, int]], duration: float = 0.5):
"""
Args:
points: is point array containg at least one point object. eg [[200, 300], [210, 320]]
Expand All @@ -463,7 +462,8 @@ def swipe_points(self, points, duration: float = 0.5):
x, y = rel2abs(p[0], p[1])
ppoints.append(x)
ppoints.append(y)
steps = int(duration * 200)
# Each step execution is throttled to 5ms per step. So for a 100 steps, the swipe will take about 1/ 2 second to complete
steps = int(duration / .005)

Check warning on line 466 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L466

Added line #L466 was not covered by tests
return self.jsonrpc.swipePoints(ppoints, steps)

def drag(self, sx, sy, ex, ey, duration=0.5):
Expand Down Expand Up @@ -512,7 +512,7 @@ def screen_off(self):
self.jsonrpc.sleep()

@property
def orientation(self):
def orientation(self) -> str:
'''
orienting the devie to left/right or natural.
left/l: rotation=90 , displayRotation=1
Expand All @@ -522,7 +522,8 @@ def orientation(self):
'''
return self.__orientation[self.info["displayRotation"]][1]

def set_orientation(self, value):
@orientation.setter
def orientation(self, value: str):
'''setter of orientation property.'''
for values in self.__orientation:
if value in values:
Expand All @@ -543,6 +544,13 @@ def last_traversed_text(self):
def clear_traversed_text(self):
'''clear the last traversed text.'''
self.jsonrpc.clearLastTraversedText()

@property
def last_toast(self) -> Optional[str]:
return self.jsonrpc.getLastToast()

Check warning on line 550 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L550

Added line #L550 was not covered by tests

def clear_toast(self):
self.jsonrpc.clearLastToast()

Check warning on line 553 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L553

Added line #L553 was not covered by tests

def open_notification(self):
return self.jsonrpc.openNotification()
Expand Down Expand Up @@ -598,49 +606,6 @@ def serial(self) -> str:
if self._serial:
return self._serial
return self.shell(['getprop', 'ro.serialno']).output.strip()

def show_float_window(self, show=True):
""" 显示悬浮窗,提高uiautomator运行的稳定性 """
arg = str(show).lower()
self.shell([
"am", "start", "-n", "com.github.uiautomator/.ToastActivity", "-e",
"showFloatWindow", arg
])

@property
def toast(self):
obj = self

class Toast(object):
def get_message(self,
wait_timeout=10,
cache_timeout=10,
default=None):
"""
Args:
wait_timeout: seconds of max wait time if toast now show right now
cache_timeout: return immediately if toast showed in recent $cache_timeout
default: default messsage to return when no toast show up
Returns:
None or toast message
"""
deadline = time.time() + wait_timeout
while 1:
message = obj.jsonrpc.getLastToast(cache_timeout * 1000)
if message:
return message
if time.time() > deadline:
return default
time.sleep(.5)

def reset(self):
return obj.jsonrpc.clearLastToast()

def show(self, text, duration=1.0):
return obj.jsonrpc.makeToast(text, duration * 1000)

return Toast()

def __call__(self, **kwargs):
return UiObject(self, Selector(**kwargs))
Expand Down Expand Up @@ -959,6 +924,16 @@ def click_post_delay(self):
def click_post_delay(self, v: Union[int, float]):
self.settings['post_delay'] = v

def unlock(self):
""" unlock screen with swipe from left-bottom to right-top """
if not self.info['screenOn']:
self.shell("input keyevent WAKEUP")
self.swipe(0.1, 0.9, 0.9, 0.1)

Check warning on line 931 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L930-L931

Added lines #L930 - L931 were not covered by tests

def show_float_window(self, show=True):
""" 显示悬浮窗,提高uiautomator运行的稳定性 """
print("show_float_window is deprecated, this is not needed anymore")

Check warning on line 935 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L935

Added line #L935 was not covered by tests

@deprecated(reason="use d.toast.show(text, duration) instead")
def make_toast(self, text, duration=1.0):
""" Show toast
Expand All @@ -967,13 +942,45 @@ def make_toast(self, text, duration=1.0):
duration (float): seconds of display
"""
return self.jsonrpc.makeToast(text, duration * 1000)

@property
def toast(self):
obj = self

Check warning on line 948 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L948

Added line #L948 was not covered by tests

def unlock(self):
""" unlock screen with swipe from left-bottom to right-top """
if not self.info['screenOn']:
self.shell("input keyevent WAKEUP")
self.swipe(0.1, 0.9, 0.9, 0.1)
class Toast(object):
def get_message(self,

Check warning on line 951 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L950-L951

Added lines #L950 - L951 were not covered by tests
wait_timeout=10,
cache_timeout=10,
default=None):
"""
Args:
wait_timeout: seconds of max wait time if toast now show right now
cache_timeout: depreacated
default: default messsage to return when no toast show up
Returns:
None or toast message
"""
deadline = time.time() + wait_timeout
while 1:
message = obj.jsonrpc.getLastToast()

Check warning on line 966 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L964-L966

Added lines #L964 - L966 were not covered by tests
if message:
return message

Check warning on line 968 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L968

Added line #L968 was not covered by tests
if time.time() > deadline:
return default
time.sleep(.5)

Check warning on line 971 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L970-L971

Added lines #L970 - L971 were not covered by tests

def reset(self):
return obj.jsonrpc.clearLastToast()

Check warning on line 974 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L973-L974

Added lines #L973 - L974 were not covered by tests

def show(self, text, duration=1.0):
return obj.jsonrpc.makeToast(text, duration * 1000)

Check warning on line 977 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L976-L977

Added lines #L976 - L977 were not covered by tests

return Toast()

Check warning on line 979 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L979

Added line #L979 was not covered by tests

def set_orientation(self, value: str):
'''setter of orientation property.'''
self.orientation = value

Check warning on line 983 in uiautomator2/__init__.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/__init__.py#L983

Added line #L983 was not covered by tests


class _PluginMixIn:
Expand Down
13 changes: 9 additions & 4 deletions uiautomator2/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"""

import atexit
import datetime
import threading
import time
import logging
Expand Down Expand Up @@ -91,7 +92,9 @@ def _http_request(dev: adbutils.AdbDevice, method: str, path: str, data: Dict[st
# so here use 127.0.0.1 instead of localhost
url = f"http://127.0.0.1:{lport}{path}"
if print_request:
fields = [time.strftime("%H:%M:%S"), f"$ curl -X {method}", url]
start_time = datetime.datetime.now()
current_time = start_time.strftime("%H:%M:%S.%f")[:-3]
fields = [current_time, f"$ curl -X {method}", url]

Check warning on line 97 in uiautomator2/core.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/core.py#L95-L97

Added lines #L95 - L97 were not covered by tests
if data:
fields.append(f"-d '{json.dumps(data)}'")
print(f"# http timeout={timeout}")
Expand All @@ -100,9 +103,11 @@ def _http_request(dev: adbutils.AdbDevice, method: str, path: str, data: Dict[st
r.raise_for_status()
response = HTTPResponse(r.content)
if print_request:
print(f"{time.strftime('%H:%M:%S')} Response >>>")
print(response.text)
print(f"<<< END")
end_time = datetime.datetime.now()
current_time = end_time.strftime("%H:%M:%S.%f")[:-3]
print(f"{current_time} Response >>>")
print(response.text.rstrip())
print(f"<<< END timed_used = %.3f\n" % (end_time - start_time).total_seconds())

Check warning on line 110 in uiautomator2/core.py

View check run for this annotation

Codecov / codecov/patch

uiautomator2/core.py#L106-L110

Added lines #L106 - L110 were not covered by tests
return response
except requests.RequestException as e:
raise HTTPError(f"HTTP request failed: {e}") from e
Expand Down

0 comments on commit 1af2d4c

Please sign in to comment.