#!/usr/bin/env python3 import argparse import os import re import subprocess import time import xml.etree.ElementTree as ET ENV = dict(os.environ, ADB_SERVER_SOCKET=os.environ.get("ADB_SERVER_SOCKET", "tcp:127.0.0.1:15037")) def adb(*args, capture=False): result = subprocess.run(["adb", *args], env=ENV, check=True, capture_output=capture, text=capture) return result.stdout if capture else "" def nodes(): adb("shell", "uiautomator", "dump", "/sdcard/window.xml") return ET.fromstring(adb("exec-out", "cat", "/sdcard/window.xml", capture=True)) def find(root, label): for node in root.iter("node"): if label in node.attrib.get("text", "") or label in node.attrib.get("content-desc", ""): return node return None def tap(node): x1, y1, x2, y2 = [int(v) for v in re.findall(r"\d+", node.attrib["bounds"])] adb("shell", "input", "tap", str((x1 + x2) // 2), str((y1 + y2) // 2)) parser = argparse.ArgumentParser() parser.add_argument("label") parser.add_argument("--force-stop", default="org.chromium.chrome") args = parser.parse_args() if args.force_stop: adb("shell", "am", "force-stop", args.force_stop) adb("shell", "am", "force-stop", "com.google.android.apps.nexuslauncher") adb("shell", "input", "keyevent", "WAKEUP") adb("shell", "input", "keyevent", "HOME") time.sleep(2) for direction in ["left"] * 5 + ["right"] * 10: root = nodes() node = find(root, args.label) if node is not None: print(f"launching {args.label} at {node.attrib['bounds']}") tap(node) time.sleep(6) break if direction == "left": adb("shell", "input", "swipe", "950", "1000", "100", "1000", "400") else: adb("shell", "input", "swipe", "100", "1000", "950", "1000", "400") time.sleep(0.7) else: raise SystemExit(f"shortcut not found: {args.label}")