#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Android Development Environment Setup Script for Windows MyMemoApp - Full Android Development Environment Setup Date: 2025-09-14 Run with Administrator privileges """ import os import sys import subprocess import shutil import zipfile import urllib.request import winreg import ctypes from pathlib import Path import json import time # Configuration JAVA_VERSION = "17" GRADLE_VERSION = "9.0.0" ANDROID_SDK_VERSION = "13114758" ANDROID_API_LEVEL = "34" BUILD_TOOLS_VERSION = "34.0.0" NDK_VERSIONS = ["25.1.8937393", "27.1.12297006"] CMAKE_VERSIONS = ["3.22.1", "3.18.1"] # Paths JAVA_INSTALL_PATH = r"C:\Program Files\Java" GRADLE_INSTALL_PATH = r"C:\Gradle" ANDROID_SDK_PATH = r"C:\Android\sdk" TEMP_DIR = Path(os.environ['TEMP']) / "android-setup" # URLs JAVA_17_URL = "https://aka.ms/download-jdk/microsoft-jdk-17.0.12-windows-x64.msi" JAVA_21_URL = "https://aka.ms/download-jdk/microsoft-jdk-21.0.4-windows-x64.msi" GRADLE_URL = f"https://services.gradle.org/distributions/gradle-{GRADLE_VERSION}-bin.zip" ANDROID_SDK_URL = f"https://dl.google.com/android/repository/commandlinetools-win-{ANDROID_SDK_VERSION}_latest.zip" class AndroidSetup: def __init__(self): self.errors = [] self.warnings = [] def print_header(self, text): """Print section header""" print("\n" + "="*60) print(f" {text}") print("="*60) def print_step(self, step_num, text): """Print step information""" print(f"\n[Step {step_num}] {text}") print("-" * 50) def is_admin(self): """Check if running with administrator privileges""" try: return ctypes.windll.shell32.IsUserAnAdmin() except: return False def run_command(self, cmd, shell=True, capture_output=True): """Run a command and return result""" try: # On Windows, use shell=True for better command resolution if isinstance(cmd, list): cmd = ' '.join(cmd) result = subprocess.run(cmd, shell=shell, capture_output=capture_output, text=True) return result.returncode == 0, result.stdout, result.stderr except Exception as e: return False, "", str(e) def download_file(self, url, destination, description=""): """Download file with progress indicator""" print(f"Downloading {description}...") print(f"URL: {url}") try: def download_progress(block_num, block_size, total_size): downloaded = block_num * block_size percent = min(downloaded * 100 / total_size, 100) progress_bar = '#' * int(percent // 2) sys.stdout.write(f'\r[{progress_bar:<50}] {percent:.1f}%') sys.stdout.flush() urllib.request.urlretrieve(url, destination, reporthook=download_progress) print() # New line after progress bar return True except Exception as e: print(f"\nDownload failed: {e}") return False def extract_zip(self, zip_path, extract_to): """Extract zip file""" print(f"Extracting {zip_path.name}...") try: with zipfile.ZipFile(zip_path, 'r') as zip_ref: zip_ref.extractall(extract_to) return True except Exception as e: print(f"Extraction failed: {e}") return False def set_environment_variable(self, name, value, user=False): """Set Windows environment variable permanently""" try: # First, update current session os.environ[name] = value print(f" Updated {name} in current session") success = False # Method 1: Direct registry modification try: if user: key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, r"Environment", 0, winreg.KEY_ALL_ACCESS) else: key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", 0, winreg.KEY_ALL_ACCESS) winreg.SetValueEx(key, name, 0, winreg.REG_EXPAND_SZ, value) winreg.CloseKey(key) print(f" ✓ Persisted {name} to system registry") success = True # Notify system of environment change (optional) try: import win32gui, win32con win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment') except ImportError: # pywin32 not installed, skip notification pass except Exception as e: print(f" Warning: Registry method failed: {e}") # Method 2: Use setx as backup if not success: try: # Use setx to set system environment variable cmd = f'setx {name} "{value}" /M' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode == 0: print(f" ✓ Set {name} using setx") success = True else: print(f" Warning: setx failed: {result.stderr}") except Exception as e: print(f" Warning: setx method failed: {e}") if not success: print(f" ⚠ {name} will only work for current session") print(f" Please set manually: {name}={value}") return True except Exception as e: print(f" Error: Failed to set {name}: {e}") return False def add_to_path(self, new_path, user=False): """Add directory to PATH environment variable permanently""" try: # First, update current session if new_path not in os.environ['PATH']: os.environ['PATH'] = f"{os.environ['PATH']};{new_path}" print(f" Added to current session PATH: {new_path}") else: print(f" Already in current session PATH: {new_path}") # Persist to system PATH using both registry and setx success = False # Method 1: Direct registry modification try: if user: key_path = r"Environment" root_key = winreg.HKEY_CURRENT_USER else: key_path = r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment" root_key = winreg.HKEY_LOCAL_MACHINE key = winreg.OpenKey(root_key, key_path, 0, winreg.KEY_ALL_ACCESS) try: current_path, reg_type = winreg.QueryValueEx(key, "Path") except: current_path = "" reg_type = winreg.REG_EXPAND_SZ if new_path not in current_path: # Clean up the path (remove duplicates and empty entries) path_parts = [p.strip() for p in current_path.split(';') if p.strip()] if new_path not in path_parts: path_parts.append(new_path) new_path_value = ';'.join(path_parts) winreg.SetValueEx(key, "Path", 0, reg_type, new_path_value) print(f" ✓ Persisted to system registry PATH") success = True else: print(f" Already in system registry PATH") success = True winreg.CloseKey(key) except Exception as e: print(f" Warning: Registry method failed: {e}") # Method 2: Use setx as backup (this has 1024 char limit but works well) if not success: try: # Use setx to set system PATH cmd = f'setx PATH "%PATH%;{new_path}" /M' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode == 0: print(f" ✓ Updated system PATH using setx") success = True else: print(f" Warning: setx failed: {result.stderr}") except Exception as e: print(f" Warning: setx method failed: {e}") if not success: print(f" ⚠ PATH update will only work for current session") print(f" Please add manually to system PATH: {new_path}") return True except Exception as e: print(f" Error: Failed to update PATH: {e}") return False def check_nodejs(self): """Check Node.js installation""" self.print_step(1, "Checking Node.js and npm") # Check Node.js success, stdout, _ = self.run_command("node --version") if success: print(f"✓ Node.js installed: {stdout.strip()}") else: print("✗ Node.js not installed") print("Please download and install from: https://nodejs.org/") return False # Check npm - try different approaches npm_found = False npm_version = "" # Try 1: Direct npm command success, stdout, _ = self.run_command("npm --version") if success: npm_found = True npm_version = stdout.strip() else: # Try 2: Use cmd /c for Windows success, stdout, _ = self.run_command("cmd /c npm --version") if success: npm_found = True npm_version = stdout.strip() else: # Try 3: Use full path if Node.js is in Program Files node_path = shutil.which('node') if node_path: npm_path = Path(node_path).parent / "npm.cmd" if npm_path.exists(): success, stdout, _ = self.run_command(f'"{npm_path}" --version') if success: npm_found = True npm_version = stdout.strip() if npm_found: print(f"✓ npm installed: {npm_version}") return True else: print("✗ npm not found") print("Note: npm should be installed with Node.js") print("Try running 'npm --version' manually to verify") response = input("\nContinue anyway? (y/n): ") return response.lower() == 'y' def check_java(self): """Check and install Java if needed""" self.print_step(2, "Checking Java") success, stdout, _ = self.run_command("java --version") if success: print(f"✓ Java installed:\n{stdout}") # Set JAVA_HOME if not set if not os.environ.get('JAVA_HOME'): # Try to find Java installation java_path = shutil.which('java') if java_path: java_home = Path(java_path).parent.parent self.set_environment_variable('JAVA_HOME', str(java_home)) print(f"✓ JAVA_HOME set to: {java_home}") else: print(f"✓ JAVA_HOME: {os.environ['JAVA_HOME']}") return True print("Java not found. Installing...") # Create temp directory TEMP_DIR.mkdir(exist_ok=True) os.chdir(TEMP_DIR) # Download Java java_msi = TEMP_DIR / "openjdk-17.msi" if not java_msi.exists(): if not self.download_file(JAVA_17_URL, java_msi, "OpenJDK 17"): # Try Java 21 as fallback java_msi = TEMP_DIR / "openjdk-21.msi" if not self.download_file(JAVA_21_URL, java_msi, "OpenJDK 21"): self.errors.append("Failed to download Java") return False # Install Java print("Installing Java... (Please complete the installation wizard)") subprocess.run(f'msiexec /i "{java_msi}"', shell=True) print("\n⚠ Please complete Java installation manually, then press Enter to continue...") input() # Verify installation success, stdout, _ = self.run_command("java --version") if success: print(f"✓ Java installed successfully") return True else: self.warnings.append("Java installation may require system restart") return False def install_gradle(self): """Install Gradle""" self.print_step(3, "Installing Gradle") # Check if already installed gradle_exe = Path(GRADLE_INSTALL_PATH) / f"gradle-{GRADLE_VERSION}" / "bin" / "gradle.bat" if gradle_exe.exists(): print(f"✓ Gradle already installed at: {gradle_exe.parent.parent}") self.add_to_path(str(gradle_exe.parent)) return True success, stdout, _ = self.run_command("gradle --version") if success: print(f"✓ Gradle found in PATH") return True print("Installing Gradle...") # Create temp directory TEMP_DIR.mkdir(exist_ok=True) os.chdir(TEMP_DIR) # Download Gradle gradle_zip = TEMP_DIR / f"gradle-{GRADLE_VERSION}-bin.zip" if not gradle_zip.exists(): if not self.download_file(GRADLE_URL, gradle_zip, f"Gradle {GRADLE_VERSION}"): self.errors.append("Failed to download Gradle") return False # Extract Gradle if not self.extract_zip(gradle_zip, TEMP_DIR): self.errors.append("Failed to extract Gradle") return False # Move to installation directory gradle_src = TEMP_DIR / f"gradle-{GRADLE_VERSION}" gradle_dst = Path(GRADLE_INSTALL_PATH) / f"gradle-{GRADLE_VERSION}" print(f"Installing Gradle to {gradle_dst}...") gradle_dst.parent.mkdir(exist_ok=True) if gradle_dst.exists(): shutil.rmtree(gradle_dst) shutil.move(str(gradle_src), str(gradle_dst)) # Add to PATH gradle_bin = gradle_dst / "bin" self.add_to_path(str(gradle_bin)) print(f"✓ Gradle {GRADLE_VERSION} installed successfully") return True def install_android_sdk(self): """Install Android SDK""" self.print_step(4, "Installing Android SDK") # Check if already installed sdkmanager = Path(ANDROID_SDK_PATH) / "cmdline-tools" / "latest" / "bin" / "sdkmanager.bat" if sdkmanager.exists(): print(f"✓ Android SDK already installed at: {ANDROID_SDK_PATH}") else: print("Installing Android SDK Command Line Tools...") # Create temp directory TEMP_DIR.mkdir(exist_ok=True) os.chdir(TEMP_DIR) # Download Android SDK sdk_zip = TEMP_DIR / f"commandlinetools-win-{ANDROID_SDK_VERSION}_latest.zip" if not sdk_zip.exists(): if not self.download_file(ANDROID_SDK_URL, sdk_zip, "Android SDK Command Line Tools"): self.errors.append("Failed to download Android SDK") return False else: print(f"Using existing SDK zip: {sdk_zip}") # Extract SDK if not self.extract_zip(sdk_zip, TEMP_DIR): self.errors.append("Failed to extract Android SDK") return False # Setup SDK directory structure print("Setting up SDK directory structure...") sdk_path = Path(ANDROID_SDK_PATH) sdk_path.mkdir(parents=True, exist_ok=True) cmdline_tools = sdk_path / "cmdline-tools" / "latest" cmdline_tools.parent.mkdir(exist_ok=True) # Move cmdline-tools print("Moving cmdline-tools to SDK directory...") src_tools = TEMP_DIR / "cmdline-tools" if cmdline_tools.exists(): print(f"Removing existing cmdline-tools at {cmdline_tools}") shutil.rmtree(cmdline_tools) shutil.move(str(src_tools), str(cmdline_tools)) print(f"✓ Android SDK installed to: {ANDROID_SDK_PATH}") # Set environment variables print("Setting environment variables...") print(" Setting ANDROID_HOME...") if not self.set_environment_variable('ANDROID_HOME', ANDROID_SDK_PATH): self.warnings.append("Failed to set ANDROID_HOME") print(" Adding cmdline-tools to PATH...") if not self.add_to_path(str(Path(ANDROID_SDK_PATH) / "cmdline-tools" / "latest" / "bin")): self.warnings.append("Failed to add cmdline-tools to PATH") print(" Adding platform-tools to PATH...") if not self.add_to_path(str(Path(ANDROID_SDK_PATH) / "platform-tools")): self.warnings.append("Failed to add platform-tools to PATH") print("✓ Environment variables configured") return True def install_sdk_components(self): """Install Android SDK components""" self.print_step(5, "Installing Android SDK Components") sdkmanager = Path(ANDROID_SDK_PATH) / "cmdline-tools" / "latest" / "bin" / "sdkmanager.bat" if not sdkmanager.exists(): print("✗ sdkmanager not found") return False # Accept licenses print("Accepting Android SDK licenses...") license_cmd = f'echo y | "{sdkmanager}" --licenses' subprocess.run(license_cmd, shell=True, capture_output=True) # Install platform-tools first (contains adb) print("Installing platform-tools (contains adb)...") cmd = f'"{sdkmanager}" "platform-tools"' result = subprocess.run(cmd, shell=True, capture_output=True, text=True) if result.returncode == 0: print("✓ platform-tools installed") # Add adb to PATH immediately after platform-tools installation platform_tools_path = Path(ANDROID_SDK_PATH) / "platform-tools" if platform_tools_path.exists(): print("Adding adb (platform-tools) to PATH...") self.add_to_path(str(platform_tools_path)) # Verify adb is accessible adb_path = platform_tools_path / "adb.exe" if adb_path.exists(): print(f"✓ adb found at: {adb_path}") else: print("⚠ adb.exe not found in platform-tools") else: print(f"⚠ Failed to install platform-tools: {result.stderr}") # Install other components components = [ f"platforms;android-{ANDROID_API_LEVEL}", f"platforms;android-35", f"platforms;android-33", f"build-tools;{BUILD_TOOLS_VERSION}", f"build-tools;35.0.0" ] for component in components: print(f"Installing {component}...") cmd = f'"{sdkmanager}" "{component}"' subprocess.run(cmd, shell=True, capture_output=True) # Install NDK for ndk_version in NDK_VERSIONS: print(f"Installing NDK {ndk_version}...") cmd = f'"{sdkmanager}" "ndk;{ndk_version}"' subprocess.run(cmd, shell=True, capture_output=True) # Install CMake for cmake_version in CMAKE_VERSIONS: print(f"Installing CMake {cmake_version}...") cmd = f'"{sdkmanager}" "cmake;{cmake_version}"' subprocess.run(cmd, shell=True, capture_output=True) print("✓ Android SDK components installed") return True def create_local_properties(self): """Create local.properties files""" self.print_step(6, "Creating local.properties files") ndk_path = Path(ANDROID_SDK_PATH) / "ndk" / NDK_VERSIONS[-1] # Create for platforms/android android_dir = Path.cwd() / "platforms" / "android" if android_dir.exists(): local_props = android_dir / "local.properties" content = f"""# Android SDK path sdk.dir={ANDROID_SDK_PATH.replace(chr(92), '/')} # NDK path ndk.dir={str(ndk_path).replace(chr(92), '/')} """ local_props.write_text(content) print(f"✓ Created: {local_props}") # Create for TestApp/android if exists testapp_dir = Path.cwd() / "TestApp" / "android" if testapp_dir.exists(): local_props = testapp_dir / "local.properties" local_props.write_text(content) print(f"✓ Created: {local_props}") return True def create_gradle_properties(self): """Create gradle.properties file""" self.print_step(7, "Creating gradle.properties") android_dir = Path.cwd() / "platforms" / "android" if android_dir.exists(): gradle_props = android_dir / "gradle.properties" content = """# Gradle settings org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.parallel=true org.gradle.daemon=true org.gradle.configureondemand=true # Android settings android.useAndroidX=true android.enableJetifier=true # React Native settings reactNativeArchitectures=armeabi-v7a,arm64-v8a # Build optimization org.gradle.caching=true """ gradle_props.write_text(content) print(f"✓ Created: {gradle_props}") return True def verify_installation(self): """Verify all installations""" self.print_step(8, "Verifying Installation") checks = [ ("Java", "java --version"), ("Gradle", "gradle --version"), ("ADB", "adb --version"), ] all_good = True for name, cmd in checks: success, stdout, _ = self.run_command(cmd) if success: print(f"✓ {name}: OK") if name == "ADB": # Show adb version print(f" {stdout.strip().split()[0]}") else: if name == "ADB": # Try with full path adb_path = Path(ANDROID_SDK_PATH) / "platform-tools" / "adb.exe" if adb_path.exists(): print(f"✓ {name}: Found at {adb_path}") print(f" (Add {adb_path.parent} to PATH manually or restart)") self.warnings.append("adb found but not in PATH - restart required") else: print(f"✗ {name}: Not found") self.warnings.append(f"{name} not installed") all_good = False else: print(f"✗ {name}: Not found (may need restart)") self.warnings.append(f"{name} verification failed - may need system restart") all_good = False # Also check ANDROID_HOME android_home = os.environ.get('ANDROID_HOME') if android_home: print(f"✓ ANDROID_HOME: {android_home}") else: print("✗ ANDROID_HOME: Not set") self.warnings.append("ANDROID_HOME not set - restart required") return all_good def run(self): """Main setup process""" self.print_header("Android Development Environment Setup") # Check admin privileges if not self.is_admin(): print("\n⚠ WARNING: Not running as administrator!") print("Some operations may fail. Run as administrator for best results.") response = input("\nContinue anyway? (y/n): ") if response.lower() != 'y': return # Check Node.js if not self.check_nodejs(): self.errors.append("Node.js is required. Please install it first.") return # Check/Install Java if not self.check_java(): self.warnings.append("Java installation incomplete") # Install Gradle if not self.install_gradle(): self.warnings.append("Gradle installation incomplete") # Install Android SDK if not self.install_android_sdk(): self.errors.append("Android SDK installation failed") return # Install SDK components if not self.install_sdk_components(): self.warnings.append("Some SDK components may not be installed") # Create configuration files os.chdir(Path(__file__).parent) # Return to script directory self.create_local_properties() self.create_gradle_properties() # Verify installation self.verify_installation() # Print summary self.print_header("Setup Complete!") if self.errors: print("\n❌ Errors:") for error in self.errors: print(f" - {error}") if self.warnings: print("\n⚠ Warnings:") for warning in self.warnings: print(f" - {warning}") print("\n✅ Next Steps:") print("1. Restart your command prompt or system") print("2. Run: npm install") print("3. Run: npm run android") print("\nFor troubleshooting, run: npx react-native doctor") if __name__ == "__main__": try: setup = AndroidSetup() setup.run() except KeyboardInterrupt: print("\n\nSetup cancelled by user") except Exception as e: print(f"\n❌ Setup failed: {e}") import traceback traceback.print_exc() input("\nPress Enter to exit...")