u2-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- u2_cli-0.1.0/.github/workflows/pypi.yml +52 -0
- u2_cli-0.1.0/.gitignore +207 -0
- u2_cli-0.1.0/CLAUDE.md +61 -0
- u2_cli-0.1.0/LICENSE +21 -0
- u2_cli-0.1.0/PKG-INFO +12 -0
- u2_cli-0.1.0/README.md +2 -0
- u2_cli-0.1.0/pyproject.toml +32 -0
- u2_cli-0.1.0/skills/u2-cli/SKILL.md +159 -0
- u2_cli-0.1.0/skills/u2-cli/references/commands.md +130 -0
- u2_cli-0.1.0/src/u2/__init__.py +57 -0
- u2_cli-0.1.0/src/u2/app.py +124 -0
- u2_cli-0.1.0/src/u2/device.py +136 -0
- u2_cli-0.1.0/src/u2/element.py +220 -0
- u2_cli-0.1.0/src/u2/interact.py +99 -0
- u2_cli-0.1.0/src/u2/screen.py +196 -0
- u2_cli-0.1.0/src/u2/watch.py +96 -0
- u2_cli-0.1.0/uv.lock +487 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# This workflow will upload a Python Package to PyPI when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: PyPI
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: [ 'v*.*.*' ]
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
release-build-and-publish-pypi:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
|
|
23
|
+
environment:
|
|
24
|
+
name: pypi
|
|
25
|
+
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v6
|
|
28
|
+
|
|
29
|
+
- name: Install uv
|
|
30
|
+
uses: astral-sh/setup-uv@v7
|
|
31
|
+
|
|
32
|
+
- name: "Set up Python"
|
|
33
|
+
uses: actions/setup-python@v6
|
|
34
|
+
with:
|
|
35
|
+
python-version-file: "pyproject.toml"
|
|
36
|
+
|
|
37
|
+
- name: Install the project
|
|
38
|
+
run: uv sync --all-extras --dev
|
|
39
|
+
|
|
40
|
+
- name: Build release distributions
|
|
41
|
+
run: uv build
|
|
42
|
+
|
|
43
|
+
- name: Upload distributions
|
|
44
|
+
uses: actions/upload-artifact@v7
|
|
45
|
+
with:
|
|
46
|
+
name: release-dists
|
|
47
|
+
path: dist/
|
|
48
|
+
|
|
49
|
+
- name: Publish PyPI
|
|
50
|
+
env:
|
|
51
|
+
UV_PUBLISH_TOKEN: ${{ secrets.UV_PUBLISH_TOKEN }}
|
|
52
|
+
run: uv publish
|
u2_cli-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
#uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
#poetry.lock
|
|
109
|
+
#poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
#pdm.lock
|
|
116
|
+
#pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
#pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# SageMath parsed files
|
|
135
|
+
*.sage.py
|
|
136
|
+
|
|
137
|
+
# Environments
|
|
138
|
+
.env
|
|
139
|
+
.envrc
|
|
140
|
+
.venv
|
|
141
|
+
env/
|
|
142
|
+
venv/
|
|
143
|
+
ENV/
|
|
144
|
+
env.bak/
|
|
145
|
+
venv.bak/
|
|
146
|
+
|
|
147
|
+
# Spyder project settings
|
|
148
|
+
.spyderproject
|
|
149
|
+
.spyproject
|
|
150
|
+
|
|
151
|
+
# Rope project settings
|
|
152
|
+
.ropeproject
|
|
153
|
+
|
|
154
|
+
# mkdocs documentation
|
|
155
|
+
/site
|
|
156
|
+
|
|
157
|
+
# mypy
|
|
158
|
+
.mypy_cache/
|
|
159
|
+
.dmypy.json
|
|
160
|
+
dmypy.json
|
|
161
|
+
|
|
162
|
+
# Pyre type checker
|
|
163
|
+
.pyre/
|
|
164
|
+
|
|
165
|
+
# pytype static type analyzer
|
|
166
|
+
.pytype/
|
|
167
|
+
|
|
168
|
+
# Cython debug symbols
|
|
169
|
+
cython_debug/
|
|
170
|
+
|
|
171
|
+
# PyCharm
|
|
172
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
173
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
174
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
175
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
176
|
+
#.idea/
|
|
177
|
+
|
|
178
|
+
# Abstra
|
|
179
|
+
# Abstra is an AI-powered process automation framework.
|
|
180
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
181
|
+
# Learn more at https://abstra.io/docs
|
|
182
|
+
.abstra/
|
|
183
|
+
|
|
184
|
+
# Visual Studio Code
|
|
185
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
186
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
188
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
189
|
+
# .vscode/
|
|
190
|
+
|
|
191
|
+
# Ruff stuff:
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
# PyPI configuration file
|
|
195
|
+
.pypirc
|
|
196
|
+
|
|
197
|
+
# Cursor
|
|
198
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
199
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
200
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
201
|
+
.cursorignore
|
|
202
|
+
.cursorindexingignore
|
|
203
|
+
|
|
204
|
+
# Marimo
|
|
205
|
+
marimo/_static/
|
|
206
|
+
marimo/_lsp/
|
|
207
|
+
__marimo__/
|
u2_cli-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`u2-cli` is a CLI tool wrapping [uiautomator2](https://github.com/openatx/uiautomator2) to control Android devices, designed for use by AI agents.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Install deps and activate env
|
|
13
|
+
uv sync
|
|
14
|
+
|
|
15
|
+
# Run CLI during development (no install needed)
|
|
16
|
+
uv run src/u2/__init__.py <cmd>
|
|
17
|
+
|
|
18
|
+
# Run after installing as a package
|
|
19
|
+
uv run u2 <cmd>
|
|
20
|
+
|
|
21
|
+
# Lint
|
|
22
|
+
uv run ruff check .
|
|
23
|
+
uv run ruff format .
|
|
24
|
+
|
|
25
|
+
# Build for PyPI
|
|
26
|
+
uv build
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
All CLI commands output JSON: `{"success": bool, "output": str, "code": str}` where `code` is the uiautomator2 Python expression that was executed, example: `d(text='Hello').click()`, `d.shell('ls')`.
|
|
32
|
+
|
|
33
|
+
The CLI is built with [Typer](https://typer.tiangolo.com/) and organized into sub-apps:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/u2/
|
|
37
|
+
├── __init__.py # Root app, State dataclass, global --serial/-s flag, main()
|
|
38
|
+
├── device.py # device sub-app: info, battery, wlan-ip, shell, keyevent, push, pull, screen-on, screen-off, orientation
|
|
39
|
+
├── app.py # app sub-app: start, stop, install, uninstall, clear, list, current, wait-activity
|
|
40
|
+
├── screen.py # screen sub-app: screenshot, dump, size, brightness
|
|
41
|
+
├── interact.py # interact sub-app: tap, long-tap, swipe, drag, type, clear
|
|
42
|
+
├── element.py # element sub-app: find, wait, exists, get-text, set-text, tap, long-tap (selector-based)
|
|
43
|
+
└── watch.py # watch sub-app: add, remove, list, run (uiautomator2 Watcher)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Sub-app responsibilities:**
|
|
47
|
+
|
|
48
|
+
- `device` — physical device state and low-level operations: device info, battery, IP, adb shell, key events (`home`, `back`, `power`, etc.), file push/pull, screen on/off, rotation
|
|
49
|
+
- `app` — app lifecycle: install/uninstall APK, start/stop by package, clear data, list installed apps, get current foreground app, wait for a target activity
|
|
50
|
+
- `screen` — visual state: screenshot (PNG to stdout or file), XML hierarchy dump, screen size, brightness
|
|
51
|
+
- `interact` — coordinate-based input: tap, long-tap, swipe (from/to coords), drag, send text to focused element, clear focused text
|
|
52
|
+
- `element` — selector-based element interaction: find elements by `text`, `resourceId`, `className`, `xpath`, `description`; check existence; get/set text; tap or long-tap matched elements
|
|
53
|
+
- `watch` — register/run UI watchers that auto-click or trigger actions when a matching element appears
|
|
54
|
+
|
|
55
|
+
**Global state:** The `--serial`/`-s` flag (Android device serial) is stored in a module-level `State` dataclass in `__init__.py`. Sub-command modules access it via `from u2 import state`.
|
|
56
|
+
|
|
57
|
+
**Entry point:** `u2 = "u2:main"` in `pyproject.toml`.
|
|
58
|
+
|
|
59
|
+
## Linting
|
|
60
|
+
|
|
61
|
+
Ruff is configured with `FBT` rules enabled — boolean positional arguments in function signatures trigger warnings. Typer-style boolean CLI options require `# noqa: FBT001/FBT002` suppressions.
|
u2_cli-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lanbao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
u2_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: u2-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI tool for executing uiautomator2 commands to control Android devices from the AI Agent
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: typer>=0.24.1
|
|
8
|
+
Requires-Dist: uiautomator2>=3.5.0
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
|
|
11
|
+
# u2-cli
|
|
12
|
+
CLI tool for executing uiautomator2 commands to control Android devices from the AI Agent
|
u2_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "u2-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "CLI tool for executing uiautomator2 commands to control Android devices from the AI Agent"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"typer>=0.24.1",
|
|
13
|
+
"uiautomator2>=3.5.0",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
u2 = "u2:main"
|
|
18
|
+
|
|
19
|
+
[tool.hatch.build.targets.wheel]
|
|
20
|
+
packages = ["src/u2"]
|
|
21
|
+
|
|
22
|
+
[tool.ruff]
|
|
23
|
+
line-length = 100
|
|
24
|
+
|
|
25
|
+
[tool.ruff.lint]
|
|
26
|
+
select = ["E", "F", "FBT"]
|
|
27
|
+
ignore = ["FBT003"]
|
|
28
|
+
|
|
29
|
+
[dependency-groups]
|
|
30
|
+
dev = [
|
|
31
|
+
"ruff>=0.15.6",
|
|
32
|
+
]
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: u2-cli
|
|
3
|
+
description: Use this skill when the user wants to control an Android device using the u2-cli tool. Triggers when: controlling an Android device via CLI, automating Android UI interactions, using uiautomator2 from the command line, finding/tapping/swiping UI elements on Android, taking screenshots or dumping UI hierarchy, managing Android apps, or running adb-based device operations through u2.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# u2-cli Skill
|
|
7
|
+
|
|
8
|
+
You are helping the user control an Android device using `u2-cli`, a CLI tool wrapping uiautomator2.
|
|
9
|
+
|
|
10
|
+
The complete command reference is in [references/commands.md](references/commands.md). Always consult it for exact syntax, flags, and defaults.
|
|
11
|
+
|
|
12
|
+
Install:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv pip install u2-cli
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Core Workflow
|
|
19
|
+
|
|
20
|
+
### 1. Orient yourself
|
|
21
|
+
|
|
22
|
+
Before interacting with the device, understand its current state:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
u2 screen dump --simplify # Get UI hierarchy as compact JSON
|
|
26
|
+
u2 screen screenshot /tmp/sc.png # Take a screenshot for visual context
|
|
27
|
+
u2 app current # See what app is in foreground
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `screen dump --simplify` as your primary tool for understanding what's on screen — it produces a compact JSON tree with pruned noise, shortened class names, and only the relevant attributes.
|
|
31
|
+
|
|
32
|
+
### 2. Find elements before acting on them
|
|
33
|
+
|
|
34
|
+
Verify elements exist before tapping or typing:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
u2 element exists --text "Login"
|
|
38
|
+
u2 element find --xpath "//android.widget.Button"
|
|
39
|
+
u2 element wait --resource-id com.example:id/submit --timeout 5
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3. Prefer `element` commands over coordinate `interact` commands
|
|
43
|
+
|
|
44
|
+
Use selector-based `element tap/long-tap` when you can identify elements by text, resource-id, or xpath. Fall back to `interact tap <x> <y>` only when no selector is available.
|
|
45
|
+
|
|
46
|
+
### 4. Parse output correctly
|
|
47
|
+
|
|
48
|
+
All commands return JSON:
|
|
49
|
+
```json
|
|
50
|
+
{"success": true, "output": "...", "code": "d(text='Login').click()"}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Check `success` before proceeding. The `output` field contains the actual data (parse as JSON if it's a list/dict). The `code` field shows what uiautomator2 expression ran.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Common Tasks
|
|
58
|
+
|
|
59
|
+
### Navigate the UI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
u2 device keyevent back # Press Back
|
|
63
|
+
u2 device keyevent home # Press Home
|
|
64
|
+
u2 interact swipe 540 1500 540 500 --duration 0.3 # Scroll up
|
|
65
|
+
u2 interact swipe 540 500 540 1500 --duration 0.3 # Scroll down
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Tap an element
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# By text
|
|
72
|
+
u2 element tap --text "Sign In"
|
|
73
|
+
|
|
74
|
+
# By resource ID
|
|
75
|
+
u2 element tap --resource-id com.example:id/btn_login
|
|
76
|
+
|
|
77
|
+
# By XPath
|
|
78
|
+
u2 element tap --xpath "//android.widget.Button[@text='OK']"
|
|
79
|
+
|
|
80
|
+
# By coordinates (fallback)
|
|
81
|
+
u2 interact tap 540 960
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Type text
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Tap the input field first, then type
|
|
88
|
+
u2 element tap --resource-id com.example:id/email_input
|
|
89
|
+
u2 interact type "user@example.com"
|
|
90
|
+
|
|
91
|
+
# Or set text directly on an element
|
|
92
|
+
u2 element set-text "user@example.com" --resource-id com.example:id/email_input
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Launch an app
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Start fresh (stop + start)
|
|
99
|
+
u2 app start com.example.app --stop --wait
|
|
100
|
+
|
|
101
|
+
# Wait for specific activity to load
|
|
102
|
+
u2 app wait-activity .MainActivity --timeout 15
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Handle popups automatically
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Block for 30s, auto-click any "Allow" button that appears
|
|
109
|
+
u2 watch add allow-popup --xpath "//android.widget.Button[@text='Allow']" --timeout 30
|
|
110
|
+
|
|
111
|
+
# One-shot: trigger immediately if a dialog is currently visible
|
|
112
|
+
u2 watch run --xpath "//android.widget.Button[@text='OK']"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Capture screen state
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Screenshot to file
|
|
119
|
+
u2 screen screenshot /tmp/screen.png
|
|
120
|
+
|
|
121
|
+
# Screenshot to stdout (pipe to viewer/tool)
|
|
122
|
+
u2 screen screenshot | display
|
|
123
|
+
|
|
124
|
+
# UI hierarchy for AI analysis
|
|
125
|
+
u2 screen dump --simplify
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Strategy for Autonomous Tasks
|
|
131
|
+
|
|
132
|
+
When given a multi-step task (e.g., "fill in this form", "open settings and enable X"):
|
|
133
|
+
|
|
134
|
+
1. **Dump the screen** with `screen dump --simplify` to understand current state
|
|
135
|
+
2. **Plan the steps** based on what elements are visible
|
|
136
|
+
3. **Act step by step**, checking success after each command
|
|
137
|
+
4. **Re-dump** after navigation or significant UI changes
|
|
138
|
+
5. **Use `element wait`** before acting when a screen transition is expected
|
|
139
|
+
|
|
140
|
+
If an element isn't found, try:
|
|
141
|
+
- `element find` with a broader selector to explore what's on screen
|
|
142
|
+
- `screen dump --simplify` to re-check the current UI state
|
|
143
|
+
- Scrolling with `interact swipe` and then retrying
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Device Selection
|
|
148
|
+
|
|
149
|
+
When multiple devices are connected, prefix all commands with `-s SERIAL`:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
u2 -s emulator-5554 screen dump --simplify
|
|
153
|
+
u2 -s R3CN10XXXXX element tap --text "Login"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Get available devices with:
|
|
157
|
+
```bash
|
|
158
|
+
adb devices
|
|
159
|
+
```
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# u2-cli Command Reference
|
|
2
|
+
|
|
3
|
+
## Global Options
|
|
4
|
+
|
|
5
|
+
Applies to every command:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
u2 [-s SERIAL] <sub-app> <command> [args]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
| Flag | Short | Type | Description |
|
|
12
|
+
|------|-------|------|-------------|
|
|
13
|
+
| `--serial` | `-s` | `str` | Android device serial (required when multiple devices connected) |
|
|
14
|
+
|
|
15
|
+
**Output format (all commands):**
|
|
16
|
+
```json
|
|
17
|
+
{"success": bool, "output": str, "code": str}
|
|
18
|
+
```
|
|
19
|
+
- `success` — whether the operation succeeded
|
|
20
|
+
- `output` — result data or error message
|
|
21
|
+
- `code` — the uiautomator2 Python expression that was executed
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## `device` sub-app
|
|
26
|
+
|
|
27
|
+
Physical device state and low-level operations.
|
|
28
|
+
|
|
29
|
+
| Command | Args | Description |
|
|
30
|
+
|---------|------|-------------|
|
|
31
|
+
| `device info` | — | Returns `d.device_info` dict |
|
|
32
|
+
| `device battery` | — | Returns battery info dict |
|
|
33
|
+
| `device wlan-ip` | — | Returns WLAN IP string |
|
|
34
|
+
| `device shell <cmd>` | `cmd: str` | Run adb shell command, returns stdout |
|
|
35
|
+
| `device keyevent <key>` | `key: str` | Send key event (e.g. `home`, `back`, `power`) |
|
|
36
|
+
| `device push <src> <dst>` | `src: str`, `dst: str` | Push local file to device |
|
|
37
|
+
| `device pull <src> <dst>` | `src: str`, `dst: str` | Pull file from device |
|
|
38
|
+
| `device screen-on` | — | Turn screen on |
|
|
39
|
+
| `device screen-off` | — | Turn screen off |
|
|
40
|
+
| `device orientation [value]` | `value?: natural\|left\|right\|upsidedown` | Get or set orientation |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## `app` sub-app
|
|
45
|
+
|
|
46
|
+
App lifecycle management.
|
|
47
|
+
|
|
48
|
+
| Command | Args / Flags | Description |
|
|
49
|
+
|---------|-------------|-------------|
|
|
50
|
+
| `app start <package>` | `--activity/-a str`, `--wait bool`, `--stop bool` | Launch app, optionally stopping first and waiting |
|
|
51
|
+
| `app stop <package>` | `package: str` | Stop app by package name |
|
|
52
|
+
| `app install <path>` | `path: str` | Install APK from path or URL |
|
|
53
|
+
| `app uninstall <package>` | `package: str` | Uninstall app |
|
|
54
|
+
| `app clear <package>` | `package: str` | Clear app data |
|
|
55
|
+
| `app list` | `--filter/-f str` (`-3` third-party, `-s` system, `-d` disabled, `-e` enabled) | List installed packages |
|
|
56
|
+
| `app current` | — | Returns current foreground app (package, activity, pid) |
|
|
57
|
+
| `app wait-activity <activity>` | `--timeout/-t float` (default: 10.0) | Wait for activity to appear; `success` reflects result |
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## `screen` sub-app
|
|
62
|
+
|
|
63
|
+
Visual state capture.
|
|
64
|
+
|
|
65
|
+
| Command | Args / Flags | Description |
|
|
66
|
+
|---------|-------------|-------------|
|
|
67
|
+
| `screen screenshot [path]` | `path?: str` | Save PNG to file (JSON output), or omit to write raw PNG bytes to stdout |
|
|
68
|
+
| `screen dump` | `--compressed bool`, `--pretty bool`, `--simplify/-s bool` | Dump UI hierarchy as XML or simplified JSON |
|
|
69
|
+
| `screen size` | — | Returns screen size as `WxH` string (e.g. `1080x2340`) |
|
|
70
|
+
| `screen brightness [value]` | `value?: int (0–255)` | Get or set screen brightness |
|
|
71
|
+
|
|
72
|
+
**`screen dump --simplify`** returns compact JSON tree with pruned/collapsed nodes:
|
|
73
|
+
- Keys: `type`, `text`, `desc`, `id`, `bounds`, plus boolean flags (`clickable`, `scrollable`, etc.) only when `true`, plus `children`
|
|
74
|
+
- Class names are shortened (e.g. `TextView` → `text`)
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## `interact` sub-app
|
|
79
|
+
|
|
80
|
+
Coordinate-based input actions.
|
|
81
|
+
|
|
82
|
+
| Command | Args / Flags | Description |
|
|
83
|
+
|---------|-------------|-------------|
|
|
84
|
+
| `interact tap <x> <y>` | `x: int`, `y: int` | Tap at coordinates |
|
|
85
|
+
| `interact long-tap <x> <y>` | `x: int`, `y: int`, `--duration/-d float` (default: 0.5) | Long press at coordinates |
|
|
86
|
+
| `interact swipe <fx> <fy> <tx> <ty>` | from/to coords, `--duration/-d float` (default: 0.5) | Swipe between two points |
|
|
87
|
+
| `interact drag <sx> <sy> <ex> <ey>` | start/end coords, `--duration/-d float` (default: 0.5) | Drag between two points |
|
|
88
|
+
| `interact type <text>` | `text: str` | Type text into focused element (clipboard paste, supports Unicode/Chinese) |
|
|
89
|
+
| `interact clear` | — | Clear text in focused element |
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## `element` sub-app
|
|
94
|
+
|
|
95
|
+
Selector-based element interaction. At least one selector required per command.
|
|
96
|
+
|
|
97
|
+
**Shared selector options:**
|
|
98
|
+
|
|
99
|
+
| Flag | Short | Description |
|
|
100
|
+
|------|-------|-------------|
|
|
101
|
+
| `--text` | `-t` | Element text |
|
|
102
|
+
| `--resource-id` | `-r` | Resource ID |
|
|
103
|
+
| `--class-name` | `-c` | Class name |
|
|
104
|
+
| `--xpath` | `-x` | XPath expression (uses `d.xpath(...)` path) |
|
|
105
|
+
| `--description` | `-d` | Content description |
|
|
106
|
+
|
|
107
|
+
Multiple non-xpath selectors can be combined. XPath takes a separate code path.
|
|
108
|
+
|
|
109
|
+
| Command | Extra Flags | Description |
|
|
110
|
+
|---------|------------|-------------|
|
|
111
|
+
| `element find` | `--timeout/-T float` (default: 0.0) | Find elements, returns JSON array of element info dicts |
|
|
112
|
+
| `element wait` | `--timeout/-T float` (default: 10.0) | Wait for element; `success` reflects result |
|
|
113
|
+
| `element exists` | — | `output` is `"true"` or `"false"` |
|
|
114
|
+
| `element get-text` | `--timeout/-T float` (default: 10.0) | Returns element's text in `output` |
|
|
115
|
+
| `element set-text <value>` | `value: str`, `--timeout/-T float` (default: 10.0) | Set element text |
|
|
116
|
+
| `element tap` | `--timeout/-T float` (default: 10.0) | Tap matched element |
|
|
117
|
+
| `element long-tap` | `--duration/-D float` (default: 0.5), `--timeout/-T float` (default: 10.0) | Long-tap matched element |
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## `watch` sub-app
|
|
122
|
+
|
|
123
|
+
UI watchers that auto-trigger actions when a matching element appears.
|
|
124
|
+
|
|
125
|
+
| Command | Args / Flags | Description |
|
|
126
|
+
|---------|-------------|-------------|
|
|
127
|
+
| `watch add <name>` | `--xpath/-x str` (required), `--action/-a str` (default: `click`), `--timeout/-t float` (default: 30.0) | Register watcher, start it, block for timeout, then stop. Actions: `click`, `back`, `home`, `recent` |
|
|
128
|
+
| `watch remove <name>` | `name: str` (use `__all__` to remove all) | Remove watcher (in-memory, same process only) |
|
|
129
|
+
| `watch list` | — | Returns whether watcher loop is running |
|
|
130
|
+
| `watch run` | `--xpath/-x str` (required), `--action/-a str` (default: `click`) | One-shot: register `__run__` watcher, fire once, remove it. `success` reflects whether it triggered |
|