<# DataGuard Pro Bootstrap Installer - Downloads & installs Python if missing - Writes full DataGuard Pro GUI installer script to Desktop - Runs the GUI with pythonw.exe (no console) #> $ErrorActionPreference = "Stop" #───────────────────────────────────────────── # CONFIG #───────────────────────────────────────────── $PythonUrl = "https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe" $PythonExe = "$env:LOCALAPPDATA\Programs\Python\Python311\python.exe" $PythonwExe = "$env:LOCALAPPDATA\Programs\Python\Python311\pythonw.exe" $TempExe = "$env:TEMP\python-installer.exe" $ScriptPath = "$env:USERPROFILE\Desktop\DataGuardProInstaller.pyw" Write-Host "`n=== DataGuard Pro Bootstrap ===`n" #───────────────────────────────────────────── # 1. Ensure Python installed #───────────────────────────────────────────── if (-Not (Test-Path $PythonExe)) { Write-Host "Python not found. Downloading..." Invoke-WebRequest -Uri $PythonUrl -OutFile $TempExe -UseBasicParsing Write-Host "Installing Python silently..." Start-Process -FilePath $TempExe -ArgumentList "/quiet InstallAllUsers=0 PrependPath=1 Include_test=0" -Wait Remove-Item $TempExe -Force } else { Write-Host "Python already installed at: $PythonExe" } if (-Not (Test-Path $PythonExe)) { Write-Host "❌ Python installation failed. Exiting." exit 1 } #───────────────────────────────────────────── # 2. Write DataGuard Pro GUI script to Desktop #───────────────────────────────────────────── $ScriptContent = @' # -*- coding: utf-8 -*- # -*- coding: utf-8 -*- """ DataGuard Pro — Device Owner Helper (Windows GUI, Light Metallic Theme) Features - Pure Tkinter GUI (no console): save as .pyw to run with pythonw.exe - Light metallic aesthetic + blue accents - Displays DataGuard Pro logo (WEBP/PNG/JPG) if Pillow is available - Step-by-step flow: 1) Ask user to remove all accounts and reboot phone (precondition) 2) Download & install Android Platform Tools (ADB) to %USERPROFILE%\Android\platform-tools 3) Guide to enable Developer Options + USB debugging 4) Start ADB, wait for connected & authorized device 5) Run device-owner command: adb shell dpm set-device-owner "com.dataguard.pro/.MyDeviceAdminReceiver" 6) Final advice: turn off USB debugging - Writes a log to Desktop: DataGuard_Installer.log - Hidden subprocess windows (no extra terminal popups) - Standard library only REQUIRED; Pillow is OPTIONAL for logo Author: DataGuard Pro """ import os import sys import ssl import io import re import time import zipfile import shutil import ctypes import threading import subprocess import urllib.request from pathlib import Path import tkinter as tk from tkinter import ttk, messagebox # ─────────────────────────────────────────────────────────────────────────────── # Configuration # ─────────────────────────────────────────────────────────────────────────────── PLATFORM_TOOLS_URL = "https://dl.google.com/android/repository/platform-tools-latest-windows.zip" ADMIN_COMPONENT = 'com.dataguard.pro/.MyDeviceAdminReceiver' USERPROFILE = os.environ.get("USERPROFILE", str(Path.home())) TARGET_ROOT = os.path.join(USERPROFILE, "Android") TARGET_DIR = os.path.join(TARGET_ROOT, "platform-tools") ZIP_PATH = os.path.join(TARGET_ROOT, "platform-tools-latest-windows.zip") LOG_PATH = os.path.join(USERPROFILE, "Desktop", "DataGuard_Installer.log") CREATE_NO_WINDOW = 0x08000000 # Hide subprocess console windows on Windows # Force TLS1.2 on older Windows for HTTPS downloads try: ssl._create_default_https_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) except Exception: pass # Attempt Pillow import for WEBP/PNG/JPG logo support PIL_OK = False try: from PIL import Image, ImageTk # type: ignore PIL_OK = True except Exception: PIL_OK = False # ─────────────────────────────────────────────────────────────────────────────── # Utilities # ─────────────────────────────────────────────────────────────────────────────── def is_windows() -> bool: return os.name == "nt" def ensure_windows_or_exit(): if not is_windows(): messagebox.showerror("Unsupported OS", "This installer is for Windows only.") sys.exit(1) def write_log(msg: str): line = f"[DataGuard] {msg}" try: with open(LOG_PATH, "a", encoding="utf-8") as f: f.write(line + "\n") except Exception: pass def safe_mkdir(path: str): os.makedirs(path, exist_ok=True) def download_with_progress(url: str, out_path: str, progress_cb=None, status_cb=None): def hook(blocknum, blocksize, totalsize): try: if totalsize <= 0: if progress_cb: progress_cb(0) return downloaded = blocknum * blocksize if downloaded > totalsize: downloaded = totalsize pct = int(downloaded * 100 / totalsize) if progress_cb: progress_cb(pct) except Exception: pass try: if status_cb: status_cb("Downloading Platform Tools…") urllib.request.urlretrieve(url, out_path, hook) if progress_cb: progress_cb(100) except Exception as e: write_log(f"Download error: {e}") raise def extract_platform_tools(zip_path: str, dest_root: str, status_cb=None): if status_cb: status_cb("Extracting Platform Tools…") temp_dir = os.path.join(dest_root, "_pt_unzip_tmp") try: if os.path.exists(temp_dir): shutil.rmtree(temp_dir, ignore_errors=True) os.makedirs(temp_dir, exist_ok=True) with zipfile.ZipFile(zip_path, 'r') as zf: zf.extractall(temp_dir) extracted_pt = os.path.join(temp_dir, "platform-tools") if not os.path.isdir(extracted_pt): raise RuntimeError("Zip missing 'platform-tools' folder.") if os.path.exists(TARGET_DIR): shutil.rmtree(TARGET_DIR, ignore_errors=True) shutil.move(extracted_pt, TARGET_DIR) finally: try: shutil.rmtree(temp_dir, ignore_errors=True) except Exception: pass try: os.remove(zip_path) except Exception: pass def adb_path() -> str: exe = os.path.join(TARGET_DIR, "adb.exe") if not os.path.isfile(exe): raise FileNotFoundError(f"adb.exe not found at: {exe}") return exe def run_proc(args, timeout=240): write_log(f"RUN: {' '.join(args)}") try: p = subprocess.run( args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=timeout, creationflags=CREATE_NO_WINDOW, text=True, encoding="utf-8", errors="replace" ) out = (p.stdout or "") err = (p.stderr or "") if err.strip(): write_log(f"STDERR: {err.strip()}") if out.strip(): write_log(f"STDOUT: {out.strip()}") return p.returncode, out, err except subprocess.TimeoutExpired: write_log("Command timed out") return 1, "", "Timeout" def adb_cmd(*args, timeout=240): exe = adb_path() return run_proc([exe] + list(args), timeout=timeout) def start_adb_and_list(): adb_cmd("kill-server") adb_cmd("start-server") time.sleep(0.8) return adb_cmd("devices") def devices_authorized(devices_text: str) -> bool: if "unauthorized" in devices_text.lower(): return False return bool(re.search(r"^[A-Za-z0-9\.\-:_]+\tdevice\s*$", devices_text, re.M)) def elevate_if_user_agrees(root: tk.Tk): try: is_admin = ctypes.windll.shell32.IsUserAnAdmin() except Exception: is_admin = False if is_admin: return ans = messagebox.askyesno( "Administrator Permission", "Administrator rights can improve reliability for some steps.\n\n" "Do you want to re-launch this installer as Administrator?" ) if not ans: return params = ' '.join([f'"{arg}"' for arg in sys.argv]) try: ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, params, None, 1) root.destroy() sys.exit(0) except Exception: messagebox.showwarning("Elevation Failed", "Continuing without Administrator privileges.") return # ─────────────────────────────────────────────────────────────────────────────── # Themed GUI # ─────────────────────────────────────────────────────────────────────────────── class MetallicTheme: """ Light metallic theme: silver base with steel/blue accents. """ BG = "#eef1f5" # base window background (very light silver) PANEL = "#f6f8fb" # cards / panels EDGE = "#e1e5ea" # borders TEXT = "#1d2a35" # primary text MUTED = "#4b5b6a" # secondary text ACCENT = "#2b6cb0" # blue accent ACCENT2 = "#5aa0ff" # lighter blue accent BTN = "#f2f6fb" BTN_HI = "#e7effa" @staticmethod def apply(style: ttk.Style): style.theme_use('clam') # overall style.configure('.', background=MetallicTheme.BG, foreground=MetallicTheme.TEXT, font=("Segoe UI", 10)) # frames / labels style.configure('Card.TFrame', background=MetallicTheme.PANEL, relief='groove', borderwidth=1) style.configure('Header.TLabel', font=("Segoe UI Semibold", 15), background=MetallicTheme.BG, foreground=MetallicTheme.TEXT) style.configure('Sub.TLabel', font=("Segoe UI", 9), foreground=MetallicTheme.MUTED, background=MetallicTheme.BG) style.configure('Body.TLabel', background=MetallicTheme.PANEL, foreground=MetallicTheme.TEXT) # buttons style.configure('TButton', background=MetallicTheme.BTN, foreground=MetallicTheme.TEXT, bordercolor=MetallicTheme.EDGE, focusthickness=2, focuscolor=MetallicTheme.ACCENT, padding=8) style.map('TButton', background=[('active', MetallicTheme.BTN_HI)], relief=[('pressed', 'sunken'), ('!pressed', 'raised')]) style.configure('Accent.TButton', background=MetallicTheme.ACCENT, foreground="#ffffff", padding=9, bordercolor=MetallicTheme.ACCENT) style.map('Accent.TButton', background=[('active', MetallicTheme.ACCENT2)], relief=[('pressed', 'sunken'), ('!pressed', 'raised')]) # progressbar style.configure('Blue.Horizontal.TProgressbar', troughcolor=MetallicTheme.EDGE, background=MetallicTheme.ACCENT) # status bar style.configure('Status.TLabel', background=MetallicTheme.PANEL, foreground=MetallicTheme.MUTED, relief='groove', anchor='w') # ─────────────────────────────────────────────────────────────────────────────── # Main Application # ─────────────────────────────────────────────────────────────────────────────── class InstallerGUI(tk.Tk): def __init__(self): super().__init__() self.title("DataGuard Pro — ADB Helper") self.geometry("860x640") self.minsize(820, 600) self.configure(background=MetallicTheme.BG) # Apply theme self.style = ttk.Style(self) MetallicTheme.apply(self.style) # Top header area with optional logo and gradient bar self._build_header() self._build_body() self._build_footer() # State / steps self.step_index = 0 self.steps = [ self.step_accounts_info, self.step_download_install_adb, self.step_enable_dev_usb, self.step_wait_for_device, self.step_set_device_owner, self.step_final ] # Init display ensure_windows_or_exit() safe_mkdir(os.path.dirname(LOG_PATH)) self.write_text( "Welcome to the DataGuard Pro Device Owner helper.\n\n" "Click Start to begin.\n\n" f"Log file will be written to:\n {LOG_PATH}\n" ) # Offer optional elevation self.after(350, lambda: elevate_if_user_agrees(self)) # ───── Header with Logo ───── def _build_header(self): header = ttk.Frame(self, style='Card.TFrame') header.pack(fill="x", padx=16, pady=(14, 8)) # Left: logo (if available) self.logo_label = ttk.Label(header) self.logo_label.pack(side="left", padx=(12, 16), pady=10) self._load_logo_into(self.logo_label) # Right: title + subtitle title_frame = ttk.Frame(header, style='Card.TFrame') title_frame.pack(side="left", fill="x", expand=True, pady=10) hdr = ttk.Label(title_frame, text="DataGuard Pro — Device Owner Helper", style="Header.TLabel") hdr.pack(anchor="w") sub = ttk.Label( title_frame, text="Light metallic interface • Secure ADB setup • Guided provisioning", style="Sub.TLabel" ) sub.pack(anchor="w", pady=(2, 0)) # Gradient accent bar under header accent = tk.Canvas(self, height=4, highlightthickness=0, bg=MetallicTheme.BG, bd=0) accent.pack(fill="x", padx=16) # draw gradient strip self._draw_gradient(accent, MetallicTheme.ACCENT2, MetallicTheme.ACCENT) def _draw_gradient(self, canvas: tk.Canvas, c1: str, c2: str): width = self.winfo_reqwidth() steps = 80 for i in range(steps): r = i / (steps - 1) color = self._blend_hex(c1, c2, r) canvas.create_line(i * (width/steps), 0, i * (width/steps), 4, fill=color) @staticmethod def _blend_hex(h1, h2, t): def hex_to_rgb(h): h = h.lstrip("#") return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) def rgb_to_hex(r,g,b): return f"#{r:02x}{g:02x}{b:02x}" r1,g1,b1 = hex_to_rgb(h1); r2,g2,b2 = hex_to_rgb(h2) r = int(r1 + (r2 - r1) * t) g = int(g1 + (g2 - g1) * t) b = int(b1 + (b2 - b1) * t) return rgb_to_hex(r,g,b) def _candidate_logo_paths(self): here = Path(__file__).resolve().parent candidates = [ here / "datalogo.webp", here / "datalogo.png", here / "datalogo.jpg", Path("/mnt/data/datalogo.webp"), # fallback for known container path ] return [str(p) for p in candidates if p.exists()] def _load_logo_into(self, label: ttk.Label): self.logo_img_ref = None paths = self._candidate_logo_paths() if not paths or not PIL_OK: # fallback: styled text badge label.configure(text="DATA GUARD PRO", style="Header.TLabel") return try: img = Image.open(paths[0]).convert("RGBA") # resize to a nice height while preserving aspect ratio target_h = 88 w, h = img.size scale = target_h / float(h) target_w = max(88, int(w * scale)) img = img.resize((target_w, target_h), Image.LANCZOS) # Put on a soft white glow glow = Image.new("RGBA", (target_w+20, target_h+20), (0,0,0,0)) glow.paste(img, (10,10), img) self.logo_img_ref = ImageTk.PhotoImage(glow) label.configure(image=self.logo_img_ref) except Exception: label.configure(text="DATA GUARD PRO", style="Header.TLabel") # ───── Body / Content ───── def _build_body(self): # Main card card = ttk.Frame(self, style="Card.TFrame") card.pack(fill="both", expand=True, padx=16, pady=10) # Text area with custom scrollbar text_wrap = ttk.Frame(card, style="Card.TFrame") text_wrap.pack(fill="both", expand=True, padx=10, pady=10) self.text = tk.Text(text_wrap, height=16, wrap="word", bg=MetallicTheme.PANEL, fg=MetallicTheme.TEXT, relief="flat", font=("Segoe UI", 10), insertbackground=MetallicTheme.TEXT) self.text.configure(state="disabled") self.text.pack(side="left", fill="both", expand=True) s = ttk.Scrollbar(text_wrap, orient="vertical", command=self.text.yview) s.pack(side="right", fill="y") self.text.configure(yscrollcommand=s.set) # Progress + status prog_wrap = ttk.Frame(card, style="Card.TFrame") prog_wrap.pack(fill="x", padx=10, pady=(0, 10)) self.progress = ttk.Progressbar(prog_wrap, orient="horizontal", mode="determinate", length=680, style="Blue.Horizontal.TProgressbar") self.progress.pack(fill="x", pady=(4, 6)) self.status_var = tk.StringVar(value="Ready.") self.status = ttk.Label(prog_wrap, textvariable=self.status_var, style="Status.TLabel") self.status.pack(fill="x") # Buttons btns = ttk.Frame(self, style="Card.TFrame") btns.pack(fill="x", padx=16, pady=(2, 16)) self.back_btn = ttk.Button(btns, text="Back", command=self.on_back, state="disabled") self.back_btn.pack(side="left", padx=6) self.next_btn = ttk.Button(btns, text="Start", command=self.on_next, style="Accent.TButton") self.next_btn.pack(side="left", padx=6) self.quit_btn = ttk.Button(btns, text="Quit", command=self.on_quit) self.quit_btn.pack(side="right", padx=6) def _build_footer(self): # Simple copyright or tagline foot = ttk.Label(self, text="© DataGuard Pro", style="Sub.TLabel") foot.pack(side="bottom", pady=(0, 8)) # ───── UI Helpers ───── def write_text(self, s: str, clear=False): self.text.configure(state="normal") if clear: self.text.delete("1.0", "end") self.text.insert("end", s + ("" if s.endswith("\n") else "\n")) self.text.see("end") self.text.configure(state="disabled") def set_status(self, s: str): self.status_var.set(s) self.status.update_idletasks() write_log(s) def set_progress(self, value: int): self.progress["value"] = max(0, min(100, value)) self.progress.update_idletasks() def disable_buttons(self): self.back_btn.configure(state="disabled") self.next_btn.configure(state="disabled") self.quit_btn.configure(state="disabled") def enable_buttons(self, back=True, next=True, quit=True, next_text=None): self.back_btn.configure(state=("normal" if back else "disabled")) self.next_btn.configure(state=("normal" if next else "disabled")) if next_text: self.next_btn.configure(text=next_text) self.quit_btn.configure(state=("normal" if quit else "disabled")) # ───── Navigation ───── def on_back(self): if self.step_index > 0: self.step_index -= 1 self.run_step() def on_next(self): self.run_step() def on_quit(self): self.destroy() def run_step(self): self.set_progress(0) self.text.configure(state="normal") self.text.delete("1.0", "end") self.text.configure(state="disabled") self.disable_buttons() try: step_fn = self.steps[self.step_index] except IndexError: self.enable_buttons(back=False, next=False, quit=True) return step_fn() def step_done(self, success: bool): if not success: # allow retry or back self.enable_buttons(back=(self.step_index > 0), next=True, quit=True, next_text="Retry") return # success -> advance self.step_index += 1 if self.step_index >= len(self.steps): self.enable_buttons(back=False, next=False, quit=True) else: self.enable_buttons(back=(self.step_index > 0), next=True, quit=True, next_text="Next") # ───── Steps ───── def step_accounts_info(self): self.write_text( "STEP 1 — Remove ALL accounts & RESTART the phone\n\n" "On your Android device:\n" " • Settings → Accounts (or Passwords & accounts)\n" " • Remove EVERY account\n" " • RESTART the phone\n\n" "This greatly increases the chance that Device Owner activation will succeed.\n\n" "When done, click Next.", clear=True ) self.set_status("Waiting for confirmation that accounts were removed and phone was restarted.") self.step_done(success=True) def step_download_install_adb(self): already = os.path.isdir(TARGET_DIR) and os.path.isfile(os.path.join(TARGET_DIR, "adb.exe")) if already: reuse = messagebox.askyesno( "Platform Tools Found", f"Platform Tools already exist here:\n{TARGET_DIR}\n\nUse the existing files?" ) if reuse: self.set_status("Using existing Platform Tools.") self.write_text(f"Using existing Platform Tools at:\n {TARGET_DIR}", clear=True) self.step_done(success=True) return self.write_text( "STEP 2 — Download & install Android Platform Tools (ADB)\n\n" "Files will be installed to:\n" f" {TARGET_DIR}\n\n" "Please wait...", clear=True ) def work(): try: safe_mkdir(TARGET_ROOT) self.set_progress(0) download_with_progress( PLATFORM_TOOLS_URL, ZIP_PATH, progress_cb=lambda p: self.after(0, self.set_progress, p), status_cb=lambda s: self.after(0, self.set_status, s) ) extract_platform_tools(ZIP_PATH, TARGET_ROOT, status_cb=lambda s: self.after(0, self.set_status, s)) self.after(0, self.write_text, f"Platform Tools installed to:\n {TARGET_DIR}\n", False) self.after(0, self.set_progress, 100) self.after(0, self.step_done, True) except Exception as e: self.after(0, self.write_text, f"Error: {e}", False) self.after(0, self.set_status, "Failed to install Platform Tools.") self.after(0, self.step_done, False) threading.Thread(target=work, daemon=True).start() def step_enable_dev_usb(self): self.write_text( "STEP 3 — Enable Developer Options & USB debugging\n\n" "On your Android device:\n" " 1) Settings → About phone → Software information\n" " 2) Tap 'Build number' 7 times (enter PIN if asked) to enable Developer Options\n" " 3) Go back → Settings → Developer options\n" " 4) Turn ON 'USB debugging'\n\n" "Connect the phone via USB, then click Next.", clear=True ) self.set_status("Waiting for USB debugging to be enabled and device connected.") self.step_done(success=True) def step_wait_for_device(self): self.write_text( "STEP 4 — Connect & authorize device\n\n" "We will start the ADB server and look for your device.\n" "If a prompt appears on your phone asking to 'Allow USB debugging', tap 'Allow'.\n\n" "Please wait...", clear=True ) def work(): try: _ = adb_path() # ensure exists rc, out, err = start_adb_and_list() devices_txt = (out or "") + "\n" + (err or "") if "unauthorized" in devices_txt.lower(): self.after(0, self.write_text, "Your device shows as UNAUTHORIZED.\n" "Unplug/replug and accept the USB debugging prompt on the phone,\n" "then click Retry.", False) self.after(0, self.set_status, "Device unauthorized. Awaiting user action.") self.after(0, self.step_done, False) return if not devices_authorized(devices_txt): self.after(0, self.write_text, "No authorized device detected.\n" "Make sure the phone is connected via USB and USB debugging is ON,\n" "accept any prompts on the phone, then click Retry.", False) self.after(0, self.set_status, "No authorized device found.") self.after(0, self.step_done, False) return # wait for device to be fully ready adb_cmd("wait-for-device") self.after(0, self.write_text, "Device connected and authorized.\n", False) self.after(0, self.set_progress, 100) self.after(0, self.set_status, "Device ready.") self.after(0, self.step_done, True) except Exception as e: self.after(0, self.write_text, f"Error: {e}", False) self.after(0, self.set_status, "Failed to detect device.") self.after(0, self.step_done, False) threading.Thread(target=work, daemon=True).start() def step_set_device_owner(self): self.write_text( "STEP 5 — Set DataGuard Pro as Device Owner (best effort)\n\n" f"Running:\n adb shell dpm set-device-owner \"{ADMIN_COMPONENT}\"\n\n" "This usually succeeds only when NO accounts exist on the device and it was recently restarted.\n" "Please wait...", clear=True ) self.set_status("Attempting to set Device Owner…") def work(): try: rc, out, err = adb_cmd("shell", "dpm", "set-device-owner", ADMIN_COMPONENT, timeout=240) combined = (out or "") + "\n" + (err or "") success = (rc == 0) or ("Success" in combined) if success: msg = "SUCCESS: DataGuard Pro was set as Device Owner." self.after(0, self.write_text, msg + "\n", False) self.after(0, self.set_progress, 100) self.after(0, self.set_status, "Device Owner set successfully.") self.after(0, self.step_done, True) else: self.after(0, self.write_text, "FAILED to set Device Owner.\n" "Common reasons:\n" " • An account is still present on the device\n" " • Another device owner/admin is already active\n" " • OEM/Android version blocks this path on provisioned devices\n\n" "You can remove all accounts, restart the phone, and try again.", False) self.after(0, self.set_status, "Device Owner attempt failed.") self.after(0, self.step_done, False) except Exception as e: self.after(0, self.write_text, f"Error: {e}", False) self.after(0, self.set_status, "Error while setting Device Owner.") self.after(0, self.step_done, False) threading.Thread(target=work, daemon=True).start() def step_final(self): self.write_text( "FINAL — Security cleanup\n\n" "• You can now TURN OFF 'USB debugging' in Developer Options.\n" "• You may disconnect the USB cable.\n\n" f"Log saved to:\n {LOG_PATH}\n\n" "Click Quit to close.", clear=True ) self.set_status("Completed.") self.enable_buttons(back=False, next=False, quit=True) # ─────────────────────────────────────────────────────────────────────────────── # Main # ─────────────────────────────────────────────────────────────────────────────── def main(): ensure_windows_or_exit() app = InstallerGUI() app.mainloop() if __name__ == "__main__": main() '@ Write-Host "Writing DataGuard Pro script to: $ScriptPath" Set-Content -Path $ScriptPath -Value $ScriptContent -Encoding UTF8 -Force #───────────────────────────────────────────── # 3. Run it with pythonw.exe (no console) #───────────────────────────────────────────── Write-Host "Launching GUI installer..." Start-Process -FilePath $PythonwExe -ArgumentList "`"$ScriptPath`"" Write-Host "`n✅ DataGuard Pro Installer launched. Close this window."