turboadb 0.1.0__py3-none-any.whl
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.
- turboadb/README.md +453 -0
- turboadb/__init__.py +65 -0
- turboadb/__main__.py +6 -0
- turboadb/assets/icon.ico +0 -0
- turboadb/assets/icon.png +0 -0
- turboadb/bin/turboadb-gui.exe +0 -0
- turboadb/cli.py +505 -0
- turboadb/config.py +115 -0
- turboadb/core.py +1040 -0
- turboadb/devices.py +92 -0
- turboadb/exceptions.py +49 -0
- turboadb/gui/__init__.py +15 -0
- turboadb/gui/__main__.py +6 -0
- turboadb/gui/app.py +91 -0
- turboadb/gui/apps_panel.py +141 -0
- turboadb/gui/device_tab.py +264 -0
- turboadb/gui/file_browser.py +202 -0
- turboadb/gui/log_panel.py +61 -0
- turboadb/gui/logcat_view.py +169 -0
- turboadb/gui/main_window.py +403 -0
- turboadb/gui/session_dialog.py +96 -0
- turboadb/gui/sessions.py +51 -0
- turboadb/gui/settings.py +50 -0
- turboadb/gui/settings_dialog.py +109 -0
- turboadb/gui/terminal.py +108 -0
- turboadb/gui/theme.py +133 -0
- turboadb/results.py +153 -0
- turboadb/scrcpy.py +71 -0
- turboadb/tools.py +172 -0
- turboadb-0.1.0.dist-info/METADATA +477 -0
- turboadb-0.1.0.dist-info/RECORD +35 -0
- turboadb-0.1.0.dist-info/WHEEL +5 -0
- turboadb-0.1.0.dist-info/entry_points.txt +7 -0
- turboadb-0.1.0.dist-info/licenses/LICENSE +21 -0
- turboadb-0.1.0.dist-info/top_level.txt +1 -0
turboadb/README.md
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
# TurboADB
|
|
2
|
+
|
|
3
|
+
**An Android ADB + scrcpy device toolkit for automotive/embedded Android
|
|
4
|
+
(Android Automotive OS / IVI head units) and general Android work.**
|
|
5
|
+
|
|
6
|
+
TurboADB wraps Google's `adb` and `scrcpy` behind one robust, structured layer
|
|
7
|
+
you can drive three ways from a single `pip install turboadb`:
|
|
8
|
+
|
|
9
|
+
1. **Python API** — `import turboadb` for scripts and test frameworks.
|
|
10
|
+
2. **CLI** — `turboadb …`, fully argument-driven (argparse).
|
|
11
|
+
3. **Desktop GUI** — `turboadb-gui`, a full PyQt5 app shipped as a prebuilt
|
|
12
|
+
Windows `.exe` (PyQt5 baked in) so it runs even where PyQt5 has no wheel
|
|
13
|
+
(e.g. Windows ARM64) — **no PyQt5 install required**.
|
|
14
|
+
|
|
15
|
+
Every action returns a structured result (`CommandResult` / `TransferResult` /
|
|
16
|
+
`StreamResult`), with a **safe mode** that returns an `OperationResult` instead
|
|
17
|
+
of raising, and a typed exception hierarchy (`ADBError` base). All blocking work
|
|
18
|
+
can run on background threads, so it never blocks or crashes the caller.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Table of contents
|
|
23
|
+
|
|
24
|
+
- [Install](#install)
|
|
25
|
+
- [The GUI](#the-gui)
|
|
26
|
+
- [Quick start (API)](#quick-start-api)
|
|
27
|
+
- [Connecting — USB vs network (Wi-Fi / Ethernet)](#connecting--usb-vs-network-wifi--ethernet)
|
|
28
|
+
- [Running shell commands](#running-shell-commands)
|
|
29
|
+
- [Live logcat (continuous logs)](#live-logcat-continuous-logs)
|
|
30
|
+
- [File transfer (push / pull)](#file-transfer-push--pull)
|
|
31
|
+
- [App management](#app-management)
|
|
32
|
+
- [Media — screenshot & screen record](#media--screenshot--screen-record)
|
|
33
|
+
- [Port forwarding (forward / reverse)](#port-forwarding-forward--reverse)
|
|
34
|
+
- [scrcpy mirroring (the visual session)](#scrcpy-mirroring-the-visual-session)
|
|
35
|
+
- [Automotive (Android Automotive OS / IVI)](#automotive-android-automotive-os--ivi)
|
|
36
|
+
- [Result objects & error handling](#result-objects--error-handling)
|
|
37
|
+
- [CLI reference](#cli-reference)
|
|
38
|
+
- [API map](#api-map)
|
|
39
|
+
- [Building the GUI exe & releasing](#building-the-gui-exe--releasing)
|
|
40
|
+
- [License](#license)
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install turboadb
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That gives you the Python API, the `turboadb` CLI, and `turboadb-gui` (which
|
|
51
|
+
launches the bundled Windows exe; on other platforms install the GUI extra:
|
|
52
|
+
`pip install "turboadb[gui]"`).
|
|
53
|
+
|
|
54
|
+
TurboADB needs Google's **platform-tools (`adb`)** and, for mirroring,
|
|
55
|
+
**`scrcpy`**. It auto-detects them on your `PATH`, in the Android SDK, or in
|
|
56
|
+
common install locations. If they're missing it tells you exactly how to install
|
|
57
|
+
them — or set explicit paths:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
turboadb doctor # shows where adb / scrcpy were found (or how to get them)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- `adb`: <https://developer.android.com/tools/releases/platform-tools>
|
|
64
|
+
- `scrcpy`: <https://github.com/Genymobile/scrcpy>
|
|
65
|
+
(Windows: `winget install scrcpy` · macOS: `brew install scrcpy` · Linux: `apt install scrcpy`)
|
|
66
|
+
|
|
67
|
+
Override detection any time with `ADBConfig(adb_path=..., scrcpy_path=...)`,
|
|
68
|
+
the `TURBOADB_ADB` / `TURBOADB_SCRCPY` environment variables, or the GUI's
|
|
69
|
+
Settings dialog.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## The GUI
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
turboadb-gui # or: python -m turboadb.gui (from source)
|
|
77
|
+
turboadb-shortcut # make a Desktop shortcut (Windows)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
A tabbed, multi-device workspace:
|
|
81
|
+
|
|
82
|
+
- **Tabbed multi-device sessions** — each device opens its own tab; tabs are
|
|
83
|
+
closable, movable, and there's a **`+`** new-tab button. A **Split/tile** view
|
|
84
|
+
shows several devices at once.
|
|
85
|
+
- **Sidebar device manager** — saved targets **plus a LIVE `adb devices`** list
|
|
86
|
+
that auto-refreshes; a **Quick connect** filter box; right-click context menu
|
|
87
|
+
(New / Open / Edit / Duplicate / Delete). Double-click a live device to open it.
|
|
88
|
+
- **Ribbon toolbar** with colorful buttons: Device, Shell, Logcat, Files, Apps,
|
|
89
|
+
Scrcpy (mirror), Screenshot, Split, Settings, Help, Exit.
|
|
90
|
+
- **Per-device panels:** an interactive **Shell** terminal; a live **Logcat**
|
|
91
|
+
viewer (regex filter, level filter, pause/clear/save); a **Files** browser
|
|
92
|
+
(adb push/pull with file *and* folder pickers for both directions, progress
|
|
93
|
+
bar); an **Apps** manager (install via file dialog incl. split APKs, list,
|
|
94
|
+
uninstall, clear, start, stop); and a one-click **Mirror (scrcpy)** button.
|
|
95
|
+
- A dark, color-coded **log dock** at the bottom.
|
|
96
|
+
- **Settings** (persisted to `~/.turboadb/settings.json`, applied live):
|
|
97
|
+
dark/light theme, terminal font + size, default scrcpy options, adb/scrcpy
|
|
98
|
+
paths, logcat format. Defaults to a clean **black** dark theme; the light
|
|
99
|
+
theme isn't glaring.
|
|
100
|
+
- **Crash-proof:** a startup-failure native popup + crash log, a global
|
|
101
|
+
exception hook that logs and shows a non-fatal popup, and clean thread
|
|
102
|
+
shutdown when a tab closes.
|
|
103
|
+
|
|
104
|
+
The window/app icon is an automotive **speedometer** fused with the **Android
|
|
105
|
+
robot** and a terminal prompt.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Quick start (API)
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from turboadb import ADBHandler, ADBConfig
|
|
113
|
+
|
|
114
|
+
# USB: the only attached device (or pass serial="..." to pick one)
|
|
115
|
+
with ADBHandler() as dev:
|
|
116
|
+
print(dev.device_info().value if False else dev.shell("getprop ro.build.version.release").text)
|
|
117
|
+
dev.push("app.apk", "/data/local/tmp/app.apk")
|
|
118
|
+
dev.install("app.apk", grant_perms=True)
|
|
119
|
+
|
|
120
|
+
# live logs, with a regex match + tee to a file
|
|
121
|
+
dev.logcat(tag="ActivityManager", match=r"ANR|FATAL",
|
|
122
|
+
on_line=print, save_to="boot.log", stop_on_match=True)
|
|
123
|
+
|
|
124
|
+
dev.screenshot("shot.png")
|
|
125
|
+
dev.mirror(max_size=1280) # launch scrcpy
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The raw adb path is always available at `dev.adb_path`, and any adb command is a
|
|
129
|
+
call away: `dev.adb("shell", "wm", "size")`.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Connecting — USB vs network (Wi-Fi / Ethernet)
|
|
134
|
+
|
|
135
|
+
**USB (local):**
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from turboadb import ADBHandler, ADBConfig, list_devices
|
|
139
|
+
|
|
140
|
+
for d in list_devices():
|
|
141
|
+
print(d) # serial, state, model…
|
|
142
|
+
|
|
143
|
+
with ADBHandler(ADBConfig(serial="emulator-5554")) as dev:
|
|
144
|
+
...
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Network (remote head unit / IVI on the bench LAN):**
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# Enable TCP mode once while on USB, then connect wirelessly:
|
|
151
|
+
with ADBHandler() as usb:
|
|
152
|
+
usb.tcpip(5555)
|
|
153
|
+
|
|
154
|
+
with ADBHandler(ADBConfig(host="192.168.1.50", port=5555)) as hu:
|
|
155
|
+
print(hu.shell("getprop ro.build.characteristics").text)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Android 11+ wireless pairing:**
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
dev = ADBHandler(ADBConfig(host="192.168.1.50", port=5555))
|
|
162
|
+
dev.pair("192.168.1.50", 37123, "482913") # host:pairing_port + code
|
|
163
|
+
dev.connect()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`connect()` auto-runs `adb connect host:port` for network targets, waits for the
|
|
167
|
+
device, and verifies it's `device` (not `unauthorized`/`offline`) with a clear,
|
|
168
|
+
actionable error if not.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Running shell commands
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
res = dev.shell("pm list packages -3")
|
|
176
|
+
print(res.ok, res.exit_code, res.duration)
|
|
177
|
+
for line in res.lines:
|
|
178
|
+
print(line)
|
|
179
|
+
|
|
180
|
+
dev.shell("settings put global development_settings_enabled 1", check=True)
|
|
181
|
+
dev.shell("svc power stayon true", su=True) # wrap in su -c for rooted devices
|
|
182
|
+
|
|
183
|
+
# an interactive shell session (used by the GUI terminal, usable in scripts)
|
|
184
|
+
sh = dev.open_shell()
|
|
185
|
+
sh.send_line("top -n 1")
|
|
186
|
+
import time; time.sleep(1)
|
|
187
|
+
print(sh.read().decode(errors="replace"))
|
|
188
|
+
sh.close()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Live logcat (continuous logs)
|
|
194
|
+
|
|
195
|
+
Stream logcat **live, line by line**, cleanly formatted, with regex matching,
|
|
196
|
+
match callbacks, stop-on-match, and tee-to-file — plus tag/priority filters and
|
|
197
|
+
buffer selection.
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
# tag + minimum priority, collect ANR/FATAL, save everything, stop on first hit
|
|
201
|
+
res = dev.logcat(tag="ActivityManager", priority="I",
|
|
202
|
+
match=r"ANR|FATAL", on_match=lambda l: print("HIT:", l),
|
|
203
|
+
save_to="session.log", stop_on_match=True,
|
|
204
|
+
buffers=["main", "system", "crash"], clear_first=True)
|
|
205
|
+
print(res.lines, "lines,", len(res.matches), "matches")
|
|
206
|
+
|
|
207
|
+
# arbitrary streaming command + a stop event from another thread
|
|
208
|
+
import threading
|
|
209
|
+
stop = threading.Event()
|
|
210
|
+
dev.logcat(on_line=print, stop_event=stop) # stop.set() to end
|
|
211
|
+
|
|
212
|
+
dev.logcat_clear() # adb logcat -c
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`fmt=` sets the `-v` format (default `threadtime`); `dump=True` does `-d`
|
|
216
|
+
(dump current buffer and exit); `filterspecs=[...]` passes explicit `TAG:LEVEL`
|
|
217
|
+
specs. Lines are ANSI/control-char cleaned by default (`clean=True`).
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## File transfer (push / pull)
|
|
222
|
+
|
|
223
|
+
Files **and** folders, with a live percent callback (the GUI shows a progress
|
|
224
|
+
bar):
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
dev.push("local_dir/", "/sdcard/local_dir", on_progress=lambda p: print(p, "%"))
|
|
228
|
+
dev.pull("/sdcard/Download", "out/", on_progress=print)
|
|
229
|
+
|
|
230
|
+
tr = dev.push("big.bin", "/data/local/tmp/big.bin")
|
|
231
|
+
print(tr.human_size, tr.human_speed, tr.duration) # 12.0MB 48.0MB/s 0.25
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## App management
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
dev.install("app.apk", replace=True, grant_perms=True) # adb install -r -g
|
|
240
|
+
dev.install_multiple(["base.apk", "split_config.en.apk"]) # split APKs
|
|
241
|
+
dev.uninstall("com.example.app", keep_data=False)
|
|
242
|
+
|
|
243
|
+
dev.list_packages(third_party=True) # -> ["com.foo", ...]
|
|
244
|
+
dev.clear_app("com.example.app") # pm clear
|
|
245
|
+
dev.start_app("com.example.app") # launcher intent
|
|
246
|
+
dev.start_activity("com.example.app/.MainActivity")
|
|
247
|
+
dev.stop_app("com.example.app") # am force-stop
|
|
248
|
+
dev.grant("com.example.app", "android.permission.CAMERA")
|
|
249
|
+
dev.revoke("com.example.app", "android.permission.CAMERA")
|
|
250
|
+
print(dev.current_activity()) # foreground component
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Media — screenshot & screen record
|
|
256
|
+
|
|
257
|
+
```python
|
|
258
|
+
dev.screenshot("shot.png") # exec-out screencap -p
|
|
259
|
+
png_bytes = dev.screenshot() # raw PNG bytes if no path
|
|
260
|
+
|
|
261
|
+
# record on-device, then pull it (stop early with a stop_event)
|
|
262
|
+
dev.screen_record("clip.mp4", time_limit=20, size="1280x720", bit_rate="8M")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Port forwarding (forward / reverse)
|
|
268
|
+
|
|
269
|
+
Stoppable handles (`adb forward` / `adb reverse`):
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
fwd = dev.forward("tcp:9222", "localabstract:chrome_devtools_remote")
|
|
273
|
+
# ... use 127.0.0.1:9222 on the host ...
|
|
274
|
+
fwd.close() # adb forward --remove
|
|
275
|
+
|
|
276
|
+
with dev.reverse("tcp:8000", "tcp:8000"): # device reaches your PC:8000
|
|
277
|
+
...
|
|
278
|
+
print(dev.list_forwards())
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## scrcpy mirroring (the visual session)
|
|
284
|
+
|
|
285
|
+
Launch real-time screen mirroring + control — the visual analog of opening a
|
|
286
|
+
remote desktop for a host:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from turboadb import ScrcpyOptions
|
|
290
|
+
|
|
291
|
+
sess = dev.mirror(max_size=1280, bit_rate="8M", stay_awake=True)
|
|
292
|
+
# ... a scrcpy window is now mirroring & controlling the device ...
|
|
293
|
+
sess.stop()
|
|
294
|
+
|
|
295
|
+
# full control via ScrcpyOptions (crop is great for IVI displays):
|
|
296
|
+
opts = ScrcpyOptions(crop="1920x1080:0:0", display_id=0,
|
|
297
|
+
record="drive.mp4", turn_screen_off=True)
|
|
298
|
+
dev.mirror(opts)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Automotive (Android Automotive OS / IVI)
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
with ADBHandler(ADBConfig(host="192.168.1.50")) as hu:
|
|
307
|
+
info = hu.device_info()
|
|
308
|
+
print(info["manufacturer"], info["model"], "Android", info["android_version"])
|
|
309
|
+
if hu.is_automotive():
|
|
310
|
+
print("This is an Android Automotive OS head unit.")
|
|
311
|
+
# multi-display head units: mirror a specific display
|
|
312
|
+
hu.mirror(display_id=1, crop="1280x720:0:0")
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
`device_info()` includes an `automotive` flag (from the automotive hardware
|
|
316
|
+
feature / build characteristics), so you can branch IVI-specific flows.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Result objects & error handling
|
|
321
|
+
|
|
322
|
+
Every operation returns a structured result:
|
|
323
|
+
|
|
324
|
+
| Result | Key fields / props |
|
|
325
|
+
|-----------------|----------------------------------------------------------------|
|
|
326
|
+
| `CommandResult` | `.ok`, `.exit_code`, `.stdout`, `.stderr`, `.duration`, `.text`, `.lines` |
|
|
327
|
+
| `TransferResult`| `.size_bytes`, `.duration`, `.files`, `.human_size`, `.human_speed` |
|
|
328
|
+
| `StreamResult` | `.lines`, `.matches`, `.matched`, `.saved_to` |
|
|
329
|
+
| `OperationResult`| `.success`/`bool`, `.value`, `.error`, `.unwrap()` (safe mode) |
|
|
330
|
+
|
|
331
|
+
**Raise mode (default)** — great for test automation:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
try:
|
|
335
|
+
dev.shell("false", check=True)
|
|
336
|
+
except ADBError as exc:
|
|
337
|
+
print("failed:", exc)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Safe mode** — great for GUIs (never throws):
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
dev = ADBHandler(cfg, safe=True)
|
|
344
|
+
res = dev.install("app.apk")
|
|
345
|
+
if res: # OperationResult is falsy on failure
|
|
346
|
+
print("ok:", res.value)
|
|
347
|
+
else:
|
|
348
|
+
print("error:", res.error)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Exception hierarchy (catch `ADBError` for everything):
|
|
352
|
+
`ADBNotFoundError`, `ADBConnectionError`, `ADBTimeoutError`,
|
|
353
|
+
`ADBNotConnectedError`, `ADBCommandError`, `ADBTransferError`,
|
|
354
|
+
`ADBInstallError`, `ScrcpyError`.
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## CLI reference
|
|
359
|
+
|
|
360
|
+
```
|
|
361
|
+
turboadb doctor # is adb / scrcpy installed?
|
|
362
|
+
turboadb devices # list attached/known devices
|
|
363
|
+
turboadb info [-s S] # device identity / build / automotive flag
|
|
364
|
+
turboadb shell [-s S] -- CMD… # one-shot adb shell (use --su to wrap in su -c)
|
|
365
|
+
turboadb logcat [-s S] [--tag T --priority I --match RE --save F --stop-on-match --clear --dump]
|
|
366
|
+
turboadb logcat-clear[-s S]
|
|
367
|
+
turboadb push [-s S] LOCAL REMOTE
|
|
368
|
+
turboadb pull [-s S] REMOTE LOCAL
|
|
369
|
+
turboadb install [-s S] APK [APK…] [--grant --downgrade --no-replace]
|
|
370
|
+
turboadb uninstall [-s S] PKG [--keep-data]
|
|
371
|
+
turboadb packages [-s S] [--third-party --system] [FILTER]
|
|
372
|
+
turboadb clear [-s S] PKG
|
|
373
|
+
turboadb start|stop [-s S] PKG
|
|
374
|
+
turboadb screenshot [-s S] PATH
|
|
375
|
+
turboadb record [-s S] PATH [--time-limit 30 --size 1280x720 --bit-rate 8M]
|
|
376
|
+
turboadb forward [-s S] LOCAL REMOTE # stays until Ctrl+C
|
|
377
|
+
turboadb reverse [-s S] REMOTE LOCAL
|
|
378
|
+
turboadb scrcpy [-s S] [--max-size 1280 --bit-rate 8M --record F --turn-screen-off --no-control --wait]
|
|
379
|
+
turboadb connect HOST:PORT
|
|
380
|
+
turboadb disconnect [HOST:PORT]
|
|
381
|
+
turboadb tcpip [-s S] [PORT]
|
|
382
|
+
turboadb pair HOST:PAIRPORT CODE
|
|
383
|
+
turboadb reboot [-s S] [recovery|bootloader|sideload]
|
|
384
|
+
turboadb root [-s S]
|
|
385
|
+
turboadb gui # launch the desktop GUI
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
`-s` accepts a USB serial **or** a `host:port` network target. Add `--json` to
|
|
389
|
+
most commands for machine-readable output. Examples:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
turboadb -s 192.168.1.50:5555 shell -- dumpsys power | findstr mWakefulness
|
|
393
|
+
turboadb logcat --tag ActivityManager --priority I --match "ANR|FATAL" --save boot.log
|
|
394
|
+
turboadb install base.apk split_config.en.apk --grant
|
|
395
|
+
turboadb screenshot dash.png
|
|
396
|
+
turboadb scrcpy --max-size 1280 --bit-rate 8M
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Other console scripts: `turboadb-gui`, `turboadb-docs`, `turboadb-shortcut`.
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## API map
|
|
404
|
+
|
|
405
|
+
```
|
|
406
|
+
turboadb
|
|
407
|
+
├── ADBHandler(config=ADBConfig|None, *, serial=, safe=, quiet=, log_callback=)
|
|
408
|
+
│ ├── connect() / disconnect() / is_connected / get_state() / wait_for_device()
|
|
409
|
+
│ ├── tcpip(port) / connect_tcp(host, port) / pair(host, port, code)
|
|
410
|
+
│ ├── reboot(mode) / root() / unroot() / remount()
|
|
411
|
+
│ ├── getprop(name?) / device_info() / is_automotive()
|
|
412
|
+
│ ├── shell(cmd, su=, check=) / shell_many() / open_shell() -> ShellSession
|
|
413
|
+
│ ├── iter_lines(args) / stream(args, …) / logcat(…) / logcat_clear()
|
|
414
|
+
│ ├── push(local, remote, on_progress=) / pull(remote, local, on_progress=)
|
|
415
|
+
│ ├── install() / install_multiple() / uninstall() / list_packages()
|
|
416
|
+
│ ├── clear_app() / start_app() / start_activity() / stop_app()
|
|
417
|
+
│ ├── grant() / revoke() / current_activity()
|
|
418
|
+
│ ├── screenshot(path?) / screen_record(path, …)
|
|
419
|
+
│ ├── forward(local, remote) / reverse(remote, local) -> ForwardHandle
|
|
420
|
+
│ ├── list_forwards() / remove_all_forwards()
|
|
421
|
+
│ ├── mirror(options?|**opts) -> ScrcpySession
|
|
422
|
+
│ └── adb(*args) · adb_path · serial
|
|
423
|
+
├── ADBConfig / ScrcpyOptions
|
|
424
|
+
├── Device / list_devices() / first_online()
|
|
425
|
+
├── launch_scrcpy() / ScrcpySession
|
|
426
|
+
├── find_adb() / find_scrcpy() / adb_available() / scrcpy_available() / diagnose()
|
|
427
|
+
├── CommandResult / TransferResult / StreamResult / OperationResult / strip_ansi
|
|
428
|
+
└── ADBError + ADBNotFoundError / ADBConnectionError / ADBTimeoutError /
|
|
429
|
+
ADBNotConnectedError / ADBCommandError / ADBTransferError /
|
|
430
|
+
ADBInstallError / ScrcpyError
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Building the GUI exe & releasing
|
|
436
|
+
|
|
437
|
+
```bash
|
|
438
|
+
python scripts/make_icon.py # (re)generate the icon
|
|
439
|
+
python scripts/build_exe.py # -> dist/turboadb-gui.exe (PyQt5 baked in)
|
|
440
|
+
# copy the exe into turboadb/bin/ so the wheel ships it, then:
|
|
441
|
+
python scripts/release.py patch # bump -> test -> build -> twine check -> upload
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
`turboadb-gui` runs the bundled exe when present and falls back to running from
|
|
445
|
+
source (`turboadb[gui]`) otherwise. The release helper reads the PyPI token from
|
|
446
|
+
`TWINE_PASSWORD` (never hard-coded) and supports `--wheel-only` if a flaky
|
|
447
|
+
network makes the sdist+wheel upload hang.
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## License
|
|
452
|
+
|
|
453
|
+
MIT — see [LICENSE](LICENSE).
|
turboadb/__init__.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TurboADB
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
An Android **ADB + scrcpy** device toolkit for automotive/embedded Android
|
|
6
|
+
(Android Automotive OS / IVI head units) and general Android work. Wraps Google's
|
|
7
|
+
``adb`` and ``scrcpy`` behind one robust, structured API — usable as a Python
|
|
8
|
+
library, a CLI (``turboadb``), or a full PyQt5 desktop GUI (``turboadb-gui``).
|
|
9
|
+
|
|
10
|
+
Quick start
|
|
11
|
+
-----------
|
|
12
|
+
from turboadb import ADBHandler, ADBConfig
|
|
13
|
+
|
|
14
|
+
# USB (only device) — or serial="..." to pick one of several
|
|
15
|
+
with ADBHandler() as dev:
|
|
16
|
+
print(dev.shell("getprop ro.build.version.release").text)
|
|
17
|
+
dev.push("app.apk", "/data/local/tmp/app.apk")
|
|
18
|
+
dev.install("app.apk", grant_perms=True)
|
|
19
|
+
dev.logcat(tag="ActivityManager", match=r"ANR|FATAL", on_line=print)
|
|
20
|
+
|
|
21
|
+
# Network / Wi-Fi head unit
|
|
22
|
+
with ADBHandler(ADBConfig(host="192.168.1.50", port=5555)) as hu:
|
|
23
|
+
hu.mirror(max_size=1280) # launch scrcpy
|
|
24
|
+
|
|
25
|
+
Every action returns a structured result (``CommandResult`` / ``TransferResult``
|
|
26
|
+
/ ``StreamResult``). Pass ``safe=True`` to get an ``OperationResult`` instead of
|
|
27
|
+
exceptions (ideal for GUIs). The raw adb path is always at ``dev.adb_path``.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
__version__ = "0.1.0"
|
|
33
|
+
|
|
34
|
+
from .config import ADBConfig, ScrcpyOptions
|
|
35
|
+
from .core import ADBHandler, ADBDevice, ShellSession, ForwardHandle
|
|
36
|
+
from .devices import Device, list_devices, first_online
|
|
37
|
+
from .scrcpy import launch_scrcpy, ScrcpySession
|
|
38
|
+
from .tools import (find_adb, find_scrcpy, adb_available, scrcpy_available,
|
|
39
|
+
adb_version, diagnose, ADB_DOWNLOAD, SCRCPY_DOWNLOAD)
|
|
40
|
+
from .results import (CommandResult, TransferResult, StreamResult,
|
|
41
|
+
OperationResult, strip_ansi)
|
|
42
|
+
from .exceptions import (
|
|
43
|
+
ADBError,
|
|
44
|
+
ADBNotFoundError,
|
|
45
|
+
ADBConnectionError,
|
|
46
|
+
ADBTimeoutError,
|
|
47
|
+
ADBNotConnectedError,
|
|
48
|
+
ADBCommandError,
|
|
49
|
+
ADBTransferError,
|
|
50
|
+
ADBInstallError,
|
|
51
|
+
ScrcpyError,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
"ADBHandler", "ADBDevice", "ADBConfig", "ScrcpyOptions", "ShellSession",
|
|
56
|
+
"ForwardHandle", "Device", "list_devices", "first_online",
|
|
57
|
+
"launch_scrcpy", "ScrcpySession",
|
|
58
|
+
"find_adb", "find_scrcpy", "adb_available", "scrcpy_available",
|
|
59
|
+
"adb_version", "diagnose", "ADB_DOWNLOAD", "SCRCPY_DOWNLOAD",
|
|
60
|
+
"CommandResult", "TransferResult", "StreamResult", "OperationResult",
|
|
61
|
+
"strip_ansi",
|
|
62
|
+
"ADBError", "ADBNotFoundError", "ADBConnectionError", "ADBTimeoutError",
|
|
63
|
+
"ADBNotConnectedError", "ADBCommandError", "ADBTransferError",
|
|
64
|
+
"ADBInstallError", "ScrcpyError", "__version__",
|
|
65
|
+
]
|
turboadb/__main__.py
ADDED
turboadb/assets/icon.ico
ADDED
|
Binary file
|
turboadb/assets/icon.png
ADDED
|
Binary file
|
|
Binary file
|