176 lines
6.2 KiB
Python
176 lines
6.2 KiB
Python
import json
|
|
import subprocess
|
|
import os
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
from tkinterdnd2 import DND_FILES, TkinterDnD # Import DnD support
|
|
|
|
def load_devices(config_file):
|
|
try:
|
|
with open(config_file, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
return data.get("devices", [])
|
|
except (FileNotFoundError, json.JSONDecodeError):
|
|
return []
|
|
|
|
def unlock_device():
|
|
device_serial = selected_device.get()
|
|
if not device_serial:
|
|
status_label.config(text="Error: No device selected.")
|
|
return
|
|
subprocess.run(["adb", "-s", device_serial, "shell", "input", "keyevent", "26"])
|
|
subprocess.run(["adb", "-s", device_serial, "shell", "input", "keyevent", "82"])
|
|
status_label.config(text=f"Device {device_serial} unlocked successfully.")
|
|
|
|
def take_screenshot():
|
|
device_serial = selected_device.get()
|
|
if not device_serial:
|
|
status_label.config(text="Error: No device selected.")
|
|
return
|
|
|
|
screenshot_name = screenshot_name_entry.get().strip()
|
|
if not screenshot_name:
|
|
status_label.config(text="Error: Screenshot name cannot be empty.")
|
|
return
|
|
|
|
if not screenshot_name.lower().endswith(".png"):
|
|
screenshot_name += ".png"
|
|
|
|
remote_screenshot_path = f"/sdcard/{screenshot_name}"
|
|
local_screenshot_folder = "/Users/justinschwegmann/Downloads/"
|
|
local_screenshot_path = os.path.join(local_screenshot_folder, screenshot_name)
|
|
|
|
result = subprocess.run(["adb", "-s", device_serial, "shell", "screencap", "-p", remote_screenshot_path])
|
|
if result.returncode != 0:
|
|
status_label.config(text=f"Error capturing screenshot on device {device_serial}.")
|
|
return
|
|
|
|
result = subprocess.run(["adb", "-s", device_serial, "pull", remote_screenshot_path, local_screenshot_path])
|
|
if result.returncode != 0:
|
|
status_label.config(text=f"Error pulling screenshot from device {device_serial}.")
|
|
return
|
|
|
|
status_label.config(text=f"Screenshot saved to {local_screenshot_path}.")
|
|
|
|
def start_scrcpy():
|
|
device_serial = selected_device.get()
|
|
if not device_serial:
|
|
status_label.config(text="Error: No device selected.")
|
|
return
|
|
|
|
fps_value_str = scrcpy_fps_entry.get().strip()
|
|
if not fps_value_str.isdigit():
|
|
fps_value_str = "60"
|
|
|
|
command = [
|
|
"scrcpy",
|
|
"--video-codec=h265",
|
|
"-m", "1920",
|
|
f"--max-fps={fps_value_str}",
|
|
"--no-audio",
|
|
"-K",
|
|
"-s", device_serial
|
|
]
|
|
|
|
try:
|
|
subprocess.Popen(command)
|
|
status_label.config(text=f"scrcpy started with FPS={fps_value_str}.")
|
|
except FileNotFoundError:
|
|
status_label.config(text="Error: scrcpy not found in PATH.")
|
|
|
|
def push_photo():
|
|
device_serial = selected_device.get()
|
|
if not device_serial:
|
|
status_label.config(text="Error: No device selected.")
|
|
return
|
|
|
|
local_photo_path = photo_path_entry.get().strip()
|
|
if not local_photo_path or not os.path.isfile(local_photo_path):
|
|
status_label.config(text="Error: Invalid local photo path.")
|
|
return
|
|
|
|
destination_path = "/sdcard/DCIM/"
|
|
result = subprocess.run(["adb", "-s", device_serial, "push", local_photo_path, destination_path])
|
|
if result.returncode != 0:
|
|
status_label.config(text=f"Error pushing photo to device {device_serial}.")
|
|
else:
|
|
status_label.config(text=f"Photo pushed to device {device_serial} successfully.")
|
|
|
|
def handle_drop(event):
|
|
"""
|
|
Handle file drop event. Extracts file path from the event data
|
|
and inserts it into the photo_path_entry widget.
|
|
"""
|
|
# event.data can contain one or more file paths; we'll use the first one
|
|
files = root.tk.splitlist(event.data)
|
|
if files:
|
|
photo_path_entry.delete(0, tk.END)
|
|
photo_path_entry.insert(0, files[0])
|
|
|
|
# -------------- Main GUI code --------------
|
|
if __name__ == "__main__":
|
|
CONFIG_FILE = "devices.json"
|
|
devices_list = load_devices(CONFIG_FILE)
|
|
|
|
# Use TkinterDnD's Tk class for drag-and-drop support
|
|
root = TkinterDnD.Tk()
|
|
root.title("ADB Unlock, Screenshot, Scrcpy & Photo Push Tool")
|
|
root.geometry("450x550")
|
|
|
|
main_frame = ttk.Frame(root, padding="10")
|
|
main_frame.pack(fill="both", expand=True)
|
|
|
|
# Device selection
|
|
device_label = ttk.Label(main_frame, text="Select Device:")
|
|
device_label.pack(pady=5)
|
|
|
|
selected_device = tk.StringVar()
|
|
device_combobox = ttk.Combobox(main_frame, textvariable=selected_device,
|
|
values=devices_list, state="readonly")
|
|
device_combobox.pack(pady=5)
|
|
if devices_list:
|
|
device_combobox.current(0)
|
|
|
|
unlock_button = ttk.Button(main_frame, text="Unlock Device", command=unlock_device)
|
|
unlock_button.pack(pady=5)
|
|
|
|
# Screenshot section
|
|
screenshot_label = ttk.Label(main_frame, text="Screenshot Name (.png):")
|
|
screenshot_label.pack(pady=5)
|
|
|
|
screenshot_name_entry = ttk.Entry(main_frame, width=30)
|
|
screenshot_name_entry.pack(pady=5)
|
|
|
|
screenshot_button = ttk.Button(main_frame, text="Take Screenshot", command=take_screenshot)
|
|
screenshot_button.pack(pady=5)
|
|
|
|
# scrcpy section
|
|
scrcpy_fps_label = ttk.Label(main_frame, text="scrcpy Max FPS:")
|
|
scrcpy_fps_label.pack(pady=5)
|
|
|
|
scrcpy_fps_entry = ttk.Entry(main_frame, width=10)
|
|
scrcpy_fps_entry.insert(0, "60")
|
|
scrcpy_fps_entry.pack(pady=5)
|
|
|
|
scrcpy_button = ttk.Button(main_frame, text="Start scrcpy", command=start_scrcpy)
|
|
scrcpy_button.pack(pady=5)
|
|
|
|
# Photo push section with drag-and-drop enabled entry
|
|
photo_label = ttk.Label(main_frame, text="Local Photo Path (drag & drop a file below):")
|
|
photo_label.pack(pady=5)
|
|
|
|
# Use a standard tk.Entry for drag-and-drop compatibility
|
|
photo_path_entry = tk.Entry(main_frame, width=40)
|
|
photo_path_entry.pack(pady=5)
|
|
|
|
# Register the entry as a drop target and bind the drop event
|
|
photo_path_entry.drop_target_register(DND_FILES)
|
|
photo_path_entry.dnd_bind('<<Drop>>', handle_drop)
|
|
|
|
push_photo_button = ttk.Button(main_frame, text="Push Photo", command=push_photo)
|
|
push_photo_button.pack(pady=5)
|
|
|
|
status_label = ttk.Label(main_frame, text="Status: Ready", foreground="blue")
|
|
status_label.pack(pady=10)
|
|
|
|
root.mainloop() |