You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
4.3 KiB

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
adb_swipe_norm.py
-----------------
Use normalized (0~1) coordinates and distance to perform an ADB swipe that adapts to any screen resolution.
Examples:
python adb_swipe_norm.py --dir down
python adb_swipe_norm.py --x 0.731156 --y 0.0102157 --dir down --dist 0.5 --duration 300
python adb_swipe_norm.py --dir up --dist 0.4
python adb_swipe_norm.py --device <serial> --dir down
Author: ChatGPT
"""
import argparse
import re
import subprocess
import sys
from typing import Optional, Tuple
def run_adb(cmd: list, device: Optional[str] = None) -> subprocess.CompletedProcess:
full = ["adb"]
if device:
full += ["-s", device]
full += cmd
return subprocess.run(full, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=False)
def ensure_device(device: Optional[str]) -> None:
# Make sure adb is available and at least one device (or the specified one) is connected
try:
out = run_adb(["devices"]).stdout.strip().splitlines()
except FileNotFoundError:
print("Error: 'adb' not found. Please install Android Platform Tools and ensure 'adb' is in PATH.")
sys.exit(1)
devices = [line.split()[0] for line in out[1:] if line.strip() and "\tdevice" in line]
if device:
if device not in devices:
print(f"Error: device '{device}' not found. Connected devices: {devices or '[]'}")
sys.exit(1)
else:
if not devices:
print("Error: no connected devices. Run 'adb devices' to verify.")
sys.exit(1)
def get_screen_size(device: Optional[str] = None) -> Tuple[int, int]:
# Try wm size first
res = run_adb(["shell", "wm", "size"], device=device)
m = re.search(r"(\d+)\s*x\s*(\d+)", res.stdout)
if m:
w, h = int(m.group(1)), int(m.group(2))
return w, h
# Fallback: dumpsys display
res = run_adb(["shell", "dumpsys", "display"], device=device)
m = re.search(r"(\d+)\s*x\s*(\d+)", res.stdout)
if m:
w, h = int(m.group(1)), int(m.group(2))
return w, h
print("Error: failed to detect screen size via 'wm size' or 'dumpsys display'.")
sys.exit(1)
def clamp01(v: float) -> float:
return max(0.0, min(1.0, v))
def main():
parser = argparse.ArgumentParser(description="ADB swipe with normalized coordinates (0~1) adapting to any screen size.")
parser.add_argument("--x", type=float, default=0.5, help="Normalized start x in [0,1]. Default: 0.5 (center).")
parser.add_argument("--y", type=float, default=0.2, help="Normalized start y in [0,1]. Default: 0.2 (upper area).")
parser.add_argument("--dir", choices=["up", "down", "left", "right"], default="down", help="Swipe direction. Default: down.")
parser.add_argument("--dist", type=float, default=0.6, help="Swipe distance as fraction of screen (0~1). Default: 0.6.")
parser.add_argument("--duration", type=int, default=300, help="Swipe duration in milliseconds. Default: 300.")
parser.add_argument("--device", type=str, default=None, help="ADB device serial (optional).")
args = parser.parse_args()
# Validate and clamp
sx_n = clamp01(args.x)
sy_n = clamp01(args.y)
dist = max(0.0, min(1.0, args.dist))
ensure_device(args.device)
width, height = get_screen_size(device=args.device)
# Convert normalized start point to pixels
sx = int(round(sx_n * width))
sy = int(round(sy_n * height))
# Compute end point based on direction & distance
ex, ey = sx, sy
if args.dir == "down":
ey = sy + int(round(height * dist))
elif args.dir == "up":
ey = sy - int(round(height * dist))
elif args.dir == "right":
ex = sx + int(round(width * dist))
elif args.dir == "left":
ex = sx - int(round(width * dist))
# Clamp endpoints to screen bounds just in case
ex = max(0, min(width - 1, ex))
ey = max(0, min(height - 1, ey))
# Build and run adb command
cmd = ["shell", "input", "swipe", str(sx), str(sy), str(ex), str(ey), str(args.duration)]
res = run_adb(cmd, device=args.device)
if res.returncode != 0:
print("ADB error:", res.stderr.strip() or res.stdout.strip())
sys.exit(res.returncode)
print(f"OK: swipe {args.dir} from ({sx},{sy}) to ({ex},{ey}) in {args.duration} ms on {width}x{height}")
if __name__ == "__main__":
main()