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('<>', 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()