feat(android): follow the activity lifecycle and build an APK (Issue #42)
- The drawing surface is created once the native window exists (Resumed) and released when the app goes to the background (Suspended); before that everything is saved, as Android may stop the app at any time - Android's back button acts like Escape instead of closing the app - Panics are written to logcat; no update check on Android yet - scripts/package-android.sh builds target/ink-notes_<version>_arm64.apk without Gradle (NDK, aapt2, zipalign, apksigner); signing key from ANDROID_KEYSTORE/ANDROID_KEYSTORE_PASS/ANDROID_KEY_ALIAS, otherwise the debug key Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ce3a1f697b
commit
7faf78ddeb
9 files changed
+1662
-353
No files matched your search
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# Builds the Android app target/ink-notes_<version>_arm64.apk (Issues #42, #45)
|
||||
# without Gradle: the Rust library is built as cdylib with the NDK, aapt2 links the
|
||||
# manifest and the icon, zipalign and apksigner finish the APK.
|
||||
#
|
||||
# Needs the Android SDK (build-tools, platform android-34) and the NDK:
|
||||
# ANDROID_HOME SDK folder (default: ~/android-sdk)
|
||||
# ANDROID_NDK_HOME NDK folder (default: newest in $ANDROID_HOME/ndk)
|
||||
#
|
||||
# Signing key (all three, otherwise the debug key ~/.android/debug.keystore is used and
|
||||
# created if missing; such an APK is only meant for testing):
|
||||
# ANDROID_KEYSTORE, ANDROID_KEYSTORE_PASS, ANDROID_KEY_ALIAS
|
||||
# ==============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
cd "${ROOT_DIR}"
|
||||
|
||||
TARGET="aarch64-linux-android"
|
||||
ABI="arm64-v8a"
|
||||
MIN_SDK=26
|
||||
TARGET_SDK=34
|
||||
VERSION=$(grep -m1 '^version = ' Cargo.toml | cut -d '"' -f2)
|
||||
IFS=. read -r MAJOR MINOR PATCH <<<"${VERSION%%-*}"
|
||||
VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH))
|
||||
APK_NAME="ink-notes_${VERSION}_arm64.apk"
|
||||
|
||||
ANDROID_HOME="${ANDROID_HOME:-${HOME}/android-sdk}"
|
||||
if [ -z "${ANDROID_NDK_HOME:-}" ]; then
|
||||
ANDROID_NDK_HOME=$(ls -d "${ANDROID_HOME}"/ndk/*/ 2>/dev/null | sort -V | tail -n1)
|
||||
fi
|
||||
BUILD_TOOLS=$(ls -d "${ANDROID_HOME}"/build-tools/*/ 2>/dev/null | sort -V | tail -n1)
|
||||
ANDROID_JAR="${ANDROID_HOME}/platforms/android-${TARGET_SDK}/android.jar"
|
||||
NDK_BIN="${ANDROID_NDK_HOME%/}/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
||||
|
||||
for needed in "${NDK_BIN}/${TARGET}${MIN_SDK}-clang" "${BUILD_TOOLS}aapt2" "${ANDROID_JAR}"; do
|
||||
if [ ! -e "${needed}" ]; then
|
||||
echo "FEHLER: ${needed} nicht gefunden (Android-SDK/NDK, siehe Kopf des Skripts)."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
if command -v rustup >/dev/null 2>&1; then
|
||||
rustup target add "${TARGET}"
|
||||
fi
|
||||
|
||||
echo "=== [1/4] Baue libink_notes.so (${TARGET}) ==="
|
||||
export CC_aarch64_linux_android="${NDK_BIN}/${TARGET}${MIN_SDK}-clang"
|
||||
export AR_aarch64_linux_android="${NDK_BIN}/llvm-ar"
|
||||
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${NDK_BIN}/${TARGET}${MIN_SDK}-clang"
|
||||
cargo rustc --release --lib --crate-type cdylib --target "${TARGET}"
|
||||
LIB="${CARGO_TARGET_DIR:-target}/${TARGET}/release/libink_notes.so"
|
||||
|
||||
echo "=== [2/4] Ressourcen und Manifest ==="
|
||||
WORK="target/android-apk"
|
||||
rm -rf "${WORK}"
|
||||
mkdir -p "${WORK}/res/mipmap-xxxhdpi" "${WORK}/compiled" "${WORK}/apk/lib/${ABI}"
|
||||
cp assets/ink-notes.png "${WORK}/res/mipmap-xxxhdpi/ic_launcher.png"
|
||||
sed -e "s/@VERSION_CODE@/${VERSION_CODE}/" -e "s/@VERSION@/${VERSION}/" \
|
||||
packaging/android/AndroidManifest.xml >"${WORK}/AndroidManifest.xml"
|
||||
"${BUILD_TOOLS}aapt2" compile --dir "${WORK}/res" -o "${WORK}/compiled/res.zip"
|
||||
"${BUILD_TOOLS}aapt2" link -o "${WORK}/unaligned.apk" -I "${ANDROID_JAR}" \
|
||||
--manifest "${WORK}/AndroidManifest.xml" \
|
||||
--min-sdk-version "${MIN_SDK}" --target-sdk-version "${TARGET_SDK}" \
|
||||
"${WORK}/compiled/res.zip"
|
||||
|
||||
echo "=== [3/4] Native Bibliothek hinzufügen ==="
|
||||
cp "${LIB}" "${WORK}/apk/lib/${ABI}/"
|
||||
(cd "${WORK}/apk" && zip -q -r ../unaligned.apk lib)
|
||||
"${BUILD_TOOLS}zipalign" -f -p 4 "${WORK}/unaligned.apk" "${WORK}/aligned.apk"
|
||||
|
||||
echo "=== [4/4] Signieren ==="
|
||||
if [ -n "${ANDROID_KEYSTORE:-}" ] && [ -n "${ANDROID_KEYSTORE_PASS:-}" ] && [ -n "${ANDROID_KEY_ALIAS:-}" ]; then
|
||||
KEYSTORE="${ANDROID_KEYSTORE}"
|
||||
KEY_ALIAS="${ANDROID_KEY_ALIAS}"
|
||||
export KS_PASS="${ANDROID_KEYSTORE_PASS}"
|
||||
else
|
||||
KEYSTORE="${HOME}/.android/debug.keystore"
|
||||
KEY_ALIAS="androiddebugkey"
|
||||
export KS_PASS="android"
|
||||
if [ ! -f "${KEYSTORE}" ]; then
|
||||
mkdir -p "$(dirname "${KEYSTORE}")"
|
||||
keytool -genkeypair -keystore "${KEYSTORE}" -storepass android -keypass android \
|
||||
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 \
|
||||
-dname "CN=Android Debug,O=Android,C=US"
|
||||
fi
|
||||
echo "Hinweis: Signiert mit dem Debug-Schlüssel ${KEYSTORE} (nur zum Testen)."
|
||||
fi
|
||||
"${BUILD_TOOLS}apksigner" sign --ks "${KEYSTORE}" --ks-key-alias "${KEY_ALIAS}" \
|
||||
--ks-pass env:KS_PASS --key-pass env:KS_PASS \
|
||||
--out "target/${APK_NAME}" "${WORK}/aligned.apk"
|
||||
"${BUILD_TOOLS}apksigner" verify "target/${APK_NAME}"
|
||||
ls -lh "target/${APK_NAME}"
|
||||
Reference in new issue
Block a user