reorganized file and folder structure to reflect responsibilities

This commit is contained in:
2025-09-23 06:01:53 +08:00
committed by Jason Ross
parent 6d08df1807
commit f8a942bf51
20 changed files with 58 additions and 158 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ A simple Windows GUI tool to pull folders from an Android device using ADB (Andr
2. Run the application:
```sh
poetry run python3 src/main.py
poetry run python -m src.main
```
3. Follow the on-screen instructions:
+1 -3
View File
@@ -3,9 +3,8 @@ Android File Handler - Package Initialization
Exposes the main components of the Android file handler application.
"""
from .adb_manager import (
from .core.adb_manager import (
ADBManager,
LinuxMTPManager,
get_adb_binary_path,
is_adb_available,
get_platform_type,
@@ -14,7 +13,6 @@ from .gui import AndroidFileHandlerGUI, main
__all__ = [
"ADBManager",
"LinuxMTPManager",
"AndroidFileHandlerGUI",
"get_adb_binary_path",
"is_adb_available",
+4
View File
@@ -0,0 +1,4 @@
"""
Core Package
Core functionality and business logic components.
"""
+3 -129
View File
@@ -19,9 +19,9 @@ from typing import Optional
import hashlib
try:
from file_deduplication import FileDeduplicator
from utils.file_deduplication import FileDeduplicator
except ImportError:
from .file_deduplication import FileDeduplicator
from ..utils.file_deduplication import FileDeduplicator
def get_executable_directory() -> str:
"""Get the directory containing the executable or script."""
@@ -1029,133 +1029,7 @@ class ADBManager:
return self.deduplicator.check_files_identical(
local_path, remote_path, self.run_adb_command, algorithm
)
class LinuxMTPManager:
"""Manages MTP operations on Linux systems."""
def __init__(self):
self.mount_point = "/tmp/android_mtp"
def mount_mtp_device(self) -> Optional[str]:
"""Mount MTP device to filesystem using jmtpfs."""
try:
# Create mount point
os.makedirs(self.mount_point, exist_ok=True)
# Check if already mounted
result = subprocess.run(
["mountpoint", self.mount_point], capture_output=True, text=True
)
if result.returncode == 0:
return self.mount_point
# First, try to unmount any existing GVFS MTP mounts
self._unmount_gvfs_mtp()
# Kill any existing MTP processes that might be interfering
subprocess.run(["pkill", "-f", "gvfs-mtp"], capture_output=True)
subprocess.run(["pkill", "-f", "jmtpfs"], capture_output=True)
# Wait a moment for processes to clean up
time.sleep(1)
# Mount using jmtpfs
result = subprocess.run(
["jmtpfs", self.mount_point], capture_output=True, text=True
)
if result.returncode == 0:
return self.mount_point
else:
print(f"Failed to mount MTP device: {result.stderr}")
return None
except Exception as e:
print(f"Error mounting MTP device: {e}")
return None
def _unmount_gvfs_mtp(self):
"""Unmount any GVFS MTP mounts."""
try:
# Find GVFS MTP mounts
result = subprocess.run(["mount"], capture_output=True, text=True)
for line in result.stdout.splitlines():
if "gvfs" in line and "mtp" in line:
# Extract mount point from mount line
parts = line.split()
if len(parts) >= 3:
mount_point = parts[2]
subprocess.run(
["fusermount", "-u", mount_point], capture_output=True
)
# Also try to unmount common GVFS locations
gvfs_locations = ["/run/user/*/gvfs/mtp*", "/media/*", "~/.gvfs/mtp*"]
for location_pattern in gvfs_locations:
result = subprocess.run(
["find", "/run/user", "-name", "mtp*", "-type", "d"],
capture_output=True,
text=True,
)
for mount_point in result.stdout.strip().split("\n"):
if mount_point:
subprocess.run(
["fusermount", "-u", mount_point], capture_output=True
)
except Exception as e:
print(f"Warning: Could not unmount GVFS MTP: {e}")
def unmount_mtp_device(self) -> bool:
"""Unmount MTP device."""
try:
subprocess.run(["fusermount", "-u", self.mount_point], check=True)
return True
except subprocess.CalledProcessError as e:
print(f"Failed to unmount: {e}")
return False
def find_gvfs_mtp_mount(self) -> Optional[str]:
"""Find existing GVFS MTP mount point."""
try:
# Check common GVFS mount locations (Linux only)
if sys.platform.startswith("linux"):
try:
if hasattr(os, "getuid"):
user_id = os.getuid() # type: ignore
gvfs_patterns = [
f"/run/user/{user_id}/gvfs/mtp*",
"/media/*android*",
"/media/*MTP*",
]
else:
# Fallback if getuid is not available
gvfs_patterns = [
"/run/user/*/gvfs/mtp*",
"/media/*android*",
"/media/*MTP*",
]
except (AttributeError, OSError):
# Fallback if getuid is not available or fails
gvfs_patterns = [
"/run/user/*/gvfs/mtp*",
"/media/*android*",
"/media/*MTP*",
]
for pattern in gvfs_patterns:
matches = glob.glob(pattern)
if matches:
# Return the first valid mount point
for mount in matches:
if os.path.isdir(mount):
return mount
return None
except Exception as e:
print(f"Error finding GVFS mount: {e}")
return None
# Helper functions for standalone usage
def is_adb_available() -> bool:
"""Check if ADB binary is available using the centralized resolver."""
+1 -1
View File
@@ -4,7 +4,7 @@ Provides modular GUI components for the Android file transfer application.
"""
from .main_window import AndroidFileHandlerGUI, main
from .file_browser import AndroidFileBrowser
from .components.file_browser import AndroidFileBrowser
__all__ = [
"AndroidFileHandlerGUI",
+4
View File
@@ -0,0 +1,4 @@
"""
GUI Components Package
Reusable UI components and widgets.
"""
@@ -8,9 +8,9 @@ from tkinter import messagebox
from typing import Callable, Optional
try:
from .license_agreement import LicenseAgreementFrame, check_license_agreement
from ..dialogs.license_agreement import LicenseAgreementFrame, check_license_agreement
except ImportError:
from license_agreement import LicenseAgreementFrame, check_license_agreement
from gui.dialogs.license_agreement import LicenseAgreementFrame, check_license_agreement
class PathSelectorFrame:
+4
View File
@@ -0,0 +1,4 @@
"""
GUI Dialogs Package
Dialog windows and user interaction components.
"""
+4
View File
@@ -0,0 +1,4 @@
"""
GUI Handlers Package
Event handlers and animation controllers.
"""
+14 -14
View File
@@ -10,33 +10,33 @@ from tkinter import messagebox, filedialog
try:
# Try relative import first (when used as module)
from ..adb_manager import ADBManager, is_adb_available
from ..core.adb_manager import ADBManager, is_adb_available
except ImportError:
# Fall back to direct import (when run directly)
from adb_manager import ADBManager, is_adb_available
from core.adb_manager import ADBManager, is_adb_available
try:
# Try relative imports first
from .file_browser import AndroidFileBrowser
from .animation_handler import AnimationHandler
from .device_manager import DeviceManager
from .dialog_manager import DialogManager
from .transfer_manager import TransferManager
from .ui_components import (
from .components.file_browser import AndroidFileBrowser
from .handlers.animation_handler import AnimationHandler
from .dialogs.dialog_manager import DialogManager
from .components.ui_components import (
PathSelectorFrame, DirectionSelector, StatusLabel,
TransferButton, LicenseManager
)
from ..managers.device_manager import DeviceManager
from ..managers.transfer_manager import TransferManager
except ImportError:
# Fall back to direct imports
from src.gui.file_browser import AndroidFileBrowser
from src.gui.animation_handler import AnimationHandler
from src.gui.device_manager import DeviceManager
from src.gui.dialog_manager import DialogManager
from src.gui.transfer_manager import TransferManager
from src.gui.ui_components import (
from components.file_browser import AndroidFileBrowser
from handlers.animation_handler import AnimationHandler
from dialogs.dialog_manager import DialogManager
from components.ui_components import (
PathSelectorFrame, DirectionSelector, StatusLabel,
TransferButton, LicenseManager
)
from managers.device_manager import DeviceManager
from managers.transfer_manager import TransferManager
class AndroidFileHandlerGUI(tk.Tk):
+6 -2
View File
@@ -4,8 +4,12 @@ Android File Handler - Main Entry Point
Simple entry point to launch the Android file transfer application.
"""
from gui.main_window import main
from gui.license_agreement import run_windows_first_run_if_needed
try:
from gui.main_window import main
from gui.dialogs.license_agreement import run_windows_first_run_if_needed
except ImportError:
from .gui.main_window import main
from .gui.dialogs.license_agreement import run_windows_first_run_if_needed
if __name__ == "__main__":
+4
View File
@@ -0,0 +1,4 @@
"""
Managers Package
Business logic managers for device and transfer operations.
"""
@@ -8,9 +8,9 @@ from tkinter import messagebox
from typing import Optional, Callable
try:
from ..adb_manager import ADBManager, is_adb_available
from ..core.adb_manager import ADBManager, is_adb_available
except ImportError:
from adb_manager import ADBManager, is_adb_available
from core.adb_manager import ADBManager, is_adb_available
class DeviceManager:
@@ -9,12 +9,12 @@ from typing import Optional, Callable, Tuple, Dict, Any
try:
from .device_manager import DeviceManager
from .animation_handler import AnimationHandler
from .dialog_manager import DialogManager
from ..gui.handlers.animation_handler import AnimationHandler
from ..gui.dialogs.dialog_manager import DialogManager
except ImportError:
from device_manager import DeviceManager
from animation_handler import AnimationHandler
from dialog_manager import DialogManager
from gui.handlers.animation_handler import AnimationHandler
from gui.dialogs.dialog_manager import DialogManager
class TransferManager:
+4
View File
@@ -0,0 +1,4 @@
"""
Utils Package
Utility functions and helper modules.
"""