draft init
This commit is contained in:
224
phone_agent/adb/device.py
Normal file
224
phone_agent/adb/device.py
Normal file
@@ -0,0 +1,224 @@
|
||||
"""Device control utilities for Android automation."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import time
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from phone_agent.config.apps import APP_PACKAGES
|
||||
|
||||
|
||||
def get_current_app(device_id: str | None = None) -> str:
|
||||
"""
|
||||
Get the currently focused app name.
|
||||
|
||||
Args:
|
||||
device_id: Optional ADB device ID for multi-device setups.
|
||||
|
||||
Returns:
|
||||
The app name if recognized, otherwise "System Home".
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
result = subprocess.run(
|
||||
adb_prefix + ["shell", "dumpsys", "window"], capture_output=True, text=True
|
||||
)
|
||||
output = result.stdout
|
||||
|
||||
# Parse window focus info
|
||||
for line in output.split("\n"):
|
||||
if "mCurrentFocus" in line or "mFocusedApp" in line:
|
||||
for app_name, package in APP_PACKAGES.items():
|
||||
if package in line:
|
||||
return app_name
|
||||
|
||||
return "System Home"
|
||||
|
||||
|
||||
def tap(x: int, y: int, device_id: str | None = None, delay: float = 1.0) -> None:
|
||||
"""
|
||||
Tap at the specified coordinates.
|
||||
|
||||
Args:
|
||||
x: X coordinate.
|
||||
y: Y coordinate.
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after tap.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def double_tap(
|
||||
x: int, y: int, device_id: str | None = None, delay: float = 1.0
|
||||
) -> None:
|
||||
"""
|
||||
Double tap at the specified coordinates.
|
||||
|
||||
Args:
|
||||
x: X coordinate.
|
||||
y: Y coordinate.
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after double tap.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
|
||||
)
|
||||
time.sleep(0.1)
|
||||
subprocess.run(
|
||||
adb_prefix + ["shell", "input", "tap", str(x), str(y)], capture_output=True
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def long_press(
|
||||
x: int,
|
||||
y: int,
|
||||
duration_ms: int = 3000,
|
||||
device_id: str | None = None,
|
||||
delay: float = 1.0,
|
||||
) -> None:
|
||||
"""
|
||||
Long press at the specified coordinates.
|
||||
|
||||
Args:
|
||||
x: X coordinate.
|
||||
y: Y coordinate.
|
||||
duration_ms: Duration of press in milliseconds.
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after long press.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix
|
||||
+ ["shell", "input", "swipe", str(x), str(y), str(x), str(y), str(duration_ms)],
|
||||
capture_output=True,
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def swipe(
|
||||
start_x: int,
|
||||
start_y: int,
|
||||
end_x: int,
|
||||
end_y: int,
|
||||
duration_ms: int | None = None,
|
||||
device_id: str | None = None,
|
||||
delay: float = 1.0,
|
||||
) -> None:
|
||||
"""
|
||||
Swipe from start to end coordinates.
|
||||
|
||||
Args:
|
||||
start_x: Starting X coordinate.
|
||||
start_y: Starting Y coordinate.
|
||||
end_x: Ending X coordinate.
|
||||
end_y: Ending Y coordinate.
|
||||
duration_ms: Duration of swipe in milliseconds (auto-calculated if None).
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after swipe.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
if duration_ms is None:
|
||||
# Calculate duration based on distance
|
||||
dist_sq = (start_x - end_x) ** 2 + (start_y - end_y) ** 2
|
||||
duration_ms = int(dist_sq / 1000)
|
||||
duration_ms = max(1000, min(duration_ms, 2000)) # Clamp between 1000-2000ms
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix
|
||||
+ [
|
||||
"shell",
|
||||
"input",
|
||||
"swipe",
|
||||
str(start_x),
|
||||
str(start_y),
|
||||
str(end_x),
|
||||
str(end_y),
|
||||
str(duration_ms),
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def back(device_id: str | None = None, delay: float = 1.0) -> None:
|
||||
"""
|
||||
Press the back button.
|
||||
|
||||
Args:
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after pressing back.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix + ["shell", "input", "keyevent", "4"], capture_output=True
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def home(device_id: str | None = None, delay: float = 1.0) -> None:
|
||||
"""
|
||||
Press the home button.
|
||||
|
||||
Args:
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after pressing home.
|
||||
"""
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix + ["shell", "input", "keyevent", "KEYCODE_HOME"], capture_output=True
|
||||
)
|
||||
time.sleep(delay)
|
||||
|
||||
|
||||
def launch_app(app_name: str, device_id: str | None = None, delay: float = 1.0) -> bool:
|
||||
"""
|
||||
Launch an app by name.
|
||||
|
||||
Args:
|
||||
app_name: The app name (must be in APP_PACKAGES).
|
||||
device_id: Optional ADB device ID.
|
||||
delay: Delay in seconds after launching.
|
||||
|
||||
Returns:
|
||||
True if app was launched, False if app not found.
|
||||
"""
|
||||
if app_name not in APP_PACKAGES:
|
||||
return False
|
||||
|
||||
adb_prefix = _get_adb_prefix(device_id)
|
||||
package = APP_PACKAGES[app_name]
|
||||
|
||||
subprocess.run(
|
||||
adb_prefix
|
||||
+ [
|
||||
"shell",
|
||||
"monkey",
|
||||
"-p",
|
||||
package,
|
||||
"-c",
|
||||
"android.intent.category.LAUNCHER",
|
||||
"1",
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
time.sleep(delay)
|
||||
return True
|
||||
|
||||
|
||||
def _get_adb_prefix(device_id: str | None) -> list:
|
||||
"""Get ADB command prefix with optional device specifier."""
|
||||
if device_id:
|
||||
return ["adb", "-s", device_id]
|
||||
return ["adb"]
|
||||
Reference in New Issue
Block a user