's'
This commit is contained in:
@@ -4,7 +4,9 @@ import asyncio
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from urllib.parse import quote
|
||||
|
||||
@@ -293,13 +295,42 @@ async def android_ui_nodes(serial: str, limit: int = 80) -> list[dict]:
|
||||
return nodes[: max(1, min(limit, 200))]
|
||||
|
||||
|
||||
def _is_png(data: bytes) -> bool:
|
||||
return bool(data and len(data) > 8 and data.startswith(b"\x89PNG\r\n\x1a\n"))
|
||||
|
||||
|
||||
async def android_screenshot(serial: str) -> bytes:
|
||||
if not serial:
|
||||
raise ValueError("Missing Android device serial")
|
||||
code, data, err = await _adb_binary(serial, "exec-out", "screencap", "-p", timeout=20.0)
|
||||
if code != 0 or not data:
|
||||
raise RuntimeError(err or "Failed to capture screenshot")
|
||||
return data.replace(b"\r\n", b"\n")
|
||||
if code == 0 and data:
|
||||
normalized = data.replace(b"\r\n", b"\n")
|
||||
if _is_png(data) or normalized.startswith(b"\x89PNG"):
|
||||
return normalized
|
||||
|
||||
last_err = err or "exec-out screencap failed"
|
||||
for remote in ("/data/local/tmp/live-cap.png", "/sdcard/live-cap.png"):
|
||||
tmp = Path(tempfile.mkstemp(suffix=".png")[1])
|
||||
try:
|
||||
sh_code, _, sh_err = await _adb_text(serial, "shell", f"screencap -p {remote}", timeout=25.0)
|
||||
if sh_code != 0:
|
||||
last_err = sh_err or f"screencap to {remote} failed"
|
||||
continue
|
||||
pull_code, _, pull_err = await _run_cmd(
|
||||
_adb_prefix(serial) + ["pull", remote, str(tmp)],
|
||||
timeout=35.0,
|
||||
)
|
||||
if pull_code != 0:
|
||||
last_err = pull_err or "adb pull failed"
|
||||
continue
|
||||
pulled = tmp.read_bytes()
|
||||
if _is_png(pulled) or pulled.startswith(b"\x89PNG"):
|
||||
return pulled.replace(b"\r\n", b"\n")
|
||||
last_err = "Pulled file is not a valid PNG"
|
||||
finally:
|
||||
tmp.unlink(missing_ok=True)
|
||||
|
||||
raise RuntimeError(last_err)
|
||||
|
||||
|
||||
def _normalize_text(text: str) -> str:
|
||||
@@ -420,4 +451,12 @@ async def android_action(action: str, serial: str, payload: Optional[dict] = Non
|
||||
output = await _shell(serial, f"pm clear '{package}'", timeout=25.0)
|
||||
return {"message": f"Cleared app data: {package}", "output": output}
|
||||
|
||||
if action == "install_apk":
|
||||
apk_path = str(payload.get("path", "")).strip()
|
||||
if not apk_path or any(ch in apk_path for ch in ";$`&|()<>\n\r"):
|
||||
raise ValueError("Invalid apk path on device (use adb push to /sdcard/ first)")
|
||||
apk_path = apk_path.replace("'", "'\\''")
|
||||
output = await _shell(serial, f"pm install -r -t '{apk_path}'", timeout=180.0)
|
||||
return {"message": "APK install finished", "output": output}
|
||||
|
||||
raise ValueError(f"Unsupported Android action: {action}")
|
||||
|
||||
Reference in New Issue
Block a user