#!/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 --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()