#!/usr/bin/env bash
set -euo pipefail

SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
PROJECT_DIR="${ANDROID_PROJECT_DIR:-$PWD}"
GRADLE_TASK="${ANDROID_GRADLE_TASK:-:app:assembleDebug}"
APK_PATH="${ANDROID_APK_PATH:-app/build/outputs/apk/debug/app-debug.apk}"
AVD_NAME="${ANDROID_AVD_NAME:-qa_api28}"
EMULATOR_PORT="${ANDROID_EMULATOR_PORT:-5556}"
BOOT_TIMEOUT_SECONDS="${ANDROID_EMULATOR_BOOT_TIMEOUT:-180}"
SERIAL="emulator-$EMULATOR_PORT"
ADB="$SDK_ROOT/platform-tools/adb"
EMULATOR="$SDK_ROOT/emulator/emulator"
STARTED_EMULATOR=0
EMULATOR_PID=""

if [[ -e /dev/kvm && ! -r /dev/kvm && "${ANDROID_KVM_REEXEC:-0}" != "1" ]]; then
    if getent group kvm | cut -d: -f4 | tr ',' '\n' | grep -Fxq "$USER"; then
        quoted_command="ANDROID_KVM_REEXEC=1"
        for variable in ANDROID_HOME ANDROID_SDK_ROOT ANDROID_PROJECT_DIR ANDROID_GRADLE_TASK ANDROID_APK_PATH ANDROID_AVD_NAME ANDROID_EMULATOR_PORT ANDROID_EMULATOR_BOOT_TIMEOUT ANDROID_APP_PACKAGE ANDROID_LAUNCH_ACTIVITY ANDROID_GRANT_PERMISSIONS; do
            [[ -z "${!variable+x}" ]] || printf -v quoted_command '%s %q=%q' "$quoted_command" "$variable" "${!variable}"
        done
        printf -v quoted_command '%s %q' "$quoted_command" "$0"
        exec sg kvm -c "$quoted_command"
    fi
fi

fail() {
    printf 'ERROR: %s\n' "$*" >&2
    exit 1
}

cleanup() {
    if [[ "$STARTED_EMULATOR" == "1" ]]; then
        "$ADB" -s "$SERIAL" emu kill >/dev/null 2>&1 || true
        [[ -z "$EMULATOR_PID" ]] || wait "$EMULATOR_PID" 2>/dev/null || true
    fi
}
trap cleanup EXIT

[[ -x "$ADB" ]] || fail "adb not found at $ADB"
[[ -x "$EMULATOR" ]] || fail "emulator not found at $EMULATOR"
[[ ! -e /dev/kvm || -r /dev/kvm ]] || fail "current user cannot access /dev/kvm; activate membership in the kvm group"
[[ -x "$PROJECT_DIR/gradlew" ]] || fail "Gradle wrapper not found in $PROJECT_DIR"
"$EMULATOR" -list-avds | grep -Fxq "$AVD_NAME" || fail "AVD '$AVD_NAME' is not installed"

export ANDROID_HOME="$SDK_ROOT"
export ANDROID_SDK_ROOT="$SDK_ROOT"

(cd "$PROJECT_DIR" && ./gradlew "$GRADLE_TASK" --no-daemon)
[[ "$APK_PATH" = /* ]] || APK_PATH="$PROJECT_DIR/$APK_PATH"
[[ -f "$APK_PATH" ]] || fail "APK was not created at $APK_PATH"

AAPT="${ANDROID_AAPT:-}"
if [[ -z "$AAPT" ]]; then
    AAPT="$(find "$SDK_ROOT/build-tools" -mindepth 2 -maxdepth 2 -type f -name aapt -print 2>/dev/null | sort -V | tail -1)"
fi
[[ -x "$AAPT" ]] || fail "aapt not found; set ANDROID_AAPT"

BADGING="$($AAPT dump badging "$APK_PATH")"
PACKAGE="${ANDROID_APP_PACKAGE:-$(sed -n "s/^package: name='\([^']*\)'.*/\1/p" <<<"$BADGING" | head -1)}"
ACTIVITY="${ANDROID_LAUNCH_ACTIVITY:-$(sed -n "s/^launchable-activity: name='\([^']*\)'.*/\1/p" <<<"$BADGING" | head -1)}"
[[ -n "$PACKAGE" ]] || fail "could not determine the application package"
[[ -n "$ACTIVITY" ]] || fail "could not determine the launch activity; set ANDROID_LAUNCH_ACTIVITY"

if ! "$ADB" devices | awk 'NR > 1 {print $1}' | grep -Fxq "$SERIAL"; then
    "$EMULATOR" -avd "$AVD_NAME" -port "$EMULATOR_PORT" -no-window -no-audio \
        -no-boot-anim -no-snapshot-load -gpu swiftshader_indirect \
        >"${TMPDIR:-/tmp}/android-emulator-$EMULATOR_PORT.log" 2>&1 &
    EMULATOR_PID=$!
    STARTED_EMULATOR=1
fi

deadline=$((SECONDS + BOOT_TIMEOUT_SECONDS))
until "$ADB" -s "$SERIAL" get-state >/dev/null 2>&1; do
    [[ "$STARTED_EMULATOR" != "1" || -z "$EMULATOR_PID" || -e "/proc/$EMULATOR_PID" ]] || fail "emulator exited before becoming available"
    (( SECONDS < deadline )) || fail "emulator did not become available within $BOOT_TIMEOUT_SECONDS seconds"
    sleep 2
done
while [[ "$("$ADB" -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]]; do
    (( SECONDS < deadline )) || fail "emulator did not boot within $BOOT_TIMEOUT_SECONDS seconds"
    sleep 2
done

"$ADB" -s "$SERIAL" install -r -t "$APK_PATH" >/dev/null
for permission in ${ANDROID_GRANT_PERMISSIONS:-}; do
    "$ADB" -s "$SERIAL" shell pm grant "$PACKAGE" "$permission"
done
"$ADB" -s "$SERIAL" logcat -c
"$ADB" -s "$SERIAL" shell am force-stop "$PACKAGE"
"$ADB" -s "$SERIAL" shell am start -W -n "$PACKAGE/$ACTIVITY" >/dev/null

resumed="$($ADB -s "$SERIAL" shell dumpsys activity activities | grep -m1 'mResumedActivity' || true)"
[[ "$resumed" == *"$PACKAGE"* ]] || fail "launcher activity is not resumed: ${resumed:-no activity reported}"
pid="$($ADB -s "$SERIAL" shell pidof "$PACKAGE" | tr -d '\r')"
[[ -n "$pid" ]] || fail "app process is not running"
if "$ADB" -s "$SERIAL" logcat -d -b crash | grep -Fq "$PACKAGE"; then
    "$ADB" -s "$SERIAL" logcat -d -b crash >&2
    fail "crash log contains an entry for $PACKAGE"
fi

printf 'PASS: %s installed and launched on %s (%s), pid=%s\n' "$APK_PATH" "$AVD_NAME" "$SERIAL" "$pid"
