You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							494 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							494 lines
						
					
					
						
							17 KiB
						
					
					
				| #!/bin/bash | |
| # build-android.sh | |
| # Author: Matthew Raymer | |
| # Date: 2025-07-11 | |
| # Description: Android build script for TimeSafari application | |
| # This script handles the complete Android build process including cleanup, | |
| # web build, Capacitor build, Gradle build, and Android Studio launch. | |
| # | |
| # Usage: | |
| #   ./scripts/build-android.sh [options] | |
| # | |
| # Options: | |
| #   --dev, --development    Build for development environment | |
| #   --test                  Build for testing environment   | |
| #   --prod, --production    Build for production environment | |
| #   --debug                 Build debug APK | |
| #   --release               Build release APK | |
| #   --studio                Open Android Studio after build | |
| #   --apk                   Build APK file | |
| #   --aab                   Build AAB (Android App Bundle) | |
| #   --clean                 Clean build artifacts only | |
| #   --sync                  Sync Capacitor only | |
| #   --assets                Generate assets only | |
| #   --deploy                Deploy APK to connected device | |
| #   -h, --help             Show this help message | |
| #   -v, --verbose          Enable verbose logging | |
| # | |
| # Examples: | |
| #   ./scripts/build-android.sh --dev --studio    # Development build + open studio | |
| #   ./scripts/build-android.sh --prod --apk      # Production APK build | |
| #   ./scripts/build-android.sh --test --aab      # Testing AAB build | |
| #   ./scripts/build-android.sh --clean           # Clean only | |
| #   ./scripts/build-android.sh --sync            # Sync only | |
| # | |
| # Exit Codes: | |
| # 1 - Android cleanup failed | |
| # 2 - Web build failed | |
| # 3 - Capacitor build failed | |
| # 4 - Gradle clean failed | |
| # 5 - Gradle assemble failed | |
| # 6 - Capacitor sync failed | |
| # 7 - Asset generation failed | |
| # 8 - Android Studio launch failed | |
| # 9 - Android asset validation failed | |
| 
 | |
| # Exit on any error | |
| set -e | |
| 
 | |
| # Source common utilities | |
| source "$(dirname "$0")/common.sh" | |
| 
 | |
| # Function to validate critical dependencies | |
| validate_dependencies() { | |
|     log_info "Validating critical dependencies..." | |
|      | |
|     # Check if node_modules exists | |
|     if [ ! -d "node_modules" ]; then | |
|         log_error "node_modules directory not found. Please run 'npm install' first." | |
|         exit 1 | |
|     fi | |
|      | |
|     # Check if tsx is available | |
|     if [ ! -f "node_modules/.bin/tsx" ]; then | |
|         log_error "tsx dependency not found. Please run 'npm install' first." | |
|         exit 1 | |
|     fi | |
|      | |
|     # Check if capacitor-assets is available | |
|     if [ ! -f "node_modules/.bin/capacitor-assets" ]; then | |
|         log_error "capacitor-assets dependency not found. Please run 'npm install' first." | |
|         exit 1 | |
|     fi | |
|      | |
|     log_success "All critical dependencies validated successfully" | |
| } | |
| 
 | |
| # Function to validate Android assets and resources | |
| validate_android_assets() { | |
|     log_info "Validating Android assets and resources..." | |
|      | |
|     # Check if source assets exist | |
|     local missing_assets=() | |
|      | |
|     if [ ! -f "resources/icon.png" ]; then | |
|         missing_assets+=("resources/icon.png") | |
|     fi | |
|      | |
|     if [ ! -f "resources/splash.png" ]; then | |
|         missing_assets+=("resources/splash.png") | |
|     fi | |
|      | |
|     if [ ! -f "resources/splash_dark.png" ]; then | |
|         missing_assets+=("resources/splash_dark.png") | |
|     fi | |
|      | |
|     if [ ${#missing_assets[@]} -gt 0 ]; then | |
|         log_error "Missing source assets:" | |
|         for asset in "${missing_assets[@]}"; do | |
|             log_error "  - $asset" | |
|         done | |
|         log_error "Please ensure all required assets are present in the resources/ directory." | |
|         return 1 | |
|     fi | |
|      | |
|     # Check if Android drawable resources exist | |
|     local missing_drawables=() | |
|      | |
|     if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then | |
|         missing_drawables+=("drawable/splash.png") | |
|     fi | |
|      | |
|     # Check if mipmap resources exist | |
|     local missing_mipmaps=() | |
|     local mipmap_dirs=("mipmap-mdpi" "mipmap-hdpi" "mipmap-xhdpi" "mipmap-xxhdpi" "mipmap-xxxhdpi") | |
|      | |
|     for dir in "${mipmap_dirs[@]}"; do | |
|         if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then | |
|             missing_mipmaps+=("$dir/ic_launcher.png") | |
|         fi | |
|         if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then | |
|             missing_mipmaps+=("$dir/ic_launcher_round.png") | |
|         fi | |
|     done | |
|      | |
|     # If any resources are missing, regenerate them | |
|     if [ ${#missing_drawables[@]} -gt 0 ] || [ ${#missing_mipmaps[@]} -gt 0 ]; then | |
|         log_warn "Missing Android resources detected:" | |
|         for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do | |
|             log_warn "  - $resource" | |
|         done | |
|          | |
|         log_info "Regenerating Android assets..." | |
|          | |
|         # Create assets directory if it doesn't exist | |
|         mkdir -p assets | |
|          | |
|         # Copy source assets to assets directory for capacitor-assets | |
|         cp resources/icon.png assets/ 2>/dev/null || log_warn "Could not copy icon.png" | |
|         cp resources/splash.png assets/ 2>/dev/null || log_warn "Could not copy splash.png" | |
|         cp resources/splash_dark.png assets/ 2>/dev/null || log_warn "Could not copy splash_dark.png" | |
|          | |
|         # Generate assets | |
|         if npx @capacitor/assets generate >/dev/null 2>&1; then | |
|             log_success "Android assets regenerated successfully" | |
|              | |
|             # Clean up temporary assets | |
|             rm -f assets/icon.png assets/splash.png assets/splash_dark.png | |
|              | |
|             # Verify the resources were created | |
|             local verification_failed=false | |
|              | |
|             if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then | |
|                 log_error "Failed to generate drawable/splash.png" | |
|                 verification_failed=true | |
|             fi | |
|              | |
|             for dir in "${mipmap_dirs[@]}"; do | |
|                 if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then | |
|                     log_error "Failed to generate $dir/ic_launcher.png" | |
|                     verification_failed=true | |
|                 fi | |
|                 if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then | |
|                     log_error "Failed to generate $dir/ic_launcher_round.png" | |
|                     verification_failed=true | |
|                 fi | |
|             done | |
|              | |
|             if [ "$verification_failed" = true ]; then | |
|                 log_error "Asset generation completed but some resources are still missing." | |
|                 log_info "You may need to manually create the missing resources or check the asset generation process." | |
|                 return 1 | |
|             fi | |
|         else | |
|             log_error "Failed to generate Android assets" | |
|             log_info "You may need to manually create the missing resources:" | |
|             for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do | |
|                 log_info "  - android/app/src/main/res/$resource" | |
|             done | |
|             return 1 | |
|         fi | |
|     else | |
|         log_success "All Android assets and resources validated successfully" | |
|     fi | |
|      | |
|     return 0 | |
| } | |
| 
 | |
| # Default values | |
| BUILD_MODE="development" | |
| BUILD_TYPE="debug" | |
| OPEN_STUDIO=false | |
| BUILD_APK=false | |
| BUILD_AAB=false | |
| CLEAN_ONLY=false | |
| SYNC_ONLY=false | |
| ASSETS_ONLY=false | |
| DEPLOY_APP=false | |
| AUTO_RUN=false | |
| CUSTOM_API_IP="" | |
| 
 | |
| # Function to parse Android-specific arguments | |
| parse_android_args() { | |
|     local args=("$@") | |
|     local i=0 | |
|      | |
|     while [ $i -lt ${#args[@]} ]; do | |
|         local arg="${args[$i]}" | |
|          | |
|         case $arg in | |
|             --dev|--development) | |
|                 BUILD_MODE="development" | |
|                 ;; | |
|             --test) | |
|                 BUILD_MODE="test" | |
|                 ;; | |
|             --prod|--production) | |
|                 BUILD_MODE="production" | |
|                 ;; | |
|             --debug) | |
|                 BUILD_TYPE="debug" | |
|                 ;; | |
|             --release) | |
|                 BUILD_TYPE="release" | |
|                 ;; | |
|             --studio) | |
|                 OPEN_STUDIO=true | |
|                 ;; | |
|             --apk) | |
|                 BUILD_APK=true | |
|                 ;; | |
|             --aab) | |
|                 BUILD_AAB=true | |
|                 ;; | |
|             --clean) | |
|                 CLEAN_ONLY=true | |
|                 ;; | |
|             --sync) | |
|                 SYNC_ONLY=true | |
|                 ;; | |
|             --assets|--assets-only) | |
|                 ASSETS_ONLY=true | |
|                 ;; | |
|             --deploy) | |
|                 DEPLOY_APP=true | |
|                 ;; | |
|             --auto-run) | |
|                 AUTO_RUN=true | |
|                 ;; | |
|             --api-ip) | |
|                 if [ $((i + 1)) -lt ${#args[@]} ]; then | |
|                     CUSTOM_API_IP="${args[$((i + 1))]}" | |
|                     i=$((i + 1))  # Skip the next argument | |
|                 else | |
|                     log_error "Error: --api-ip requires an IP address" | |
|                     exit 1 | |
|                 fi | |
|                 ;; | |
|             --api-ip=*) | |
|                 CUSTOM_API_IP="${arg#*=}" | |
|                 ;; | |
|             -h|--help) | |
|                 print_android_usage | |
|                 exit 0 | |
|                 ;; | |
|             -v|--verbose) | |
|                 set -x | |
|                 ;; | |
|             *) | |
|                 log_warn "Unknown argument: $arg" | |
|                 ;; | |
|         esac | |
|         i=$((i + 1)) | |
|     done | |
| } | |
| 
 | |
| # Function to print Android-specific usage | |
| print_android_usage() { | |
|     echo "Usage: $0 [options]" | |
|     echo "" | |
|     echo "Android Build Options:" | |
|     echo "  --dev, --development    Build for development environment" | |
|     echo "  --test                  Build for testing environment" | |
|     echo "  --prod, --production    Build for production environment" | |
|     echo "  --debug                 Build debug APK (default)" | |
|     echo "  --release               Build release APK" | |
|     echo "  --studio                Open Android Studio after build" | |
|     echo "  --apk                   Build APK file" | |
|     echo "  --aab                   Build AAB (Android App Bundle)" | |
|     echo "  --clean                 Clean build artifacts only" | |
|     echo "  --sync                  Sync Capacitor only" | |
|     echo "  --assets                Generate assets only" | |
|     echo "  --deploy                Deploy APK to connected device" | |
|     echo "  --auto-run              Auto-run app after build" | |
|     echo "  --api-ip <ip>          Custom IP address for claim API (defaults to 10.0.2.2)" | |
|     echo "" | |
|     echo "Common Options:" | |
|     echo "  -h, --help             Show this help message" | |
|     echo "  -v, --verbose          Enable verbose logging" | |
|     echo "" | |
|     echo "Examples:" | |
|     echo "  $0 --dev --studio      # Development build + open studio" | |
|     echo "  $0 --prod --apk        # Production APK build" | |
|     echo "  $0 --test --aab        # Testing AAB build" | |
|     echo "  $0 --test --auto-run   # Test build + auto-run" | |
|     echo "  $0 --clean             # Clean only" | |
|     echo "  $0 --sync              # Sync only" | |
|     echo "  $0 --deploy            # Build and deploy to device" | |
|     echo "  $0 --dev                    # Dev build with default 10.0.2.2" | |
|     echo "  $0 --dev --api-ip 192.168.1.100  # Dev build with custom API IP" | |
|     echo "" | |
| } | |
| 
 | |
| # Parse command line arguments | |
| parse_android_args "$@" | |
| 
 | |
| # Print build header | |
| print_header "TimeSafari Android Build Process" | |
| 
 | |
| # Validate dependencies before proceeding | |
| validate_dependencies | |
| 
 | |
| # Validate Android assets and resources | |
| validate_android_assets || { | |
|     log_error "Android asset validation failed. Please fix the issues above and try again." | |
|     exit 9 | |
| } | |
| 
 | |
| # Log build start | |
| log_info "Starting Android build process at $(date)" | |
| log_info "Build mode: $BUILD_MODE" | |
| log_info "Build type: $BUILD_TYPE" | |
| 
 | |
| # Setup environment for Capacitor build | |
| setup_build_env "capacitor" "$BUILD_MODE" | |
| 
 | |
| # Override API servers for Android development | |
| if [ "$BUILD_MODE" = "development" ]; then | |
|     if [ -n "$CUSTOM_API_IP" ]; then | |
|         # Use custom IP for physical device development | |
|         export VITE_DEFAULT_ENDORSER_API_SERVER="http://${CUSTOM_API_IP}:3000" | |
|         export VITE_DEFAULT_PARTNER_API_SERVER="http://${CUSTOM_API_IP}:3000" | |
|         log_info "Android development mode: Using custom IP ${CUSTOM_API_IP} for physical device" | |
|     else | |
|         # Use Android emulator IP (10.0.2.2) for Android development | |
|         export VITE_DEFAULT_ENDORSER_API_SERVER="http://10.0.2.2:3000" | |
|         export VITE_DEFAULT_PARTNER_API_SERVER="http://10.0.2.2:3000" | |
|         log_debug "Android development mode: Using 10.0.2.2 for emulator" | |
|     fi | |
| fi | |
| 
 | |
| # Setup application directories | |
| setup_app_directories | |
| 
 | |
| # Load environment from .env file if it exists | |
| load_env_file ".env" | |
| 
 | |
| # Handle clean-only mode | |
| if [ "$CLEAN_ONLY" = true ]; then | |
|     log_info "Clean-only mode: cleaning build artifacts" | |
|     safe_execute "Cleaning Android app" "npm run clean:android" || exit 1 | |
|     safe_execute "Cleaning dist directory" "clean_build_artifacts dist" || exit 1 | |
|     safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4 | |
|     log_success "Clean completed successfully!" | |
|     exit 0 | |
| fi | |
| 
 | |
| # Handle sync-only mode | |
| if [ "$SYNC_ONLY" = true ]; then | |
|     log_info "Sync-only mode: syncing with Capacitor" | |
|     safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6 | |
|     log_success "Sync completed successfully!" | |
|     exit 0 | |
| fi | |
| 
 | |
| # Handle assets-only mode | |
| if [ "$ASSETS_ONLY" = true ]; then | |
|     log_info "Assets-only mode: generating assets" | |
|     safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7 | |
|     log_success "Assets generation completed successfully!" | |
|     exit 0 | |
| fi | |
| 
 | |
| # Handle deploy-app mode | |
| if [ "$DEPLOY_APP" = true ]; then | |
|     log_info "Deploy-app mode: building APK and deploying to device" | |
|      | |
|     # Check for connected device | |
|     if ! adb devices | grep -q $'\tdevice'; then | |
|         log_error "No Android device connected. Please connect a device and try again." | |
|         exit 1 | |
|     fi | |
|      | |
|     # Build APK | |
|     safe_execute "Building APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5 | |
|      | |
|     # Install APK on device | |
|     safe_execute "Installing APK on device" "adb install -r android/app/build/outputs/apk/debug/app-debug.apk" || exit 6 | |
|      | |
|     log_success "APK deployed successfully to device!" | |
|     log_info "You can now run the app with: npx cap run android" | |
|     exit 0 | |
| fi | |
| 
 | |
| # Step 1: Validate asset configuration | |
| safe_execute "Validating asset configuration" "npm run assets:validate" || { | |
|     log_warn "Asset validation found issues, but continuing with build..." | |
|     log_info "If you encounter build failures, please run 'npm install' first to ensure all dependencies are available." | |
| } | |
| 
 | |
| # Step 2: Clean Android app | |
| safe_execute "Cleaning Android app" "npm run clean:android" || exit 1 | |
| 
 | |
| # Step 3: Clean dist directory | |
| log_info "Cleaning dist directory..." | |
| clean_build_artifacts "dist" | |
| 
 | |
| # Step 4: Build Capacitor version with mode | |
| if [ "$BUILD_MODE" = "development" ]; then | |
|     safe_execute "Building Capacitor version (development)" "npm run build:capacitor" || exit 3 | |
| elif [ "$BUILD_MODE" = "test" ]; then | |
|     safe_execute "Building Capacitor version (test)" "npm run build:capacitor -- --mode test" || exit 3 | |
| elif [ "$BUILD_MODE" = "production" ]; then | |
|     safe_execute "Building Capacitor version (production)" "npm run build:capacitor -- --mode production" || exit 3 | |
| fi | |
| 
 | |
| # Step 5: Clean Gradle build | |
| safe_execute "Cleaning Gradle build" "cd android && ./gradlew clean && cd .." || exit 4 | |
| 
 | |
| # Step 6: Build based on type | |
| if [ "$BUILD_TYPE" = "debug" ]; then | |
|     safe_execute "Assembling debug build" "cd android && ./gradlew assembleDebug && cd .." || exit 5 | |
| elif [ "$BUILD_TYPE" = "release" ]; then | |
|     safe_execute "Assembling release build" "cd android && ./gradlew assembleRelease && cd .." || exit 5 | |
| fi | |
| 
 | |
| # Step 7: Sync with Capacitor | |
| safe_execute "Syncing with Capacitor" "npx cap sync android" || exit 6 | |
| 
 | |
| # Step 8: Generate assets | |
| safe_execute "Generating assets" "npx capacitor-assets generate --android" || exit 7 | |
| 
 | |
| # Step 9: Build APK/AAB if requested | |
| if [ "$BUILD_APK" = true ]; then | |
|     if [ "$BUILD_TYPE" = "debug" ]; then | |
|         safe_execute "Building debug APK" "cd android && ./gradlew assembleDebug && cd .." || exit 5 | |
|     else | |
|         safe_execute "Building release APK" "cd android && ./gradlew assembleRelease && cd .." || exit 5 | |
|     fi | |
| fi | |
| 
 | |
| if [ "$BUILD_AAB" = true ]; then | |
|     safe_execute "Building AAB" "cd android && ./gradlew bundleRelease && cd .." || exit 5 | |
| fi | |
| 
 | |
| # Step 10: Auto-run app if requested | |
| if [ "$AUTO_RUN" = true ]; then | |
|     log_step "Auto-running Android app..." | |
|     safe_execute "Launching app" "npx cap run android" || { | |
|         log_error "Failed to launch Android app" | |
|         log_info "You can manually run with: npx cap run android" | |
|         exit 9 | |
|     } | |
|     log_success "Android app launched successfully!" | |
| fi | |
| 
 | |
| # Step 11: Open Android Studio if requested | |
| if [ "$OPEN_STUDIO" = true ]; then | |
|     safe_execute "Opening Android Studio" "npx cap open android" || exit 8 | |
| fi | |
| 
 | |
| # Print build summary | |
| log_success "Android build completed successfully!" | |
| log_info "Build mode: $BUILD_MODE" | |
| log_info "Build type: $BUILD_TYPE" | |
| if [ "$BUILD_APK" = true ]; then | |
|     log_info "APK build: completed" | |
| fi | |
| if [ "$BUILD_AAB" = true ]; then | |
|     log_info "AAB build: completed" | |
| fi | |
| if [ "$AUTO_RUN" = true ]; then | |
|     log_info "Auto-run: completed" | |
| fi | |
| if [ "$OPEN_STUDIO" = true ]; then | |
|     log_info "Android Studio: opened" | |
| fi | |
| 
 | |
| # Reminder about dependency management | |
| log_info "💡 Tip: If you encounter dependency issues, run 'npm install' to ensure all packages are up to date." | |
| 
 | |
| print_footer "Android Build" | |
| 
 | |
| # Exit with success | |
| exit 0  |