`adb shell run-as` refuses on a release build — "package not debuggable" — and the app's private storage is then unreachable from the host. That storage is where the face shards, the thumbnail store and the catalog live, so when a device disagrees with the desktop about what it has synced there is no way to find out which of them is right. An evening was spent guessing at exactly that. `DARKROOM_DEBUGGABLE=1 ./docker/android/package.sh --install` now sets `android:debuggable` through aapt2's `--debug-mode`, and nothing else changes. Set through aapt2 rather than written into `AndroidManifest.xml` on purpose: the flag then exists only for the build that asked for it, and a release build cannot inherit it because somebody forgot to take it out again. A debuggable APK lets any process on the device read this app's files, so it belongs on a test tablet and nowhere else. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
90 lines
4.2 KiB
Bash
Executable File
90 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Build DarkRoom into an installable APK, without Gradle.
|
|
#
|
|
# ./docker/android/package.sh # build + package, debug-signed
|
|
# ./docker/android/package.sh --install # ...and adb install to a device
|
|
#
|
|
# Gradle would add a second build system, a second dependency tree, and a
|
|
# second place for the toolchain versions to drift out of step with the
|
|
# Dockerfile. The four tools it would have driven — aapt2, d8, zipalign,
|
|
# apksigner — are in build-tools already and are enough on their own, because
|
|
# the app has no Java of its own: android-activity's glue calls android_main
|
|
# directly, and the only classes in the APK are the ones Slint's build script
|
|
# compiles for its own helper.
|
|
set -euo pipefail
|
|
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO="$(cd "${HERE}/../.." && pwd)"
|
|
|
|
ABI="arm64-v8a"
|
|
RUST_TARGET="aarch64-linux-android"
|
|
PKG_DIR="${REPO}/apps/darkroom-android/android"
|
|
# The container writes here (see build.sh); the APK is assembled in the same
|
|
# place so both halves of the build agree on one output directory.
|
|
CACHE="${XDG_CACHE_HOME:-${HOME}/.cache}/darkroom-android"
|
|
# Under the cache's target/ rather than beside it: the container writes to
|
|
# /work/target-android/apk, and that is the directory bind-mounted here.
|
|
OUT="${CACHE}/target/apk"
|
|
APK="${OUT}/darkroom.apk"
|
|
|
|
# The version, taken from the workspace rather than restated here.
|
|
#
|
|
# `AndroidManifest.xml` deliberately declares none: a manifest that states a
|
|
# version is a second place for one to be wrong, and it was — the APK reported
|
|
# `versionName=null` and `versionCode=0` on the device while the binary inside
|
|
# it knew perfectly well what it was.
|
|
#
|
|
# `versionCode` must be a single increasing integer, which a semantic version
|
|
# is not, so it is packed: MAJOR*10000 + MINOR*100 + PATCH. That keeps the
|
|
# ordering Android needs (it refuses to install an APK whose code is lower than
|
|
# the installed one) and stays readable — 0.4.0 is 400. It allows 99 minors and
|
|
# 99 patches per major, which is a limit worth knowing about and a long way off.
|
|
VERSION_NAME="$(sed -n 's/^version = "\(.*\)"$/\1/p' "${REPO}/Cargo.toml" | head -1)"
|
|
if [[ -z "${VERSION_NAME}" ]]; then
|
|
echo "error: no version in Cargo.toml" >&2
|
|
exit 1
|
|
fi
|
|
VERSION_CODE="$(awk -F. '{ print $1 * 10000 + $2 * 100 + $3 }' <<< "${VERSION_NAME}")"
|
|
echo "==> version ${VERSION_NAME} (code ${VERSION_CODE})"
|
|
|
|
INSTALL=0
|
|
[[ "${1:-}" == "--install" ]] && INSTALL=1
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Cross-compile the shared library.
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> building libdarkroom.so (${ABI})"
|
|
"${HERE}/build.sh" cargo ndk -t "${ABI}" -o /work/target-android/jniLibs \
|
|
build --release -p darkroom-android
|
|
|
|
SO="${CACHE}/target/jniLibs/${ABI}/libdarkroom.so"
|
|
[[ -f "${SO}" ]] || { echo "error: ${SO} not built" >&2; exit 1; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Assemble the APK inside the container, where the SDK lives.
|
|
#
|
|
# The assembly itself is assemble-apk.sh, which runs in the image and is shared
|
|
# with CI — see its header. Only the mount layout is decided here: the repo is
|
|
# at /work and the cache's target directory at /work/target-android, so every
|
|
# default in that script already points at the right place.
|
|
# ---------------------------------------------------------------------------
|
|
echo "==> packaging APK"
|
|
"${HERE}/build.sh" env \
|
|
ABI="${ABI}" RUST_TARGET="${RUST_TARGET}" \
|
|
DARKROOM_DEBUGGABLE="${DARKROOM_DEBUGGABLE:-}" \
|
|
/work/docker/android/assemble-apk.sh
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Install from the host.
|
|
#
|
|
# The container has adb but no device: build.sh mounts no USB and shares no
|
|
# network, so the host's already-authorised adb server is the shorter path.
|
|
# ---------------------------------------------------------------------------
|
|
if [[ "${INSTALL}" == "1" ]]; then
|
|
command -v adb >/dev/null || { echo "error: adb not on PATH" >&2; exit 1; }
|
|
echo "==> installing"
|
|
adb install -r "${APK}"
|
|
echo "==> launching"
|
|
adb shell am start -n paris.tourolle.darkroom/android.app.NativeActivity
|
|
fi
|