216 lines
7.1 KiB
Bash
Executable File
216 lines
7.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Standalone build for ArtemisAccessibilityHelper.apk (no Gradle daemon).
|
|
#
|
|
# Outputs, next to this script:
|
|
# ArtemisAccessibilityHelper.apk the signed, zipaligned artifact
|
|
# helper_manifest.json {version_code, version_name, sha256, built_at}
|
|
#
|
|
# The Python runtime (artemis/runtime/helper_manager.py) reads helper_manifest.json
|
|
# to decide whether a device needs an install or an upgrade, so the manifest must
|
|
# always be regenerated together with the APK. The version is sourced from
|
|
# app/build.gradle.kts (defaultConfig.versionCode / versionName) so the Gradle
|
|
# build and this script cannot drift.
|
|
set -e
|
|
|
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$DIR"
|
|
|
|
_to_posix() {
|
|
if command -v cygpath >/dev/null 2>&1; then
|
|
cygpath -u "$1"
|
|
else
|
|
printf '%s' "$1"
|
|
fi
|
|
}
|
|
|
|
# 1. Locate Android SDK
|
|
SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
|
|
if [ -n "$SDK_ROOT" ]; then
|
|
SDK_ROOT="$(_to_posix "$SDK_ROOT")"
|
|
fi
|
|
if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT" ]; then
|
|
for candidate in \
|
|
"$HOME/Library/Android/sdk" \
|
|
"$HOME/Android/Sdk" \
|
|
/opt/android-sdk \
|
|
/usr/local/share/android-sdk \
|
|
"$( [ -n "$LOCALAPPDATA" ] && _to_posix "$LOCALAPPDATA/Android/Sdk" )"; do
|
|
if [ -n "$candidate" ] && [ -d "$candidate" ]; then
|
|
SDK_ROOT="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT" ]; then
|
|
echo "Error: Android SDK not found. Set ANDROID_HOME." >&2
|
|
exit 1
|
|
fi
|
|
|
|
BUILD_TOOLS="$(find "$SDK_ROOT/build-tools" -maxdepth 1 -mindepth 1 | sort -V | tail -n 1)"
|
|
PLATFORM="$(find "$SDK_ROOT/platforms" -maxdepth 1 -mindepth 1 | sort -V | tail -n 1)"
|
|
|
|
if [ -z "$BUILD_TOOLS" ] || [ ! -d "$BUILD_TOOLS" ]; then
|
|
echo "Error: No build-tools found under $SDK_ROOT/build-tools" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "$PLATFORM" ] || [ ! -f "$PLATFORM/android.jar" ]; then
|
|
echo "Error: android.jar not found under $SDK_ROOT/platforms" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# 2. Locate Java compiler and runtime
|
|
if [ -z "$JAVA_HOME" ]; then
|
|
for candidate in \
|
|
"/Applications/Android Studio.app/Contents/jbr/Contents/Home" \
|
|
"$(_to_posix "${PROGRAMFILES:-/c/Program Files}/Android/Android Studio/jbr")" \
|
|
"/opt/android-studio/jbr"; do
|
|
if [ -n "$candidate" ] && [ -x "$candidate/bin/javac" ] || [ -x "$candidate/bin/javac.exe" ]; then
|
|
export JAVA_HOME="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if [ -n "$JAVA_HOME" ]; then
|
|
export PATH="$JAVA_HOME/bin:$PATH"
|
|
fi
|
|
|
|
if ! command -v javac >/dev/null 2>&1; then
|
|
echo "Error: javac not found in PATH or JAVA_HOME" >&2
|
|
exit 1
|
|
fi
|
|
|
|
PYTHON_BIN="${PYTHON:-}"
|
|
if [ -z "$PYTHON_BIN" ]; then
|
|
# The Windows Store "python" stub is on PATH but exits 49; prefer the project venv.
|
|
for candidate in "$DIR/../../.venv/Scripts/python.exe" "$DIR/../../.venv/bin/python" python3 python; do
|
|
if "$candidate" -c "import zipfile" >/dev/null 2>&1; then
|
|
PYTHON_BIN="$candidate"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if [ -z "$PYTHON_BIN" ]; then
|
|
echo "Error: python3 not found (needed to package the DEX and write helper_manifest.json)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Build-tools ship as .exe / .bat wrappers on Windows; resolve the right name once.
|
|
_tool() {
|
|
local base="$BUILD_TOOLS/$1"
|
|
for suffix in "" ".exe" ".bat"; do
|
|
if [ -f "$base$suffix" ]; then
|
|
printf '%s' "$base$suffix"
|
|
return 0
|
|
fi
|
|
done
|
|
echo "Error: $1 not found under $BUILD_TOOLS" >&2
|
|
exit 1
|
|
}
|
|
AAPT2="$(_tool aapt2)"
|
|
D8="$(_tool d8)"
|
|
ZIPALIGN="$(_tool zipalign)"
|
|
APKSIGNER="$(_tool apksigner)"
|
|
|
|
# 3. Version: single source is app/build.gradle.kts
|
|
VERSION_CODE="$(sed -n 's/^[[:space:]]*versionCode[[:space:]]*=[[:space:]]*\([0-9][0-9]*\).*/\1/p' app/build.gradle.kts | head -n 1)"
|
|
VERSION_NAME="$(sed -n 's/^[[:space:]]*versionName[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' app/build.gradle.kts | head -n 1)"
|
|
if [ -z "$VERSION_CODE" ] || [ -z "$VERSION_NAME" ]; then
|
|
echo "Error: could not read versionCode / versionName from app/build.gradle.kts" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "=================================================="
|
|
echo " Building ArtemisAccessibilityHelper.apk"
|
|
echo " Version: $VERSION_NAME (code $VERSION_CODE)"
|
|
echo " Build Tools: $(basename "$BUILD_TOOLS")"
|
|
echo " Platform: $(basename "$PLATFORM")"
|
|
echo " Java: $(javac -version 2>&1)"
|
|
echo "=================================================="
|
|
|
|
rm -rf build
|
|
mkdir -p build/gen build/obj build/apk build/res_compiled
|
|
|
|
echo "-> Compiling resources..."
|
|
"$AAPT2" compile --dir app/src/main/res -o build/res_compiled/res.zip
|
|
|
|
echo "-> Linking APK..."
|
|
"$AAPT2" link \
|
|
-I "$PLATFORM/android.jar" \
|
|
--manifest app/src/main/AndroidManifest.xml \
|
|
--min-sdk-version 24 \
|
|
--target-sdk-version 35 \
|
|
--version-code "$VERSION_CODE" \
|
|
--version-name "$VERSION_NAME" \
|
|
--java build/gen \
|
|
-o build/apk/unaligned.apk \
|
|
build/res_compiled/res.zip
|
|
|
|
echo "-> Compiling Java sources..."
|
|
JAVA_FILES="$(find build/gen app/src/main/java -name "*.java")"
|
|
javac -encoding UTF-8 \
|
|
-source 8 -target 8 \
|
|
-Xlint:-options \
|
|
-cp "$PLATFORM/android.jar" \
|
|
-d build/obj \
|
|
$JAVA_FILES
|
|
|
|
echo "-> Dexing with D8..."
|
|
CLASS_FILES="$(find build/obj -name "*.class")"
|
|
"$D8" \
|
|
--lib "$PLATFORM/android.jar" \
|
|
--output build/apk/ \
|
|
--min-api 24 \
|
|
$CLASS_FILES
|
|
|
|
echo "-> Packaging DEX..."
|
|
"$PYTHON_BIN" - <<'PY'
|
|
import zipfile
|
|
with zipfile.ZipFile("build/apk/unaligned.apk", "a", compression=zipfile.ZIP_DEFLATED) as apk:
|
|
apk.write("build/apk/classes.dex", "classes.dex")
|
|
PY
|
|
|
|
echo "-> Zipaligning..."
|
|
"$ZIPALIGN" -f -p 4 build/apk/unaligned.apk build/apk/aligned.apk
|
|
|
|
echo "-> Signing APK..."
|
|
if [ ! -f debug.keystore ]; then
|
|
echo "Creating persistent debug.keystore..."
|
|
keytool -genkey -v -keystore debug.keystore \
|
|
-storepass android -alias androiddebugkey -keypass android \
|
|
-keyalg RSA -keysize 2048 -validity 10000 \
|
|
-dname "CN=Artemis Debug,O=Artemis,C=US"
|
|
fi
|
|
|
|
"$APKSIGNER" sign \
|
|
--ks debug.keystore \
|
|
--ks-pass pass:android \
|
|
--key-pass pass:android \
|
|
--ks-key-alias androiddebugkey \
|
|
--out ArtemisAccessibilityHelper.apk \
|
|
build/apk/aligned.apk
|
|
|
|
echo "-> Writing helper_manifest.json..."
|
|
VERSION_CODE="$VERSION_CODE" VERSION_NAME="$VERSION_NAME" "$PYTHON_BIN" - <<'PY'
|
|
import datetime, hashlib, json, os
|
|
digest = hashlib.sha256(open("ArtemisAccessibilityHelper.apk", "rb").read()).hexdigest()
|
|
manifest = {
|
|
"package": "com.artemis.helper",
|
|
"version_code": int(os.environ["VERSION_CODE"]),
|
|
"version_name": os.environ["VERSION_NAME"],
|
|
"sha256": digest,
|
|
"built_at": datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat(),
|
|
}
|
|
with open("helper_manifest.json", "w", encoding="utf-8", newline="\n") as fh:
|
|
json.dump(manifest, fh, indent=2)
|
|
fh.write("\n")
|
|
print(json.dumps(manifest, indent=2))
|
|
PY
|
|
|
|
echo "=================================================="
|
|
echo " Build SUCCESSFUL!"
|
|
echo " Artifact: $DIR/ArtemisAccessibilityHelper.apk"
|
|
ls -lh ArtemisAccessibilityHelper.apk
|
|
echo "=================================================="
|