소스 검색

Add Android emulator launcher scripts with resolution selection

- Add run_emulator.py: Comprehensive Python script for launching ZenTap in Android emulators
  - Interactive resolution selection menu (8 preset options + custom)
  - Command-line arguments support
  - Automatic AVD creation and management
  - App building and installation
  - Emulator lifecycle management
  - Cross-platform compatibility

- Add run_emulator.sh: Bash script for Unix-like systems
  - Interactive menu with colored output
  - Command-line arguments
  - Basic AVD management
  - Performance optimizations

- Update scripts/README.md with comprehensive documentation
  - Usage examples and resolution options
  - Command-line argument reference
  - Troubleshooting guide
  - Performance tips

Features:
- Support for 8 preset resolutions (phone small/medium/large, tablets, foldable, desktop)
- Custom resolution support
- Debug and release build modes
- Cold boot option for clean testing
- Automatic emulator boot detection
- Graceful cleanup on exit

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fszontagh 6 달 전
부모
커밋
e96b8cbbac
3개의 변경된 파일949개의 추가작업 그리고 0개의 파일을 삭제
  1. 136 0
      scripts/README.md
  2. 360 0
      scripts/run_emulator.py
  3. 453 0
      scripts/run_emulator.sh

+ 136 - 0
scripts/README.md

@@ -10,6 +10,12 @@ Converts audio files to the proper format and specifications for the ZenTap game
 ### 📱 [`build_app.sh`](build_app.sh)
 Builds APK and AAB files for Android in debug and release modes.
 
+### 📱 [`run_emulator.py`](run_emulator.py) ⭐ **NEW**
+Comprehensive Python script to launch ZenTap in Android emulators with selectable resolutions.
+
+### 📱 [`run_emulator.sh`](run_emulator.sh) ⭐ **NEW**
+Bash script to launch ZenTap in Android emulators with resolution options.
+
 ---
 
 ## Audio Conversion Script
@@ -177,6 +183,136 @@ Both scripts follow the fSociety.hu color scheme defined in [`../SCHEME_COLORS.m
 
 ---
 
+## Emulator Scripts ⭐ **NEW**
+
+### Prerequisites for Emulator Scripts
+1. **Flutter SDK** - Ensure Flutter is installed and in your PATH
+2. **Android SDK** - Install Android SDK with:
+   - Android SDK Platform Tools
+   - Android Emulator
+   - At least one system image (API 34+ recommended)
+3. **Environment Variables** - Set one of:
+   - `ANDROID_SDK_ROOT`
+   - `ANDROID_HOME`
+4. **Python 3** (for run_emulator.py) - Python 3.6 or later
+
+### `run_emulator.py` (Recommended)
+A comprehensive Python script with full features and error handling.
+
+**Features:**
+- Interactive resolution selection menu
+- Command-line arguments support
+- Automatic AVD creation
+- App building and installation
+- Emulator lifecycle management
+- Cross-platform compatibility
+
+#### Usage Examples
+```bash
+# Interactive mode (recommended for first-time use)
+./scripts/run_emulator.py
+
+# Command line mode with preset resolution
+./scripts/run_emulator.py --resolution 3 --build-mode release
+
+# Custom resolution
+./scripts/run_emulator.py --width 1080 --height 1920
+
+# List available AVDs
+./scripts/run_emulator.py --list-avds
+
+# Full command line example
+./scripts/run_emulator.py --resolution 2 --cold-boot --build-mode debug
+```
+
+### `run_emulator.sh`
+A bash script for Unix-like systems (Linux, macOS).
+
+**Features:**
+- Interactive menu system
+- Command-line arguments
+- Colored output
+- Basic AVD management
+
+#### Usage Examples
+```bash
+# Interactive mode
+./scripts/run_emulator.sh
+
+# Command line mode
+./scripts/run_emulator.sh --width 411 --height 731 --build-mode release
+
+# List AVDs
+./scripts/run_emulator.sh --list-avds
+
+# Cold boot with custom resolution
+./scripts/run_emulator.sh --width 800 --height 1280 --cold-boot
+```
+
+### Available Resolutions
+
+| Option | Name | Resolution | Description |
+|--------|------|------------|-------------|
+| 1 | Phone Small | 360×640 | Small phone screen |
+| 2 | Phone Medium | 411×731 | Standard phone screen |
+| 3 | Phone Large | 414×896 | Large phone screen |
+| 4 | Tablet 7" | 600×960 | 7-inch tablet |
+| 5 | Tablet 10" | 800×1280 | 10-inch tablet |
+| 6 | Foldable | 673×841 | Foldable device |
+| 7 | Desktop | 1920×1080 | Desktop resolution |
+| 8 | Custom | User-defined | Specify your own resolution |
+
+### Command Line Arguments
+
+#### Common Arguments (both scripts)
+
+| Argument | Description | Example |
+|----------|-------------|---------|
+| `--width`, `-w` | Screen width in pixels | `--width 1080` |
+| `--height` | Screen height in pixels | `--height 1920` |
+| `--avd`, `-a` | Use specific AVD name | `--avd My_Custom_AVD` |
+| `--cold-boot`, `-c` | Wipe data and cold boot | `--cold-boot` |
+| `--build-mode`, `-b` | Build mode (debug/release) | `--build-mode release` |
+| `--list-avds`, `-l` | List available AVDs | `--list-avds` |
+
+#### Python Script Only
+- `--resolution`, `-r` - Use preset resolution (1-8)
+
+#### Bash Script Only
+- `--no-launch` - Don't auto-launch app
+
+### Emulator Troubleshooting
+
+#### Common Issues
+
+1. **"Emulator not found"**
+   - Install Android SDK
+   - Set ANDROID_SDK_ROOT or ANDROID_HOME environment variable
+   - Ensure emulator is installed in SDK
+
+2. **"No AVDs available"**
+   - Run: `flutter emulators --create`
+   - Or use Android Studio to create an AVD
+   - Scripts will attempt to create AVDs automatically
+
+3. **"Build failed"**
+   - Ensure you're in the project root directory
+   - Run `flutter doctor` to check Flutter setup
+   - Try `flutter clean` and rebuild
+
+4. **"Emulator won't start"**
+   - Check if virtualization is enabled in BIOS
+   - Try different system image (x86_64 vs arm64)
+   - Increase available RAM in AVD settings
+
+#### Performance Tips
+1. **Use x86_64 system images** for better performance on Intel/AMD systems
+2. **Enable hardware acceleration** (GPU host mode is used by default)
+3. **Allocate sufficient RAM** (scripts use 2GB by default)
+4. **Use SSD storage** for faster emulator boot times
+
+---
+
 ## Contributing
 
 When modifying these scripts:

+ 360 - 0
scripts/run_emulator.py

@@ -0,0 +1,360 @@
+#!/usr/bin/env python3
+"""
+ZenTap Android Emulator Launcher
+Starts the ZenTap app in an Android emulator with selectable resolution and options.
+"""
+
+import os
+import sys
+import subprocess
+import time
+import argparse
+from pathlib import Path
+
+# Common resolutions and their names
+RESOLUTIONS = {
+    '1': {'name': 'Phone Small (360x640)', 'width': 360, 'height': 640, 'density': 160},
+    '2': {'name': 'Phone Medium (411x731)', 'width': 411, 'height': 731, 'density': 160},
+    '3': {'name': 'Phone Large (414x896)', 'width': 414, 'height': 896, 'density': 160},
+    '4': {'name': 'Tablet 7" (600x960)', 'width': 600, 'height': 960, 'density': 160},
+    '5': {'name': 'Tablet 10" (800x1280)', 'width': 800, 'height': 1280, 'density': 160},
+    '6': {'name': 'Foldable (673x841)', 'width': 673, 'height': 841, 'density': 160},
+    '7': {'name': 'Desktop (1920x1080)', 'width': 1920, 'height': 1080, 'density': 160},
+    '8': {'name': 'Custom (specify)', 'width': None, 'height': None, 'density': 160}
+}
+
+class ZenTapEmulatorLauncher:
+    def __init__(self):
+        self.project_root = Path(__file__).parent.parent
+        self.android_sdk_root = self.find_android_sdk()
+        self.emulator_path = self.find_emulator_path()
+        
+    def find_android_sdk(self):
+        """Find Android SDK root directory"""
+        possible_paths = [
+            os.environ.get('ANDROID_SDK_ROOT'),
+            os.environ.get('ANDROID_HOME'),
+            Path.home() / 'Android' / 'Sdk',
+            Path.home() / 'Library' / 'Android' / 'sdk',  # macOS
+            Path('/opt/android-sdk'),
+            Path('/usr/local/android-sdk')
+        ]
+        
+        for path in possible_paths:
+            if path and Path(path).exists():
+                return Path(path)
+        
+        return None
+    
+    def find_emulator_path(self):
+        """Find emulator executable"""
+        if self.android_sdk_root:
+            emulator_path = self.android_sdk_root / 'emulator' / 'emulator'
+            if emulator_path.exists():
+                return emulator_path
+        
+        # Try to find in PATH
+        try:
+            result = subprocess.run(['which', 'emulator'], capture_output=True, text=True)
+            if result.returncode == 0:
+                return Path(result.stdout.strip())
+        except:
+            pass
+        
+        return None
+    
+    def get_available_avds(self):
+        """Get list of available Android Virtual Devices"""
+        if not self.emulator_path:
+            return []
+        
+        try:
+            result = subprocess.run([str(self.emulator_path), '-list-avds'], 
+                                  capture_output=True, text=True)
+            if result.returncode == 0:
+                return [line.strip() for line in result.stdout.strip().split('\n') if line.strip()]
+        except:
+            pass
+        
+        return []
+    
+    def create_avd_if_needed(self, avd_name, resolution_info):
+        """Create a new AVD if it doesn't exist"""
+        avds = self.get_available_avds()
+        if avd_name in avds:
+            return True
+        
+        print(f"Creating new AVD: {avd_name}")
+        
+        # Use avdmanager to create AVD
+        if self.android_sdk_root:
+            avdmanager_path = self.android_sdk_root / 'cmdline-tools' / 'latest' / 'bin' / 'avdmanager'
+            if not avdmanager_path.exists():
+                avdmanager_path = self.android_sdk_root / 'tools' / 'bin' / 'avdmanager'
+            
+            if avdmanager_path.exists():
+                try:
+                    # Create AVD with specified resolution
+                    cmd = [
+                        str(avdmanager_path), 'create', 'avd',
+                        '-n', avd_name,
+                        '-k', 'system-images;android-34;google_apis;x86_64',
+                        '-d', 'pixel',
+                        '--force'
+                    ]
+                    
+                    result = subprocess.run(cmd, input='no\n', text=True, capture_output=True)
+                    return result.returncode == 0
+                except Exception as e:
+                    print(f"Failed to create AVD: {e}")
+        
+        return False
+    
+    def wait_for_emulator(self, max_wait=120):
+        """Wait for emulator to boot completely"""
+        print("Waiting for emulator to boot...")
+        start_time = time.time()
+        
+        while time.time() - start_time < max_wait:
+            try:
+                result = subprocess.run(['adb', 'shell', 'getprop', 'sys.boot_completed'], 
+                                      capture_output=True, text=True, timeout=5)
+                if result.returncode == 0 and result.stdout.strip() == '1':
+                    print("Emulator is ready!")
+                    return True
+            except:
+                pass
+            
+            print(".", end="", flush=True)
+            time.sleep(2)
+        
+        print("\nTimeout waiting for emulator to boot")
+        return False
+    
+    def launch_emulator(self, avd_name, resolution_info, no_window=False, cold_boot=False):
+        """Launch the emulator with specified options"""
+        if not self.emulator_path:
+            print("Error: Emulator not found!")
+            return False
+        
+        cmd = [str(self.emulator_path), '-avd', avd_name]
+        
+        # Add resolution parameters if specified
+        if resolution_info['width'] and resolution_info['height']:
+            cmd.extend(['-skin', f"{resolution_info['width']}x{resolution_info['height']}"])
+        
+        # Add optional parameters
+        if no_window:
+            cmd.append('-no-window')
+        if cold_boot:
+            cmd.append('-wipe-data')
+        
+        # Performance optimizations
+        cmd.extend([
+            '-gpu', 'host',
+            '-no-boot-anim',
+            '-memory', '2048'
+        ])
+        
+        print(f"Starting emulator with command: {' '.join(cmd)}")
+        
+        try:
+            # Start emulator in background
+            process = subprocess.Popen(cmd)
+            time.sleep(5)  # Give emulator time to start
+            
+            if process.poll() is None:  # Process is still running
+                return True
+            else:
+                print("Emulator failed to start")
+                return False
+        except Exception as e:
+            print(f"Failed to start emulator: {e}")
+            return False
+    
+    def build_and_install_app(self, build_mode='debug'):
+        """Build and install the ZenTap app"""
+        print(f"Building app in {build_mode} mode...")
+        
+        # Change to project directory
+        os.chdir(self.project_root)
+        
+        # Build the app
+        build_cmd = ['flutter', 'build', 'apk']
+        if build_mode == 'debug':
+            build_cmd.append('--debug')
+        elif build_mode == 'release':
+            build_cmd.append('--release')
+        
+        try:
+            result = subprocess.run(build_cmd, check=True)
+            print("Build successful!")
+        except subprocess.CalledProcessError:
+            print("Build failed!")
+            return False
+        
+        # Install the app
+        print("Installing app on emulator...")
+        try:
+            if build_mode == 'debug':
+                apk_path = 'build/app/outputs/flutter-apk/app-debug.apk'
+            else:
+                apk_path = 'build/app/outputs/flutter-apk/app-release.apk'
+            
+            subprocess.run(['adb', 'install', '-r', apk_path], check=True)
+            print("App installed successfully!")
+            return True
+        except subprocess.CalledProcessError:
+            print("Installation failed!")
+            return False
+    
+    def launch_app(self):
+        """Launch the ZenTap app"""
+        print("Launching ZenTap...")
+        try:
+            subprocess.run([
+                'adb', 'shell', 'am', 'start',
+                '-n', 'hu.fsociety.zentap/hu.fsociety.zentap.MainActivity'
+            ], check=True)
+            print("App launched successfully!")
+            return True
+        except subprocess.CalledProcessError:
+            print("Failed to launch app!")
+            return False
+    
+    def interactive_mode(self):
+        """Run in interactive mode with menu selection"""
+        print("=== ZenTap Android Emulator Launcher ===\n")
+        
+        # Check prerequisites
+        if not self.emulator_path:
+            print("Error: Android emulator not found!")
+            print("Please install Android SDK and set ANDROID_SDK_ROOT environment variable.")
+            return False
+        
+        # Show available AVDs
+        avds = self.get_available_avds()
+        print("Available Android Virtual Devices:")
+        if avds:
+            for i, avd in enumerate(avds, 1):
+                print(f"  {i}. {avd}")
+        else:
+            print("  No AVDs found. Will create a new one.")
+        
+        print("\nSelect resolution:")
+        for key, info in RESOLUTIONS.items():
+            print(f"  {key}. {info['name']}")
+        
+        # Get user input
+        resolution_choice = input(f"\nChoose resolution (1-{len(RESOLUTIONS)}): ").strip()
+        if resolution_choice not in RESOLUTIONS:
+            print("Invalid choice!")
+            return False
+        
+        resolution_info = RESOLUTIONS[resolution_choice]
+        
+        # Handle custom resolution
+        if resolution_choice == '8':
+            try:
+                width = int(input("Enter width: "))
+                height = int(input("Enter height: "))
+                resolution_info['width'] = width
+                resolution_info['height'] = height
+            except ValueError:
+                print("Invalid resolution values!")
+                return False
+        
+        # Select or create AVD
+        avd_name = f"ZenTap_{resolution_info['width']}x{resolution_info['height']}"
+        
+        # Additional options
+        print("\nAdditional options:")
+        cold_boot = input("Cold boot (wipe data)? (y/N): ").lower().startswith('y')
+        build_mode = input("Build mode (debug/release) [debug]: ").strip() or 'debug'
+        
+        return self.run_full_cycle(avd_name, resolution_info, cold_boot, build_mode)
+    
+    def run_full_cycle(self, avd_name, resolution_info, cold_boot=False, build_mode='debug'):
+        """Run the complete cycle: create AVD, launch emulator, build, install, and run app"""
+        
+        # Create AVD if needed
+        if avd_name not in self.get_available_avds():
+            if not self.create_avd_if_needed(avd_name, resolution_info):
+                print("Failed to create AVD. Using existing AVD if available.")
+                avds = self.get_available_avds()
+                if avds:
+                    avd_name = avds[0]
+                    print(f"Using existing AVD: {avd_name}")
+                else:
+                    print("No AVDs available!")
+                    return False
+        
+        # Launch emulator
+        if not self.launch_emulator(avd_name, resolution_info, cold_boot=cold_boot):
+            return False
+        
+        # Wait for emulator to boot
+        if not self.wait_for_emulator():
+            return False
+        
+        # Build and install app
+        if not self.build_and_install_app(build_mode):
+            return False
+        
+        # Launch app
+        if not self.launch_app():
+            return False
+        
+        print("\n=== ZenTap is now running in the emulator! ===")
+        print("Press Ctrl+C to stop the emulator when done.")
+        
+        try:
+            # Keep script running
+            while True:
+                time.sleep(1)
+        except KeyboardInterrupt:
+            print("\nStopping emulator...")
+            subprocess.run(['adb', 'emu', 'kill'], capture_output=True)
+            print("Emulator stopped.")
+        
+        return True
+
+def main():
+    parser = argparse.ArgumentParser(description='Launch ZenTap in Android emulator')
+    parser.add_argument('--resolution', '-r', choices=list(RESOLUTIONS.keys()), 
+                       help='Resolution preset')
+    parser.add_argument('--width', '-w', type=int, help='Custom width')
+    parser.add_argument('--height', type=int, help='Custom height')
+    parser.add_argument('--avd', '-a', help='Specific AVD name to use')
+    parser.add_argument('--cold-boot', '-c', action='store_true', help='Cold boot (wipe data)')
+    parser.add_argument('--build-mode', '-b', choices=['debug', 'release'], default='debug',
+                       help='Build mode')
+    parser.add_argument('--list-avds', '-l', action='store_true', help='List available AVDs')
+    
+    args = parser.parse_args()
+    
+    launcher = ZenTapEmulatorLauncher()
+    
+    if args.list_avds:
+        avds = launcher.get_available_avds()
+        print("Available AVDs:")
+        for avd in avds:
+            print(f"  - {avd}")
+        return
+    
+    if args.resolution or (args.width and args.height):
+        # Command line mode
+        if args.resolution:
+            resolution_info = RESOLUTIONS[args.resolution].copy()
+        else:
+            resolution_info = {'width': args.width, 'height': args.height, 'density': 160}
+        
+        avd_name = args.avd or f"ZenTap_{resolution_info['width']}x{resolution_info['height']}"
+        
+        launcher.run_full_cycle(avd_name, resolution_info, args.cold_boot, args.build_mode)
+    else:
+        # Interactive mode
+        launcher.interactive_mode()
+
+if __name__ == '__main__':
+    main()

+ 453 - 0
scripts/run_emulator.sh

@@ -0,0 +1,453 @@
+#!/bin/bash
+
+# ZenTap Android Emulator Launcher (Bash Version)
+# Simple script to launch ZenTap in Android emulator with resolution selection
+
+set -e
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# Script directory and project root
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+
+# Find Android SDK
+find_android_sdk() {
+    local paths=(
+        "$ANDROID_SDK_ROOT"
+        "$ANDROID_HOME"
+        "$HOME/Android/Sdk"
+        "$HOME/Library/Android/sdk"
+        "/opt/android-sdk"
+        "/usr/local/android-sdk"
+    )
+    
+    for path in "${paths[@]}"; do
+        if [[ -n "$path" && -d "$path" ]]; then
+            echo "$path"
+            return 0
+        fi
+    done
+    
+    return 1
+}
+
+# Print colored output
+print_status() {
+    echo -e "${BLUE}[INFO]${NC} $1"
+}
+
+print_success() {
+    echo -e "${GREEN}[SUCCESS]${NC} $1"
+}
+
+print_error() {
+    echo -e "${RED}[ERROR]${NC} $1"
+}
+
+print_warning() {
+    echo -e "${YELLOW}[WARNING]${NC} $1"
+}
+
+# Check prerequisites
+check_prerequisites() {
+    print_status "Checking prerequisites..."
+    
+    # Check Flutter
+    if ! command -v flutter &> /dev/null; then
+        print_error "Flutter not found in PATH"
+        exit 1
+    fi
+    
+    # Check Android SDK
+    if ! ANDROID_SDK=$(find_android_sdk); then
+        print_error "Android SDK not found"
+        print_error "Please install Android SDK and set ANDROID_SDK_ROOT or ANDROID_HOME"
+        exit 1
+    fi
+    
+    # Check emulator
+    EMULATOR_PATH="$ANDROID_SDK/emulator/emulator"
+    if [[ ! -f "$EMULATOR_PATH" ]]; then
+        print_error "Android emulator not found at $EMULATOR_PATH"
+        exit 1
+    fi
+    
+    # Check ADB
+    if ! command -v adb &> /dev/null; then
+        print_error "ADB not found in PATH"
+        exit 1
+    fi
+    
+    print_success "All prerequisites satisfied"
+}
+
+# Show resolution menu
+show_resolution_menu() {
+    echo
+    echo "=== ZenTap Android Emulator Launcher ==="
+    echo
+    echo "Select target resolution:"
+    echo "  1) Phone Small (360x640)"
+    echo "  2) Phone Medium (411x731)"
+    echo "  3) Phone Large (414x896)" 
+    echo "  4) Tablet 7\" (600x960)"
+    echo "  5) Tablet 10\" (800x1280)"
+    echo "  6) Foldable (673x841)"
+    echo "  7) Desktop (1920x1080)"
+    echo "  8) Custom resolution"
+    echo
+}
+
+# Get resolution choice
+get_resolution() {
+    while true; do
+        read -p "Enter choice (1-8): " choice
+        case $choice in
+            1) WIDTH=360; HEIGHT=640; RESOLUTION_NAME="Phone_Small"; break ;;
+            2) WIDTH=411; HEIGHT=731; RESOLUTION_NAME="Phone_Medium"; break ;;
+            3) WIDTH=414; HEIGHT=896; RESOLUTION_NAME="Phone_Large"; break ;;
+            4) WIDTH=600; HEIGHT=960; RESOLUTION_NAME="Tablet_7"; break ;;
+            5) WIDTH=800; HEIGHT=1280; RESOLUTION_NAME="Tablet_10"; break ;;
+            6) WIDTH=673; HEIGHT=841; RESOLUTION_NAME="Foldable"; break ;;
+            7) WIDTH=1920; HEIGHT=1080; RESOLUTION_NAME="Desktop"; break ;;
+            8) 
+                read -p "Enter width: " WIDTH
+                read -p "Enter height: " HEIGHT
+                RESOLUTION_NAME="Custom"
+                if ! [[ "$WIDTH" =~ ^[0-9]+$ ]] || ! [[ "$HEIGHT" =~ ^[0-9]+$ ]]; then
+                    print_error "Invalid resolution values"
+                    continue
+                fi
+                break ;;
+            *) print_error "Invalid choice" ;;
+        esac
+    done
+}
+
+# Get additional options
+get_options() {
+    echo
+    read -p "Cold boot (wipe data)? (y/N): " cold_boot
+    [[ "$cold_boot" =~ ^[Yy] ]] && COLD_BOOT=true || COLD_BOOT=false
+    
+    read -p "Build mode (debug/release) [debug]: " build_mode
+    BUILD_MODE="${build_mode:-debug}"
+    
+    read -p "Auto-launch app? (Y/n): " auto_launch
+    [[ "$auto_launch" =~ ^[Nn] ]] && AUTO_LAUNCH=false || AUTO_LAUNCH=true
+}
+
+# List available AVDs
+list_avds() {
+    print_status "Available AVDs:"
+    "$EMULATOR_PATH" -list-avds | while read -r avd; do
+        echo "  - $avd"
+    done
+}
+
+# Create AVD if it doesn't exist
+ensure_avd() {
+    local avd_name="$1"
+    
+    if "$EMULATOR_PATH" -list-avds | grep -q "^$avd_name$"; then
+        print_status "Using existing AVD: $avd_name"
+        return 0
+    fi
+    
+    print_status "Creating new AVD: $avd_name"
+    
+    # Try to find avdmanager
+    local avdmanager_paths=(
+        "$ANDROID_SDK/cmdline-tools/latest/bin/avdmanager"
+        "$ANDROID_SDK/tools/bin/avdmanager"
+    )
+    
+    local avdmanager=""
+    for path in "${avdmanager_paths[@]}"; do
+        if [[ -f "$path" ]]; then
+            avdmanager="$path"
+            break
+        fi
+    done
+    
+    if [[ -z "$avdmanager" ]]; then
+        print_warning "avdmanager not found, using existing AVD"
+        local existing_avds=($("$EMULATOR_PATH" -list-avds))
+        if [[ ${#existing_avds[@]} -gt 0 ]]; then
+            AVD_NAME="${existing_avds[0]}"
+            print_status "Using first available AVD: $AVD_NAME"
+            return 0
+        else
+            print_error "No AVDs available and cannot create new one"
+            return 1
+        fi
+    fi
+    
+    # Create AVD
+    echo "no" | "$avdmanager" create avd \
+        -n "$avd_name" \
+        -k "system-images;android-34;google_apis;x86_64" \
+        -d "pixel" \
+        --force >/dev/null 2>&1 || {
+        print_warning "Failed to create AVD, will use existing one if available"
+        return 1
+    }
+    
+    print_success "AVD created: $avd_name"
+}
+
+# Launch emulator
+launch_emulator() {
+    local avd_name="$1"
+    
+    print_status "Starting emulator: $avd_name"
+    print_status "Resolution: ${WIDTH}x${HEIGHT}"
+    
+    local cmd=("$EMULATOR_PATH" -avd "$avd_name")
+    
+    # Add resolution
+    cmd+=(-skin "${WIDTH}x${HEIGHT}")
+    
+    # Add performance options
+    cmd+=(-gpu host -no-boot-anim -memory 2048)
+    
+    # Add cold boot if requested
+    if [[ "$COLD_BOOT" == "true" ]]; then
+        cmd+=(-wipe-data)
+        print_status "Cold boot enabled (wiping data)"
+    fi
+    
+    print_status "Emulator command: ${cmd[*]}"
+    
+    # Start emulator in background
+    "${cmd[@]}" &>/dev/null &
+    local emulator_pid=$!
+    
+    # Wait a bit for emulator to start
+    sleep 5
+    
+    # Check if emulator process is still running
+    if ! kill -0 $emulator_pid 2>/dev/null; then
+        print_error "Failed to start emulator"
+        return 1
+    fi
+    
+    print_success "Emulator started (PID: $emulator_pid)"
+    return 0
+}
+
+# Wait for emulator to boot
+wait_for_emulator() {
+    print_status "Waiting for emulator to boot..."
+    
+    local max_wait=120
+    local elapsed=0
+    
+    while [[ $elapsed -lt $max_wait ]]; do
+        if adb shell getprop sys.boot_completed 2>/dev/null | grep -q "1"; then
+            print_success "Emulator is ready!"
+            return 0
+        fi
+        
+        echo -n "."
+        sleep 2
+        ((elapsed += 2))
+    done
+    
+    echo
+    print_error "Timeout waiting for emulator to boot"
+    return 1
+}
+
+# Build app
+build_app() {
+    print_status "Building app in $BUILD_MODE mode..."
+    
+    cd "$PROJECT_ROOT"
+    
+    local build_cmd=(flutter build apk)
+    
+    if [[ "$BUILD_MODE" == "debug" ]]; then
+        build_cmd+=(--debug)
+    elif [[ "$BUILD_MODE" == "release" ]]; then
+        build_cmd+=(--release)
+    fi
+    
+    if "${build_cmd[@]}"; then
+        print_success "Build completed successfully"
+        return 0
+    else
+        print_error "Build failed"
+        return 1
+    fi
+}
+
+# Install app
+install_app() {
+    print_status "Installing app on emulator..."
+    
+    local apk_path
+    if [[ "$BUILD_MODE" == "debug" ]]; then
+        apk_path="$PROJECT_ROOT/build/app/outputs/flutter-apk/app-debug.apk"
+    else
+        apk_path="$PROJECT_ROOT/build/app/outputs/flutter-apk/app-release.apk"
+    fi
+    
+    if [[ ! -f "$apk_path" ]]; then
+        print_error "APK not found: $apk_path"
+        return 1
+    fi
+    
+    if adb install -r "$apk_path"; then
+        print_success "App installed successfully"
+        return 0
+    else
+        print_error "Installation failed"
+        return 1
+    fi
+}
+
+# Launch app
+launch_app() {
+    print_status "Launching ZenTap..."
+    
+    if adb shell am start -n "hu.fsociety.zentap/hu.fsociety.zentap.MainActivity"; then
+        print_success "App launched successfully!"
+        return 0
+    else
+        print_error "Failed to launch app"
+        return 1
+    fi
+}
+
+# Main execution
+main() {
+    # Parse command line arguments
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            --list-avds|-l)
+                check_prerequisites
+                list_avds
+                exit 0
+                ;;
+            --width|-w)
+                WIDTH="$2"
+                shift 2
+                ;;
+            --height)
+                HEIGHT="$2"
+                shift 2
+                ;;
+            --avd|-a)
+                AVD_NAME="$2"
+                shift 2
+                ;;
+            --cold-boot|-c)
+                COLD_BOOT=true
+                shift
+                ;;
+            --build-mode|-b)
+                BUILD_MODE="$2"
+                shift 2
+                ;;
+            --no-launch)
+                AUTO_LAUNCH=false
+                shift
+                ;;
+            --help)
+                echo "Usage: $0 [OPTIONS]"
+                echo "Options:"
+                echo "  --list-avds, -l      List available AVDs"
+                echo "  --width, -w WIDTH    Set width"
+                echo "  --height HEIGHT      Set height"
+                echo "  --avd, -a NAME       Use specific AVD"
+                echo "  --cold-boot, -c      Cold boot (wipe data)"
+                echo "  --build-mode, -b     debug|release"
+                echo "  --no-launch          Don't auto-launch app"
+                echo "  --help               Show this help"
+                exit 0
+                ;;
+            *)
+                print_error "Unknown option: $1"
+                exit 1
+                ;;
+        esac
+    done
+    
+    # Check prerequisites
+    check_prerequisites
+    
+    # Interactive mode if no resolution specified
+    if [[ -z "$WIDTH" || -z "$HEIGHT" ]]; then
+        show_resolution_menu
+        get_resolution
+        get_options
+    fi
+    
+    # Set defaults
+    AVD_NAME="${AVD_NAME:-ZenTap_${RESOLUTION_NAME}_${WIDTH}x${HEIGHT}}"
+    BUILD_MODE="${BUILD_MODE:-debug}"
+    AUTO_LAUNCH="${AUTO_LAUNCH:-true}"
+    COLD_BOOT="${COLD_BOOT:-false}"
+    
+    # Create/ensure AVD exists
+    ensure_avd "$AVD_NAME"
+    
+    # Launch emulator
+    if ! launch_emulator "$AVD_NAME"; then
+        exit 1
+    fi
+    
+    # Wait for emulator to boot
+    if ! wait_for_emulator; then
+        exit 1
+    fi
+    
+    # Build app
+    if ! build_app; then
+        exit 1
+    fi
+    
+    # Install app
+    if ! install_app; then
+        exit 1
+    fi
+    
+    # Launch app if requested
+    if [[ "$AUTO_LAUNCH" == "true" ]]; then
+        launch_app
+    fi
+    
+    echo
+    print_success "=== ZenTap is ready! ==="
+    echo "Emulator: $AVD_NAME (${WIDTH}x${HEIGHT})"
+    echo "Build mode: $BUILD_MODE"
+    if [[ "$AUTO_LAUNCH" == "true" ]]; then
+        echo "App: Launched automatically"
+    else
+        echo "App: Ready to launch manually"
+    fi
+    echo
+    echo "Press Ctrl+C to stop the emulator when done."
+    
+    # Keep script running and handle cleanup
+    trap 'echo; print_status "Stopping emulator..."; adb emu kill >/dev/null 2>&1; print_success "Emulator stopped."; exit 0' INT
+    
+    # Monitor emulator
+    while true; do
+        if ! adb devices | grep -q "emulator"; then
+            print_warning "Emulator connection lost"
+            break
+        fi
+        sleep 5
+    done
+}
+
+# Run main function
+main "$@"