Build debug APKs on the phone itself
docs: the Termux home now has a working aarch64 Android SDK, so record how to build here, how the debug build differs from the release one (separate package, own data directory, so it cannot touch the installed launcher's config), and warn about the destructive uninstall path. scripts/ondevice.sh wraps the loop: build, install over the debug package, launch, follow logcat, read the newest crash report, and build the signed release APK.
This commit is contained in:
59
CLAUDE.md
59
CLAUDE.md
@@ -47,6 +47,8 @@ keeps it for returns within five seconds, which leaves a stale query on screen.
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
On the desktop:
|
||||||
|
|
||||||
JAVA_HOME=~/jdk21 ANDROID_HOME=~/android-sdk \
|
JAVA_HOME=~/jdk21 ANDROID_HOME=~/android-sdk \
|
||||||
./gradlew :app:app:assembleDefaultRelease
|
./gradlew :app:app:assembleDefaultRelease
|
||||||
|
|
||||||
@@ -57,8 +59,56 @@ The `release` build type signs with the `local` signing config, which reads `key
|
|||||||
in the repository root (git-ignored) or the `KEYSTORE_FILE`, `KEYSTORE_PASSWORD`, `KEY_ALIAS` and
|
in the repository root (git-ignored) or the `KEYSTORE_FILE`, `KEYSTORE_PASSWORD`, `KEY_ALIAS` and
|
||||||
`KEY_PASSWORD` environment variables. The desktop key is
|
`KEY_PASSWORD` environment variables. The desktop key is
|
||||||
`~/android-keystores/kvaesitso-release.jks`; the phone has its own copy under
|
`~/android-keystores/kvaesitso-release.jks`; the phone has its own copy under
|
||||||
`~/android-keystores/` in the Termux home. Termux itself has no Android SDK, so build there only
|
`~/android-keystores/` in the Termux home.
|
||||||
after installing one (or build on the desktop and copy the APK over).
|
|
||||||
|
### Building on the phone itself (Termux)
|
||||||
|
|
||||||
|
The phone builds this project without a desktop. `scripts/ondevice.sh` wraps the loop:
|
||||||
|
|
||||||
|
scripts/ondevice.sh build # assemble the debug APK
|
||||||
|
scripts/ondevice.sh install # build, then pm install it (root)
|
||||||
|
scripts/ondevice.sh start # launch the debug build
|
||||||
|
scripts/ondevice.sh logs # follow logcat of the debug build (Ctrl-C to stop)
|
||||||
|
scripts/ondevice.sh crash # newest crash report for the debug build
|
||||||
|
scripts/ondevice.sh release # assemble the signed release APK
|
||||||
|
|
||||||
|
Or call Gradle directly: `./gradlew :app:app:assembleDefaultDebug`. A full build takes about 13
|
||||||
|
minutes (984 tasks), later ones 20-30 seconds; the release build (R8 plus `lintVital`) takes about
|
||||||
|
11 minutes. Run `termux-wake-lock` first so the screen lock does not throttle it.
|
||||||
|
|
||||||
|
The **debug** build is `de.mm20.launcher2.debug` (`isDebuggable`, its own empty data directory),
|
||||||
|
so it installs next to the real launcher and can be logged with
|
||||||
|
`su -c 'logcat --pid=$(pidof de.mm20.launcher2.debug)'`. Installing, reinstalling or uninstalling
|
||||||
|
that package cannot affect the real launcher: different package, different data directory.
|
||||||
|
|
||||||
|
> **Never uninstall `de.mm20.launcher2.release`.** It is the launcher the phone actually runs, and
|
||||||
|
> the config (widgets, pages, tags, search settings) lives in its data directory
|
||||||
|
> `/data/user/0/de.mm20.launcher2.release`, mostly under `files/datastore`. `pm install -r` of a
|
||||||
|
> build signed with the same key keeps that data, but it does replace the running launcher, so do it
|
||||||
|
> deliberately, after backing the data directory up. Reinstalling the *debug* build is always the
|
||||||
|
> safe way to try a change.
|
||||||
|
|
||||||
|
To try a fix in the real launcher with the real data, build the release APK and `pm install -r` it:
|
||||||
|
same package (`de.mm20.launcher2.release`) and same key, so it replaces the installed build in
|
||||||
|
place. The release APK is minified by R8, so this is the slow build. `scripts/ondevice.sh release`
|
||||||
|
only *builds* it; it never installs anything.
|
||||||
|
|
||||||
|
#### The SDK on the phone
|
||||||
|
|
||||||
|
`~/android-sdk` (see `~/CLAUDE.md`) provides everything Gradle needs:
|
||||||
|
|
||||||
|
* `platforms/android-37.0` — the compileSdk 37 platform, installed with `sdkmanager`.
|
||||||
|
* `build-tools/36.0.0` — the version AGP 9.3.1 defaults to. Google only ships x86_64 build-tools,
|
||||||
|
so the three native tools are symlinks into `build-tools/34.0.4`, which is AndroidIDE's aarch64
|
||||||
|
build of the AOSP tools: `aapt2`, `zipalign` and `aidl`. Everything else in build-tools
|
||||||
|
(`d8`, `apksigner`, jars) is Java and runs as is.
|
||||||
|
* `~/.gradle/gradle.properties` points `android.aapt2FromMavenOverride` at Termux's own aarch64
|
||||||
|
`aapt2` (2.20, newer than 34.0.4's) and lowers the Gradle/Kotlin daemon heaps to fit the phone.
|
||||||
|
* `local.properties` in the repo root sets `sdk.dir`; it is git-ignored. The SDK itself is
|
||||||
|
machine-wide, not project-specific: another project just needs `termux-sdk init <repo>`, and
|
||||||
|
`termux-sdk install "platforms;android-35" "build-tools;35.0.0"` adds other SDK versions (it
|
||||||
|
patches their x86_64 native tools to aarch64, which plain `sdkmanager` cannot do). See
|
||||||
|
`~/CLAUDE.md` for what does and does not build on this phone.
|
||||||
|
|
||||||
## Releasing (the phone updates through Obtanium)
|
## Releasing (the phone updates through Obtanium)
|
||||||
|
|
||||||
@@ -77,6 +127,11 @@ after installing one (or build on the desktop and copy the APK over).
|
|||||||
|
|
||||||
## Installing on the phone by hand
|
## Installing on the phone by hand
|
||||||
|
|
||||||
|
> **This is the destructive path: it costs the launcher config unless the backup is restored
|
||||||
|
> correctly. Do not do it without an explicit reason and a backup.** The current config lives in
|
||||||
|
> `/data/user/0/de.mm20.launcher2.release` (uid of the release build, currently `u0_a311`); copy it
|
||||||
|
> out with root before touching anything.
|
||||||
|
|
||||||
The phone already runs this build. The official build is signed with the upstream key, so it cannot
|
The phone already runs this build. The official build is signed with the upstream key, so it cannot
|
||||||
be updated in place and Obtanium cannot install over it either. The procedure is: back up
|
be updated in place and Obtanium cannot install over it either. The procedure is: back up
|
||||||
`/data/user/0/de.mm20.launcher2.release` with root, `pm uninstall` (a `-k` uninstall keeps the
|
`/data/user/0/de.mm20.launcher2.release` with root, `pm uninstall` (a `-k` uninstall keeps the
|
||||||
|
|||||||
87
scripts/ondevice.sh
Executable file
87
scripts/ondevice.sh
Executable file
@@ -0,0 +1,87 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Build and test this fork directly on the phone, from Termux.
|
||||||
|
# See CLAUDE.md ("Building on the phone itself") for the SDK setup this expects.
|
||||||
|
#
|
||||||
|
# The debug build is the package "de.mm20.launcher2.debug": a separate install from the
|
||||||
|
# real launcher ("de.mm20.launcher2.release") with its own, initially empty, data directory.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
REPO=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
|
PKG=de.mm20.launcher2.debug
|
||||||
|
ACTIVITY=de.mm20.launcher2.ui.launcher.LauncherActivity
|
||||||
|
DEBUG_APK=$REPO/app/app/build/outputs/apk/default/debug/app-default-debug.apk
|
||||||
|
RELEASE_APK=$REPO/app/app/build/outputs/apk/default/release/app-default-release.apk
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage: scripts/ondevice.sh <command>
|
||||||
|
|
||||||
|
build assemble the debug APK
|
||||||
|
install build, then install the debug APK (needs root)
|
||||||
|
start launch the installed debug build
|
||||||
|
logs follow logcat of the running debug build
|
||||||
|
crash print the newest crash report for the debug build
|
||||||
|
release assemble the signed release APK: same package and key as the launcher
|
||||||
|
installed from Obtanium, so it installs over it and keeps all data
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# Termux ships JDK 21 in $PREFIX; on other machines JAVA_HOME has to be set already.
|
||||||
|
if [ -z "${JAVA_HOME:-}" ] && [ -n "${PREFIX:-}" ] && [ -d "$PREFIX/lib/jvm/java-21-openjdk" ]; then
|
||||||
|
export JAVA_HOME=$PREFIX/lib/jvm/java-21-openjdk
|
||||||
|
fi
|
||||||
|
|
||||||
|
gradle() {
|
||||||
|
( cd "$REPO" && ./gradlew --console=plain "$@" )
|
||||||
|
}
|
||||||
|
|
||||||
|
# Long builds outlive the screen lock; keep the CPU awake until they are done.
|
||||||
|
wake() {
|
||||||
|
command -v termux-wake-lock >/dev/null && termux-wake-lock || true
|
||||||
|
}
|
||||||
|
|
||||||
|
build_debug() {
|
||||||
|
wake
|
||||||
|
gradle :app:app:assembleDefaultDebug
|
||||||
|
ls -l "$DEBUG_APK"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Every su -c argument below mixes two quoting levels: the single quoted parts are handed
|
||||||
|
# to the root shell as-is (so $( ) and $pid are expanded there), the "..." parts are
|
||||||
|
# expanded here.
|
||||||
|
case "${1:-}" in
|
||||||
|
build)
|
||||||
|
build_debug
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
build_debug
|
||||||
|
# Termux cannot use pm itself; root can, and can read the APK from ~/sources.
|
||||||
|
su -c "pm install -r -d '$DEBUG_APK'"
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
su -c "am start -n $PKG/$ACTIVITY"
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
su -c 'pid=$(pidof '"$PKG"')
|
||||||
|
if [ -z "$pid" ]; then
|
||||||
|
echo "not running - start it first (scripts/ondevice.sh start)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
exec logcat --pid=$pid'
|
||||||
|
;;
|
||||||
|
crash)
|
||||||
|
su -c 'for f in $(ls -t /data/system/dropbox/data_app_crash@*.txt 2>/dev/null | head -30); do
|
||||||
|
if grep -q '"$PKG"' "$f"; then echo "== $f"; cat "$f"; break; fi
|
||||||
|
done'
|
||||||
|
;;
|
||||||
|
release)
|
||||||
|
wake
|
||||||
|
gradle :app:app:assembleDefaultRelease
|
||||||
|
ls -l "$RELEASE_APK"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user