Last active
April 28, 2025 13:08
-
-
Save sergchil/694544de77ec6c4f4b86238dcae86174 to your computer and use it in GitHub Desktop.
A bash script to easily manage and run Android Emulators with a nice interactive terminal interface.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Android Emulator launcher script | |
# Created: April 28, 2025 | |
# Author: Sergey Chilingaryan | |
# | |
# This script helps manage and run Android emulators with common configuration options. | |
# It detects the Android SDK location, lists available emulators with interactive | |
# arrow key navigation, and launches them with customizable options. | |
# Setup terminal for interactive mode | |
stty -echo | |
tput civis # Hide cursor | |
# Restore terminal settings on exit | |
cleanup() { | |
tput cnorm # Show cursor | |
stty echo | |
exit 0 | |
} | |
trap cleanup EXIT | |
trap cleanup INT | |
# Colors for better readability | |
RED='\033[0;31m' | |
GREEN='\033[0;32m' | |
YELLOW='\033[0;33m' | |
BLUE='\033[0;34m' | |
CYAN='\033[0;36m' | |
WHITE='\033[1;37m' | |
BOLD='\033[1m' | |
NC='\033[0m' # No Color | |
# Symbols for menu | |
POINTER="➤" | |
UNSELECTED=" " | |
# Find Android SDK location | |
if [ -d "$HOME/Library/Android/sdk" ]; then | |
ANDROID_SDK="$HOME/Library/Android/sdk" | |
elif [ -d "$ANDROID_HOME" ]; then | |
ANDROID_SDK="$ANDROID_HOME" | |
elif [ -d "$ANDROID_SDK_ROOT" ]; then | |
ANDROID_SDK="$ANDROID_SDK_ROOT" | |
else | |
echo -e "${RED}Error: Android SDK not found.${NC}" | |
echo "Please set ANDROID_HOME or ANDROID_SDK_ROOT environment variable." | |
exit 1 | |
fi | |
# Define paths to Android tools | |
EMULATOR="$ANDROID_SDK/emulator/emulator" | |
AVDMANAGER="$ANDROID_SDK/cmdline-tools/latest/bin/avdmanager" | |
SDKMANAGER="$ANDROID_SDK/cmdline-tools/latest/bin/sdkmanager" | |
# Check if emulator exists | |
if [ ! -f "$EMULATOR" ]; then | |
# Try alternate locations | |
if [ -f "$ANDROID_SDK/tools/emulator" ]; then | |
EMULATOR="$ANDROID_SDK/tools/emulator" | |
else | |
echo -e "${RED}Error: Emulator not found at $EMULATOR${NC}" | |
echo -e "${YELLOW}Please ensure the Android emulator is installed correctly.${NC}" | |
exit 1 | |
fi | |
fi | |
# Check for avdmanager in different locations | |
if [ ! -f "$AVDMANAGER" ]; then | |
# Try alternate locations | |
if [ -f "$ANDROID_SDK/tools/bin/avdmanager" ]; then | |
AVDMANAGER="$ANDROID_SDK/tools/bin/avdmanager" | |
fi | |
fi | |
# Function to get all available emulators | |
get_emulators() { | |
emulators=$("$EMULATOR" -list-avds) | |
if [ $? -ne 0 ] || [ -z "$emulators" ]; then | |
echo -e "${YELLOW}No emulators found. Create one using Android Studio or avdmanager.${NC}" | |
exit 0 | |
fi | |
# Convert string to array using a more compatible method | |
EMULATOR_ARRAY=() | |
while IFS= read -r line; do | |
[ -n "$line" ] && EMULATOR_ARRAY+=("$line") | |
done <<< "$emulators" | |
# Verify we have emulators | |
if [ ${#EMULATOR_ARRAY[@]} -eq 0 ]; then | |
echo -e "${YELLOW}No emulators found. Create one using Android Studio or avdmanager.${NC}" | |
exit 0 | |
fi | |
} | |
# Function to read a single keypress | |
read_key() { | |
# Use a simpler method to read keys without timeout | |
escape_char=$(printf "\033") | |
IFS= read -r -s -n1 key | |
# Handle multi-character sequences like arrow keys | |
if [[ $key = $escape_char ]]; then | |
# It's an escape sequence | |
read -r -s -n1 k1 | |
read -r -s -n1 k2 | |
key="${escape_char}${k1}${k2}" | |
fi | |
echo "$key" | |
} | |
# Function to clear screen and display header | |
display_header() { | |
clear | |
echo -e "${GREEN}${BOLD}========================================${NC}" | |
echo -e "${GREEN}${BOLD} Android Emulator Launcher${NC}" | |
echo -e "${GREEN}${BOLD}========================================${NC}" | |
echo | |
} | |
# Function to display emulator list with selection | |
display_emulator_list() { | |
display_header | |
local selected_idx=$1 | |
local total=${#EMULATOR_ARRAY[@]} | |
echo -e "${BLUE}${BOLD}Available Android Virtual Devices:${NC}" | |
echo -e "${YELLOW}Use arrow keys to navigate, Enter to select${NC}" | |
echo | |
for i in "${!EMULATOR_ARRAY[@]}"; do | |
if [ "$i" -eq "$selected_idx" ]; then | |
echo -e "${CYAN}${POINTER} ${WHITE}${BOLD}${EMULATOR_ARRAY[$i]}${NC}" | |
else | |
echo -e "${UNSELECTED} ${EMULATOR_ARRAY[$i]}" | |
fi | |
done | |
} | |
# Function to select an emulator using arrow keys | |
select_emulator() { | |
get_emulators | |
# If no emulators found, exit | |
if [ ${#EMULATOR_ARRAY[@]} -eq 0 ]; then | |
echo -e "${YELLOW}No emulators found. Create one using Android Studio or avdmanager.${NC}" | |
exit 0 | |
fi | |
local selected=0 | |
local key="" | |
display_emulator_list $selected | |
while true; do | |
key=$(read_key) | |
case "$key" in | |
$'\e[A') # Up arrow | |
((selected--)) | |
if [ $selected -lt 0 ]; then | |
selected=$((${#EMULATOR_ARRAY[@]} - 1)) | |
fi | |
display_emulator_list $selected | |
;; | |
$'\e[B') # Down arrow | |
((selected++)) | |
if [ $selected -ge ${#EMULATOR_ARRAY[@]} ]; then | |
selected=0 | |
fi | |
display_emulator_list $selected | |
;; | |
'') # Enter key | |
AVD_NAME="${EMULATOR_ARRAY[$selected]}" | |
break | |
;; | |
'q') # Quit | |
echo | |
echo "Exiting..." | |
exit 0 | |
;; | |
esac | |
done | |
} | |
# Function to display boot options menu | |
display_boot_options() { | |
display_header | |
local selected_idx=$1 | |
local options=("Run normally" "Run with cold boot" "Run with wipe data" "Run with wipe data and cold boot") | |
echo -e "${BLUE}${BOLD}Selected Emulator: ${WHITE}${AVD_NAME}${NC}" | |
echo -e "${YELLOW}Use arrow keys to navigate, Enter to select${NC}" | |
echo | |
for i in "${!options[@]}"; do | |
if [ "$i" -eq "$selected_idx" ]; then | |
echo -e "${CYAN}${POINTER} ${WHITE}${BOLD}${options[$i]}${NC}" | |
else | |
echo -e "${UNSELECTED} ${options[$i]}" | |
fi | |
done | |
} | |
# Function to select boot option using arrow keys | |
select_boot_option() { | |
local selected=0 | |
local key="" | |
local options=("Run normally" "Run with cold boot" "Run with wipe data" "Run with wipe data and cold boot") | |
# Set default emulator options | |
EMULATOR_OPTS="" | |
display_boot_options $selected | |
while true; do | |
key=$(read_key) | |
case "$key" in | |
$'\e[A') # Up arrow | |
((selected--)) | |
if [ $selected -lt 0 ]; then | |
selected=$((${#options[@]} - 1)) | |
fi | |
display_boot_options $selected | |
;; | |
$'\e[B') # Down arrow | |
((selected++)) | |
if [ $selected -ge ${#options[@]} ]; then | |
selected=0 | |
fi | |
display_boot_options $selected | |
;; | |
'') # Enter key | |
case $selected in | |
0) # Run normally | |
EMULATOR_OPTS="" | |
;; | |
1) # Run with cold boot | |
EMULATOR_OPTS="$EMULATOR_OPTS -no-boot-anim -no-snapshot" | |
;; | |
2) # Run with wipe data | |
EMULATOR_OPTS="$EMULATOR_OPTS -wipe-data" | |
;; | |
3) # Run with wipe data and cold boot | |
EMULATOR_OPTS="$EMULATOR_OPTS -wipe-data -no-boot-anim -no-snapshot" | |
;; | |
esac | |
break | |
;; | |
'q') # Quit | |
echo | |
echo "Exiting..." | |
exit 0 | |
;; | |
esac | |
done | |
} | |
# Function to display help menu | |
show_help() { | |
echo -e "${GREEN}Android Emulator Launcher${NC}" | |
echo "Usage: $0 [options] [avd_name]" | |
echo | |
echo "Options:" | |
echo " -h, --help Show this help message" | |
echo " -l, --list List available emulators" | |
echo " -n, --no-snapshot Start without loading snapshot" | |
echo " -w, --wipe-data Wipe user data" | |
echo " -c, --cold-boot Perform a cold boot (no quickboot)" | |
echo " -g, --gpu MODE Set GPU emulation mode (auto, host, swiftshader, angle)" | |
echo " -d, --debug Run with debug logging enabled" | |
echo | |
echo "Examples:" | |
echo " $0 --list # List all AVDs" | |
echo " $0 Pixel_4_API_30 # Run the Pixel_4_API_30 emulator" | |
echo " $0 -n -w Pixel_4_API_30 # Run with no snapshot and wipe data" | |
echo | |
} | |
# Advanced options menu | |
display_advanced_options() { | |
display_header | |
local selected_idx=$1 | |
local options=( | |
"GPU Mode: auto" | |
"GPU Mode: host" | |
"GPU Mode: swiftshader" | |
"Debug Mode: Off" | |
"Debug Mode: On" | |
"Done - Return to previous menu" | |
) | |
echo -e "${BLUE}${BOLD}Advanced Options${NC}" | |
echo -e "${YELLOW}Use arrow keys to navigate, Enter to toggle option${NC}" | |
echo | |
for i in "${!options[@]}"; do | |
if [ "$i" -eq "$selected_idx" ]; then | |
echo -e "${CYAN}${POINTER} ${WHITE}${BOLD}${options[$i]}${NC}" | |
else | |
echo -e "${UNSELECTED} ${options[$i]}" | |
fi | |
done | |
} | |
# Function to select advanced options | |
select_advanced_options() { | |
local selected=0 | |
local key="" | |
local GPU_MODE="auto" | |
local DEBUG_MODE="off" | |
local return_to_main=false | |
while [ "$return_to_main" = false ]; do | |
display_advanced_options $selected | |
key=$(read_key) | |
case "$key" in | |
$'\e[A') # Up arrow | |
((selected--)) | |
if [ $selected -lt 0 ]; then | |
selected=5 | |
fi | |
;; | |
$'\e[B') # Down arrow | |
((selected++)) | |
if [ $selected -gt 5 ]; then | |
selected=0 | |
fi | |
;; | |
'') # Enter key | |
case $selected in | |
0) # GPU Mode: auto | |
GPU_MODE="auto" | |
EMULATOR_OPTS=$(echo "$EMULATOR_OPTS" | sed 's/-gpu [^ ]*//g') | |
EMULATOR_OPTS="$EMULATOR_OPTS -gpu auto" | |
;; | |
1) # GPU Mode: host | |
GPU_MODE="host" | |
EMULATOR_OPTS=$(echo "$EMULATOR_OPTS" | sed 's/-gpu [^ ]*//g') | |
EMULATOR_OPTS="$EMULATOR_OPTS -gpu host" | |
;; | |
2) # GPU Mode: swiftshader | |
GPU_MODE="swiftshader" | |
EMULATOR_OPTS=$(echo "$EMULATOR_OPTS" | sed 's/-gpu [^ ]*//g') | |
EMULATOR_OPTS="$EMULATOR_OPTS -gpu swiftshader" | |
;; | |
3) # Debug Mode: Off | |
DEBUG_MODE="off" | |
EMULATOR_OPTS=$(echo "$EMULATOR_OPTS" | sed 's/-verbose//g') | |
;; | |
4) # Debug Mode: On | |
DEBUG_MODE="on" | |
EMULATOR_OPTS="$EMULATOR_OPTS -verbose" | |
;; | |
5) # Done | |
return_to_main=true | |
;; | |
esac | |
;; | |
'q') # Quit | |
echo | |
echo "Exiting..." | |
exit 0 | |
;; | |
esac | |
done | |
} | |
# Main function to run the interactive menu system | |
main_menu() { | |
# Default emulator options | |
EMULATOR_OPTS="" | |
# Step 1: Select emulator | |
select_emulator | |
# Step 2: Select boot options | |
select_boot_option | |
# Launch the emulator | |
display_header | |
# Create logs directory if it doesn't exist | |
LOGS_DIR="$HOME/.android/emulator_logs" | |
mkdir -p "$LOGS_DIR" | |
# Create log file with timestamp | |
TIMESTAMP=$(date +"%Y%m%d_%H%M%S") | |
LOG_FILE="$LOGS_DIR/${AVD_NAME}_${TIMESTAMP}.log" | |
echo -e "${GREEN}Launching Android emulator: ${WHITE}${BOLD}${AVD_NAME}${NC}" | |
echo -e "${BLUE}With options: ${WHITE}${EMULATOR_OPTS}${NC}" | |
echo | |
echo -e "${GREEN}Emulator will run in the background and continue running after terminal is closed.${NC}" | |
echo -e "${BLUE}Log file: ${WHITE}${LOG_FILE}${NC}" | |
echo | |
# Show cursor again before running the emulator | |
tput cnorm | |
stty echo | |
# Launch the emulator in the background using nohup | |
nohup "$EMULATOR" -avd "$AVD_NAME" $EMULATOR_OPTS > "$LOG_FILE" 2>&1 & | |
# Store the PID | |
EMULATOR_PID=$! | |
echo -e "${YELLOW}Emulator started with PID: ${WHITE}${EMULATOR_PID}${NC}" | |
echo -e "${YELLOW}To stop the emulator later, run: ${WHITE}kill $EMULATOR_PID${NC}" | |
echo | |
# Wait a moment to make sure the emulator starts | |
sleep 2 | |
# Check if the process is still running | |
if ps -p $EMULATOR_PID > /dev/null; then | |
echo -e "${GREEN}Emulator is running successfully in the background.${NC}" | |
return 0 | |
else | |
echo -e "${RED}Failed to start emulator. Check the log file: ${WHITE}${LOG_FILE}${NC}" | |
return 1 | |
fi | |
} | |
# Parse command line arguments | |
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then | |
show_help | |
exit 0 | |
fi | |
# Call main menu to start the interactive interface | |
main_menu | |
# Exit with the status from the emulator | |
exit $? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment