updating Windows install experience
This commit is contained in:
@@ -17,6 +17,39 @@ chmod +x dist/android-file-handler
|
||||
# this ensures the package installs /usr/local/bin/android-file-handler (no extra dist/ prefix)
|
||||
|
||||
# Debian
|
||||
# Prepare packaging layout in a separate directory to avoid clobbering build artifacts
|
||||
PKG_DIR="pkg_dist"
|
||||
rm -rf "$PKG_DIR"
|
||||
mkdir -p "$PKG_DIR/usr/local/bin" \
|
||||
"$PKG_DIR/usr/share/applications" \
|
||||
"$PKG_DIR/usr/share/icons/hicolor/256x256/apps"
|
||||
|
||||
# Copy built binary
|
||||
cp dist/android-file-handler "$PKG_DIR/usr/local/bin/android-file-handler"
|
||||
chmod 0755 "$PKG_DIR/usr/local/bin/android-file-handler"
|
||||
|
||||
# Create .desktop file
|
||||
cat > "$PKG_DIR/usr/share/applications/android-file-handler.desktop" <<'EOF'
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Android File Handler
|
||||
Comment=Manage Android device files via ADB
|
||||
Exec=/usr/local/bin/android-file-handler %U
|
||||
Icon=android-file-handler
|
||||
Terminal=false
|
||||
Categories=Utility;Development;
|
||||
StartupNotify=true
|
||||
EOF
|
||||
chmod 644 "$PKG_DIR/usr/share/applications/android-file-handler.desktop"
|
||||
|
||||
# Copy icon if present in repo (fallback optional)
|
||||
if [ -f "assets/icons/android-file-handler-256.png" ]; then
|
||||
cp assets/icons/android-file-handler-256.png "$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
||||
chmod 644 "$PKG_DIR/usr/share/icons/hicolor/256x256/apps/android-file-handler.png"
|
||||
fi
|
||||
|
||||
# Build the Debian package and include postinst script
|
||||
fpm -s dir -t deb -n android-file-handler -v "$VERSION" \
|
||||
--architecture amd64 --prefix /usr/local/bin --deb-user root --deb-group root \
|
||||
-p "dist/android-file-handler_${VERSION}_amd64.deb" -C dist android-file-handler
|
||||
--architecture amd64 --prefix /usr/local/bin --deb-user root --deb-group root \
|
||||
--after-install scripts/debian/postinst.sh \
|
||||
-p "dist/android-file-handler_${VERSION}_amd64.deb" -C "$PKG_DIR" usr/local/bin/android-file-handler usr/share/applications/android-file-handler.desktop usr/share/icons/hicolor/256x256/apps/android-file-handler.png
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
# Post-installation script for android-file-handler
|
||||
# - update desktop database if available
|
||||
# - update icon cache if available
|
||||
# - ensure binary is executable
|
||||
|
||||
# Update desktop database if the utility exists
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database /usr/share/applications || true
|
||||
fi
|
||||
|
||||
# Update GTK icon cache for hicolor theme if utility exists
|
||||
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||
if [ -d /usr/share/icons/hicolor ]; then
|
||||
gtk-update-icon-cache -t -f /usr/share/icons/hicolor || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Ensure installed binary is executable
|
||||
if [ -f /usr/local/bin/android-file-handler ]; then
|
||||
chmod 0755 /usr/local/bin/android-file-handler || true
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -43,5 +43,4 @@ exe = EXE(
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
icon='icon_media/robot_files_256.png',
|
||||
)
|
||||
@@ -4,7 +4,7 @@ a = Analysis(
|
||||
['src/main.py'],
|
||||
pathex=['src'],
|
||||
binaries=[],
|
||||
datas=[('src/gui', 'gui')],
|
||||
datas=[('src/gui', 'gui'), ('../scripts/windows/first_run_install.ps1', 'scripts/windows')],
|
||||
hiddenimports=[
|
||||
'gui',
|
||||
'gui.file_browser',
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
param(
|
||||
[string]$ExePath = "$PSScriptRoot\..\..\dist\android-file-handler.exe",
|
||||
[string]$IconPath = "$PSScriptRoot\..\..\assets\icons\android-file-handler.ico",
|
||||
[string]$AppName = "Android File Handler"
|
||||
)
|
||||
|
||||
function Ensure-Elevated {
|
||||
if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
||||
# Relaunch the script with elevation
|
||||
$psi = New-Object System.Diagnostics.ProcessStartInfo
|
||||
$psi.FileName = "powershell.exe"
|
||||
$psi.Arguments = "-ExecutionPolicy Bypass -File `"$PSCommandPath`""
|
||||
$psi.Verb = "runas"
|
||||
try {
|
||||
[System.Diagnostics.Process]::Start($psi) | Out-Null
|
||||
Exit 0
|
||||
} catch {
|
||||
Write-Error "Elevation required to install to Program Files."
|
||||
Exit 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ensure-Elevated
|
||||
|
||||
$destDir = Join-Path ${env:ProgramFiles} $AppName
|
||||
if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir | Out-Null }
|
||||
|
||||
$resolvedExe = Resolve-Path -Path $ExePath -ErrorAction SilentlyContinue
|
||||
if (-not $resolvedExe) {
|
||||
Write-Error "Application executable not found at $ExePath"
|
||||
Exit 1
|
||||
}
|
||||
|
||||
Copy-Item -Path $resolvedExe -Destination (Join-Path $destDir (Split-Path $resolvedExe -Leaf)) -Force
|
||||
|
||||
# Create Start Menu shortcut
|
||||
$programs = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
|
||||
$appFolder = Join-Path $programs $AppName
|
||||
if (-not (Test-Path $appFolder)) { New-Item -ItemType Directory -Path $appFolder | Out-Null }
|
||||
|
||||
$shortcutPath = Join-Path $appFolder "$AppName.lnk"
|
||||
$wsh = New-Object -ComObject WScript.Shell
|
||||
$sc = $wsh.CreateShortcut($shortcutPath)
|
||||
$sc.TargetPath = (Join-Path $destDir (Split-Path $resolvedExe -Leaf))
|
||||
$sc.WorkingDirectory = $destDir
|
||||
if (Test-Path $IconPath) { $sc.IconLocation = Resolve-Path $IconPath }
|
||||
$sc.Save()
|
||||
|
||||
Write-Output "Installed $AppName to $destDir and created Start Menu shortcut."
|
||||
Exit 0
|
||||
@@ -9,6 +9,9 @@ import tkinter as tk
|
||||
from tkinter import messagebox, scrolledtext
|
||||
import tempfile
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
def get_license_file_path() -> str:
|
||||
@@ -104,12 +107,48 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE."""
|
||||
|
||||
|
||||
def resource_path(relative_path: str) -> str:
|
||||
"""Return absolute path to resource for dev and frozen runs."""
|
||||
try:
|
||||
if getattr(sys, "frozen", False):
|
||||
base = getattr(sys, "_MEIPASS", os.path.dirname(sys.executable))
|
||||
else:
|
||||
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
return os.path.normpath(os.path.join(base, relative_path))
|
||||
except Exception:
|
||||
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), relative_path))
|
||||
|
||||
|
||||
def run_windows_first_run_if_needed() -> None:
|
||||
"""If running on Windows and license not agreed, launch first-run installer script.
|
||||
|
||||
Uses `resource_path` to locate the bundled PowerShell script in both dev and frozen modes.
|
||||
"""
|
||||
try:
|
||||
if not sys.platform.startswith("win"):
|
||||
return
|
||||
|
||||
if check_license_agreement():
|
||||
return
|
||||
|
||||
script_rel = os.path.join("scripts", "windows", "first_run_install.ps1")
|
||||
script_path = resource_path(script_rel)
|
||||
if not os.path.exists(script_path):
|
||||
return
|
||||
try:
|
||||
subprocess.Popen(["powershell.exe", "-ExecutionPolicy", "Bypass", "-File", script_path], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class LicenseAgreementFrame(tk.Frame):
|
||||
"""License agreement UI frame that can be embedded in the main window."""
|
||||
|
||||
|
||||
def __init__(self, parent, on_agree_callback):
|
||||
"""Initialize the license agreement frame.
|
||||
|
||||
|
||||
Args:
|
||||
parent: Parent widget
|
||||
on_agree_callback: Function to call when user agrees to license
|
||||
|
||||
@@ -5,7 +5,9 @@ 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
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_windows_first_run_if_needed()
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user