| name | build-android-app |
| description | Build, install, and launch an Android app on an emulator after code changes. Auto-detects Gradle wrapper, build variant, application ID, and emulator. Use after making code changes to verify compilation, when the user says "build the app", "run the app", "launch on emulator", or when a workspace rule says to build after changes. |
Build Android App on Emulator
After making code changes, build and launch the app on an Android Emulator to verify compilation.
Step 1: Detect Project Settings
Run these commands to discover project configuration. Cache the results for the session.
Find Gradle wrapper
if [ -f "./gradlew" ]; then
GRADLE_CMD="./gradlew"
elif [ -f "../gradlew" ]; then
GRADLE_CMD="../gradlew"
else
echo "ERROR: No gradlew found. Ensure you are in the project root."
exit 1
fi
echo "GRADLE_CMD: $GRADLE_CMD"
Find build variant
$GRADLE_CMD :app:tasks --group=build 2>/dev/null | grep -oP 'assemble\K[A-Z][a-zA-Z]+' | sort -u
Pick the debug variant for local development. Prefer internalDebug if available (common for apps with internal/production flavors), otherwise fall back to debug.
Store the result as BUILD_VARIANT (e.g. internalDebug). Derive the Gradle task as assemble${BUILD_VARIANT} (e.g. assembleInternalDebug).
Find application ID
$GRADLE_CMD :app:properties 2>/dev/null | grep 'applicationId' | head -1 | awk '{print $NF}'
If that doesn't return results, parse app/build.gradle or app/build.gradle.kts:
grep -E 'applicationId\s' app/build.gradle* 2>/dev/null | head -1 | grep -oP '"[^"]+"' | tr -d '"'
For flavored builds, the application ID may have a suffix. Check the flavor block for applicationIdSuffix to get the full ID.
Store the result as APP_ID.
Find emulator (project-branch naming convention)
Emulators are named {project}_{git_branch}, where project is derived from the git remote URL and git_branch is the current git branch. Underscores replace characters that are invalid in AVD names. This ensures each project+branch combination has its own isolated emulator.
PROJECT=$(git remote get-url origin | sed 's/.*\///' | sed 's/\.git$//')
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
AVD_NAME=$(echo "${PROJECT}_${GIT_BRANCH}" | sed 's/[^a-zA-Z0-9._-]/_/g')
echo "AVD_NAME: $AVD_NAME"
Check if the emulator exists. If not, create it using a recent Pixel device and the latest installed system image:
AVD_EXISTS=$($ANDROID_HOME/emulator/emulator -list-avds 2>/dev/null | grep -x "$AVD_NAME")
if [ -z "$AVD_EXISTS" ]; then
SYSTEM_IMAGE=$(sdkmanager --list_installed 2>/dev/null | grep 'system-images' | grep 'google_apis' | grep -v 'google_apis_playstore' | tail -1 | awk '{print $1}')
if [ -z "$SYSTEM_IMAGE" ]; then
SYSTEM_IMAGE=$(sdkmanager --list_installed 2>/dev/null | grep 'system-images' | tail -1 | awk '{print $1}')
fi
if [ -z "$SYSTEM_IMAGE" ]; then
echo "ERROR: No system images installed. Install one via Android Studio SDK Manager or:"
echo " sdkmanager 'system-images;android-35;google_apis;x86_64'"
exit 1
fi
echo "Creating AVD '$AVD_NAME' with system image $SYSTEM_IMAGE"
echo "no" | avdmanager create avd -n "$AVD_NAME" -k "$SYSTEM_IMAGE" -d "pixel_6"
echo "Created: $AVD_NAME"
else
echo "AVD '$AVD_NAME' already exists"
fi
Boot emulator
Multiple emulators can run simultaneously (one per worktree/branch). NEVER shut down other running emulators — they belong to other worktrees.
Each running emulator gets a unique serial (emulator-5554, emulator-5556, etc.). To find this AVD's serial, query the AVD name property on each running emulator.
SERIALS_BEFORE=$(adb devices | grep 'emulator-' | awk '{print $1}')
DEVICE_SERIAL=""
for SERIAL in $SERIALS_BEFORE; do
AVD_ON_SERIAL=$(adb -s "$SERIAL" emu avd name 2>/dev/null | head -1 | tr -d '\r')
if [ "$AVD_ON_SERIAL" = "$AVD_NAME" ]; then
DEVICE_SERIAL="$SERIAL"
echo "AVD '$AVD_NAME' already running on $DEVICE_SERIAL"
break
fi
done
if [ -z "$DEVICE_SERIAL" ]; then
nohup $ANDROID_HOME/emulator/emulator -avd "$AVD_NAME" -no-snapshot-load &>/dev/null &
echo "Booting emulator '$AVD_NAME'..."
while true; do
sleep 2
for SERIAL in $(adb devices | grep 'emulator-' | awk '{print $1}'); do
if ! echo "$SERIALS_BEFORE" | grep -qx "$SERIAL"; then
DEVICE_SERIAL="$SERIAL"
break 2
fi
done
done
echo "New emulator appeared on $DEVICE_SERIAL"
while [ "$(adb -s "$DEVICE_SERIAL" shell getprop sys.boot_completed 2>/dev/null)" != "1" ]; do
sleep 2
done
echo "Emulator '$AVD_NAME' booted on $DEVICE_SERIAL"
fi
If a physical device is connected and preferred, use its serial instead. List devices with adb devices.
Step 2: Build
adb -s "$DEVICE_SERIAL" shell am force-stop "$APP_ID" 2>/dev/null
$GRADLE_CMD :app:assemble${BUILD_VARIANT}
If build fails, fix compilation errors before proceeding.
For faster incremental builds, Gradle's build cache is enabled by default. If you suspect a stale cache, add --no-build-cache to the build command.
Step 3: Install and Launch
APK_PATH=$(find app/build/outputs/apk -name "*.apk" -path "*/${BUILD_VARIANT}/*" 2>/dev/null | head -1)
if [ -z "$APK_PATH" ]; then
APK_PATH=$(find app/build/outputs/apk -name "*.apk" | head -1)
fi
echo "Installing: $APK_PATH"
adb -s "$DEVICE_SERIAL" install -r "$APK_PATH"
LAUNCHER_ACTIVITY=$(aapt dump badging "$APK_PATH" 2>/dev/null | grep 'launchable-activity' | grep -oP "name='[^']+'" | head -1 | grep -oP "'[^']+'" | tr -d "'")
if [ -z "$LAUNCHER_ACTIVITY" ]; then
adb -s "$DEVICE_SERIAL" shell monkey -p "$APP_ID" -c android.intent.category.LAUNCHER 1
else
adb -s "$DEVICE_SERIAL" shell am start -n "$APP_ID/$LAUNCHER_ACTIVITY"
fi
Workspace Rule Override
If the project has a .cursor/rules/build-and-run-verification.mdc workspace rule with hardcoded values (build variant, application ID, Gradle task), prefer those values over auto-detection for those specific settings. The workspace rule is authoritative for that project. The emulator naming convention ({project}_{git_branch}, where project comes from the git remote) always applies regardless of workspace rule.
Troubleshooting
- No emulator found: Run
$ANDROID_HOME/emulator/emulator -list-avds and check naming. Ensure ANDROID_HOME or ANDROID_SDK_ROOT is set.
- No system images: Install via
sdkmanager 'system-images;android-35;google_apis;x86_64' or use Android Studio SDK Manager.
- Build fails with SDK errors: Ensure
local.properties has the correct sdk.dir path or ANDROID_HOME is set.
- APK not found: Check
app/build/outputs/apk/ for the correct variant directory structure.
- App won't launch: Verify
APP_ID matches the flavor's application ID. Check adb logcat for crash logs.
- Gradle daemon issues: Run
$GRADLE_CMD --stop to kill stale daemons, then rebuild.
- Emulator won't boot: Try
emulator -avd "$AVD_NAME" -wipe-data to reset the emulator, or delete and recreate the AVD.
- Multiple emulators: Each worktree runs its own emulator in parallel. NEVER shut down emulators you didn't start — they belong to other worktrees. Always use
-s $DEVICE_SERIAL on all adb commands to target the correct emulator. To identify which AVD is on which serial: adb -s <serial> emu avd name.