reorganized file and folder structure to reflect responsibilities
This commit is contained in:
@@ -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
@@ -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",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
Core Package
|
||||
Core functionality and business logic components.
|
||||
"""
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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:
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
GUI Dialogs Package
|
||||
Dialog windows and user interaction components.
|
||||
"""
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
GUI Handlers Package
|
||||
Event handlers and animation controllers.
|
||||
"""
|
||||
+14
-14
@@ -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
@@ -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__":
|
||||
|
||||
@@ -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:
|
||||
@@ -0,0 +1,4 @@
|
||||
"""
|
||||
Utils Package
|
||||
Utility functions and helper modules.
|
||||
"""
|
||||
Reference in New Issue
Block a user