Files
FadCam/build.sh

394 lines
15 KiB
Bash
Executable File

#!/bin/zsh
# FadCam Build Script with TUI & Progress Bar
# Interactive menu when run without arguments
set -e
# Clear screen immediately
clear
export JAVA_HOME="/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"
# ANSI Colors (Red Theme)
RED='\033[0;31m'
DARK_RED='\033[38;5;52m'
BRIGHT_RED='\033[1;31m'
WHITE='\033[1;37m'
GRAY='\033[0;37m'
DIM_GRAY='\033[2;37m'
RESET='\033[0m'
CLEAR='\033c'
BOLD='\033[1m'
# Cursor control
HIDE_CURSOR='\033[?25l'
SHOW_CURSOR='\033[?25h'
SAVE_POS='\033[s'
RESTORE_POS='\033[u'
CLEAR_LINE='\033[2K'
MOVE_UP='\033[1A'
# Progress bar function
progress_bar() {
local current=$1
local total=$2
local width=40
local percent=$((current * 100 / total))
local filled=$((current * width / total))
local empty=$((width - filled))
printf "${RED}["
printf "${BRIGHT_RED}"
printf "%${filled}s" | tr ' ' '█'
printf "${DARK_RED}"
printf "%${empty}s" | tr ' ' '░'
printf "${RED}]${RESET} ${percent}%% (${current}/${total})"
}
# TUI Box drawing
draw_box() {
local width=$1
local title=$2
printf "${RED}$(printf '─%.0s' {1..$((width-2))})${RESET}\n"
if [ -n "$title" ]; then
printf "${RED}${RESET} ${BRIGHT_RED}${BOLD}${title}${RESET}${RED}${RESET}\n"
printf "${RED}$(printf '─%.0s' {1..$((width-2))})${RESET}\n"
fi
}
close_box() {
local width=$1
printf "${RED}$(printf '─%.0s' {1..$((width-2))})${RESET}\n"
}
# Print header
print_header() {
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BRIGHT_RED}${BOLD}$1${RESET}"
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
}
# Print status
print_status() {
echo -e "$1 $2"
}
# Show help
show_help() {
clear
echo ""
echo -e "${BRIGHT_RED}${BOLD}╔══════════════════════════════════════╗${RESET}"
echo -e "${BRIGHT_RED}${BOLD}║ 🔴 FadCam Build Manager 🔴 ║${RESET}"
echo -e "${BRIGHT_RED}${BOLD}╚══════════════════════════════════════╝${RESET}"
echo ""
echo -e "${BRIGHT_RED}${BOLD}Usage:${RESET}"
echo -e "${WHITE} ./build.sh${RESET}${DIM_GRAY} # Interactive menu${RESET}"
echo -e "${WHITE} ./build.sh debug true${RESET}${DIM_GRAY} # Build & install debug APK${RESET}"
echo -e "${WHITE} ./build.sh release true${RESET}${DIM_GRAY} # Build & install release APK${RESET}"
echo -e "${WHITE} ./build.sh debug false${RESET}${DIM_GRAY} # Build debug APK only${RESET}"
echo -e "${WHITE} ./build.sh release false${RESET}${DIM_GRAY} # Build release APK only${RESET}"
echo -e "${WHITE} ./build.sh --help${RESET}${DIM_GRAY} # Show this help${RESET}"
echo ""
echo -e "${BRIGHT_RED}${BOLD}Menu Options (Interactive Mode):${RESET}"
echo -e "${BRIGHT_RED}[1]${RESET} ${WHITE}Build & Install Debug${RESET}${DIM_GRAY} (default)${RESET}"
echo -e "${BRIGHT_RED}[2]${RESET} ${WHITE}Build & Install Release${RESET}"
echo -e "${BRIGHT_RED}[3]${RESET} ${WHITE}Build Only (Debug)${RESET}"
echo -e "${BRIGHT_RED}[4]${RESET} ${WHITE}Build Only (Release)${RESET}"
echo -e "${BRIGHT_RED}[5]${RESET} ${WHITE}Clean Build Cache${RESET}"
echo -e "${BRIGHT_RED}[?]${RESET} ${WHITE}Show Help${RESET}"
echo -e "${BRIGHT_RED}[0]${RESET} ${WHITE}Exit${RESET}"
echo ""
echo -e "${BRIGHT_RED}${BOLD}Examples:${RESET}"
echo -e "${DIM_GRAY} • Build and test debug: ./build.sh debug true${RESET}"
echo -e "${DIM_GRAY} • Build for release: ./build.sh release false${RESET}"
echo -e "${DIM_GRAY} • Clean cache first, then build: use menu [5] then [1]${RESET}"
echo ""
}
# Show interactive menu
show_menu() {
clear
echo ""
echo -e "${BRIGHT_RED}${BOLD}╔══════════════════════════════════════╗${RESET}"
echo -e "${BRIGHT_RED}${BOLD}║ 🔴 FadCam Build Manager 🔴 ║${RESET}"
echo -e "${BRIGHT_RED}${BOLD}╚══════════════════════════════════════╝${RESET}"
echo ""
echo -e "${RED}Available Options:${RESET}"
echo ""
echo -e "${BRIGHT_RED}[1]${RESET} ${WHITE}Build & Install Debug${RESET}${DIM_GRAY} (default)${RESET}"
echo -e "${BRIGHT_RED}[2]${RESET} ${WHITE}Build & Install Release${RESET}"
echo -e "${BRIGHT_RED}[3]${RESET} ${WHITE}Build Only (Debug)${RESET}"
echo -e "${BRIGHT_RED}[4]${RESET} ${WHITE}Build Only (Release)${RESET}"
echo -e "${BRIGHT_RED}[5]${RESET} ${WHITE}Clean Build Cache${RESET}"
echo -e "${BRIGHT_RED}[?]${RESET} ${WHITE}Help${RESET}"
echo -e "${BRIGHT_RED}[0]${RESET} ${WHITE}Exit${RESET}"
echo ""
echo -en "${RED}Choose option (default 1): ${RESET}"
}
# Execute build
execute_build() {
local BUILD_TYPE=$1
local INSTALL=$2
local ACTION_NAME=$3
if [ "$BUILD_TYPE" = "debug" ]; then
GRADLE_TASK="assembleDefaultDebug"
APK_PATTERN="*debug.apk"
else
GRADLE_TASK="assembleDefaultRelease"
APK_PATTERN="*release.apk"
fi
print_header "$ACTION_NAME"
echo -e "${RED}Task:${RESET} ${WHITE}${GRADLE_TASK}${RESET}"
echo -e "${RED}Install:${RESET} ${WHITE}${INSTALL}${RESET}"
echo ""
# Build with real output
print_status "🔨" "Building APK..."
echo ""
# Run actual build and capture output
local BUILD_LOG=$(mktemp)
local BUILD_START=$(date +%s)
local LAST_LINE_COUNT=0
local LAST_PROGRESS_LINE=""
# Start build in background and monitor output
./gradlew "$GRADLE_TASK" --no-daemon --warning-mode=summary > "$BUILD_LOG" 2>&1 &
local BUILD_PID=$!
# Show live output and progress bar while building
while kill -0 $BUILD_PID 2>/dev/null; do
sleep 1.5
# Show last 2 lines of output that haven't been shown yet
if [ -f "$BUILD_LOG" ]; then
local current_lines=$(wc -l < "$BUILD_LOG")
if [ $current_lines -gt $LAST_LINE_COUNT ]; then
# Show up to 2 new lines
local start_line=$((LAST_LINE_COUNT + 1))
local end_line=$current_lines
local show_lines=$((end_line - start_line + 1))
if [ $show_lines -gt 2 ]; then
start_line=$((end_line - 1))
show_lines=2
fi
if [ $show_lines -gt 0 ]; then
# Erase previous progress line before printing new output
if [ -n "$LAST_PROGRESS_LINE" ]; then
printf "\r\033[0K"
fi
sed -n "${start_line},${end_line}p" "$BUILD_LOG" | while read -r line; do
if [ -n "$line" ] && [[ ! "$line" =~ ^[[:space:]]*$ ]]; then
echo -e "${DIM_GRAY} $line${RESET}"
fi
done
fi
LAST_LINE_COUNT=$current_lines
fi
# Show progress bar estimate on same line (without newline)
local elapsed=$(($(date +%s) - BUILD_START))
local progress=$((elapsed * 100 / 45)) # Estimate ~45s build
if [ $progress -gt 95 ]; then progress=95; fi
# Create progress bar with correct character count
local filled=$((progress / 2)) # 0-50 characters
local empty=$((50 - filled))
local bar=$(printf "%-${filled}s" | tr ' ' '█')
local empty_bar=$(printf "%-${empty}s" | tr ' ' '░')
# Print progress line with carriage return to update same line
printf "\r${RED}[${BRIGHT_RED}${bar}${DARK_RED}${empty_bar}${RED}]${RESET} ${progress}%% (${progress}/100)\033[0K"
LAST_PROGRESS_LINE="shown"
fi
done
wait $BUILD_PID
local BUILD_EXIT=$?
local BUILD_END=$(date +%s)
local BUILD_TIME=$((BUILD_END - BUILD_START))
# Final progress bar at 100%
printf "\r${RED}[${BRIGHT_RED}$(printf '%-50s' | tr ' ' '█')${RED}]${RESET} 100%% (100/100)\033[0K\n"
echo ""
if [ $BUILD_EXIT -eq 0 ]; then
print_status "✅" "Build completed in ${BUILD_TIME}s"
echo ""
else
print_status "❌" "Build FAILED after ${BUILD_TIME}s"
echo ""
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo -e "${BRIGHT_RED}Build Output:${RESET}"
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
cat "$BUILD_LOG"
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
rm -f "$BUILD_LOG"
return 1
fi
rm -f "$BUILD_LOG"
# Find APK
APK_PATH=$(find app/build/outputs/apk -name "$APK_PATTERN" -type f 2>/dev/null | head -1)
if [ -z "$APK_PATH" ]; then
print_status "❌" "APK not found in output directory"
echo -e "${DIM_GRAY}Expected to find: app/build/outputs/apk/**/$APK_PATTERN${RESET}"
echo ""
return 1
fi
APK_DIR=$(dirname "$APK_PATH")
APK_NAME=$(basename "$APK_PATH")
APK_SIZE=$(ls -lh "$APK_PATH" | awk '{print $5}')
# Show APK info
print_header "📦 APK Information"
print_status "📁" "Directory: ${BRIGHT_RED}${APK_DIR#$(pwd)/}${RESET}"
print_status "📄" "Filename: ${BRIGHT_RED}${APK_NAME}${RESET}"
print_status "💾" "Size: ${BRIGHT_RED}${APK_SIZE}${RESET}"
echo ""
# Install if requested using Gradle (professional Android approach)
if [ "$INSTALL" = "true" ]; then
# Capitalize first letter of BUILD_TYPE for Gradle task (e.g., debug -> Debug)
local BUILD_TYPE_UPPER=$(echo "$BUILD_TYPE" | tr '[:lower:]' '[:upper:]' | cut -c1)$(echo "$BUILD_TYPE" | cut -c2-)
# Show device info using standard adb approach
local DEVICE_COUNT=$(adb devices | grep -E '^[a-zA-Z0-9].*[[:space:]]device$' | wc -l)
if [ $DEVICE_COUNT -eq 0 ]; then
print_status "⚠️" "No connected devices found"
echo ""
return 0
fi
print_status "📱" "Connected Devices: ${BRIGHT_RED}${DEVICE_COUNT}${RESET}"
adb devices | grep -E '^[a-zA-Z0-9].*[[:space:]]device$' | while read -r device _; do
local DEVICE_MODEL=$(adb -s "$device" shell getprop ro.product.model 2>/dev/null || echo "Unknown")
local DEVICE_VERSION=$(adb -s "$device" shell getprop ro.build.version.release 2>/dev/null || echo "?")
echo -e " ${BRIGHT_RED}[${device:0:16}]${RESET} ${WHITE}${DEVICE_MODEL}${RESET} - ${GRAY}Android ${DEVICE_VERSION}${RESET}"
done
echo ""
print_status "📥" "Installing to all connected devices..."
# Use gradle's standard install task (Android official way)
local INSTALL_TASK="app:installDefault${BUILD_TYPE_UPPER}"
if ./gradlew "$INSTALL_TASK" --no-daemon 2>&1 | tail -3; then
print_status "✅" "Installation successful on ${BRIGHT_RED}${DEVICE_COUNT}${RESET} device(s)"
# Launch using Android's official am start command (not gradle run which doesn't exist)
echo ""
print_status "🚀" "Launching app on all devices..."
local PACKAGE_NAME="com.fadcam.beta"
[ "$BUILD_TYPE" = "release" ] && PACKAGE_NAME="com.fadcam"
# Use am start with proper activity path - this is the Android standard
adb devices | grep -E '^[a-zA-Z0-9].*[[:space:]]device$' | while read -r device _; do
# Launch the app (Android's official way) - note: full class path needed
if adb -s "$device" shell am start -W -n "${PACKAGE_NAME}/com.fadcam.SplashActivity" 2>/dev/null; then
echo -e " ${BRIGHT_RED}[${device:0:16}]${RESET} ✅ Launched successfully"
else
echo -e " ${BRIGHT_RED}[${device:0:16}]${RESET} ⚠️ Launch may have failed"
fi
done
else
print_status "❌" "Installation failed"
echo ""
return 1
fi
else
print_status "💡" "APK ready: ${BRIGHT_RED}${APK_PATH}${RESET}"
fi
echo ""
print_header "✨ Build Complete!"
echo ""
return 0
}
# Main logic
if [ $# -eq 0 ]; then
# Interactive menu mode
while true; do
show_menu
read -r choice
choice=${choice:-1}
case $choice in
1|"")
execute_build "debug" "true" "🔴 Debug Build & Install"
;;
2)
execute_build "release" "true" "🔴 Release Build & Install"
;;
3)
execute_build "debug" "false" "🔴 Debug Build Only"
;;
4)
execute_build "release" "false" "🔴 Release Build Only"
;;
5)
print_header "🔴 Cleaning Build Cache"
print_status "🧹" "Clearing Gradle cache..."
./gradlew clean --quiet --no-daemon
print_status "✅" "Cache cleaned"
echo ""
;;
"?"|h|H)
show_help
echo -en "${RED}Press Enter to continue...${RESET}"
read
;;
0)
echo -e "${RED}Goodbye!${RESET}"
exit 0
;;
*)
print_status "❌" "Invalid option. Please try again."
sleep 2
;;
esac
if [[ "$choice" =~ ^[0-5]$ ]]; then
if [ $choice -ne 5 ] && [ $choice -ne 0 ]; then
echo -en "${RED}Press Enter to continue...${RESET}"
read
fi
elif [[ "$choice" == "?" || "$choice" == "h" || "$choice" == "H" ]]; then
# Help option, continue to next menu iteration
:
fi
done
else
# Direct execution mode or help
if [ "$1" = "--help" ] || [ "$1" = "-h" ] || [ "$1" = "help" ]; then
show_help
else
BUILD_TYPE=${1:-debug}
INSTALL=${2:-true}
if [ "$BUILD_TYPE" = "debug" ]; then
ACTION_NAME="🔴 Debug Build"
else
ACTION_NAME="🔴 Release Build"
fi
if [ "$INSTALL" = "true" ]; then
ACTION_NAME="$ACTION_NAME & Install"
fi
execute_build "$BUILD_TYPE" "$INSTALL" "$ACTION_NAME"
fi
fi