MeshChatX Android App
This directory contains the Android app build configuration using Chaquopy to embed the Python MeshChatX server.
Architecture
The app uses a WebView to display the existing Vue.js frontend. The Python server runs in the background via Chaquopy and serves the web interface on https://127.0.0.1:8000.
Build debug APK
Prerequisites:
- JDK 17 or newer (required by the Android Gradle Plugin used in this project). On distributions with multiple JDKs, point the build at JDK 17+ (for example
JAVA_HOMEfor the Gradle invocation). - Android SDK with API 34 platform and Build-Tools 34 installed. Set
ANDROID_HOMEandANDROID_SDK_ROOTto the SDK root (the same directory for both is fine). - Host build tools for wheel patching:
patchelf,cmake,pkg-config,unzip, and build essentials (gcc,make, headers). On Arch-based systems, installpatchelf cmake pkgconf base-devel rustup unzip. android/vendor/must contain the Chaquopy vendor wheels (see Updating Android Python ABI Wheels). The build fails fast if this directory is missing or incomplete.- MeshChatX Python sources at the repository root (
meshchatx/). The build syncs them into the app before compiling.
SDK licenses:
- Use Command-line Tools
sdkmanager, not the legacytools/bin/sdkmanagerfrom old SDK layouts. The legacy tool loads JAXB classes that were removed from the JDK in Java 11, so running it on JDK 17+ fails withNoClassDefFoundError: javax/xml/bind/annotation/XmlSchema. - Install Command-line Tools if needed: download the package for your OS from Android Studio command-line tools, extract it so you have
cmdline-tools/latest/bin/sdkmanagerunderANDROID_HOME(the inner folder is often namedlatest; see Google’s layout for that zip). - Accept licenses (writes under the SDK; use sudo if the SDK is root-owned):
yes | path/to/cmdline-tools/latest/bin/sdkmanager --licenses
- Install missing packages if the build still complains (platform 34, build-tools 34, etc.):
path/to/cmdline-tools/latest/bin/sdkmanager "platforms;android-34" "build-tools;34.0.0"
Build from the android/ directory:
./gradlew assembleDebug
Debug APK outputs (ABI splits plus universal) are written under app/build/outputs/apk/debug/, for example:
app-arm64-v8a-debug.apkapp-x86_64-debug.apkapp-armeabi-v7a-debug.apkapp-universal-debug.apk
By default all ABIs are included. To build only specific ABIs, pass a property:
./gradlew assembleDebug -PmeshchatxAbis=armeabi-v7a
./gradlew assembleDebug -PmeshchatxAbis=arm64-v8a,x86_64
app-universal-*.apk is produced only when more than one ABI is selected.
Signing Release APKs (optional SourceStamp)
Build release APKs first:
./gradlew --no-daemon :app:assembleRelease
Create a release signing key (one time):
mkdir -p android/keystore
keytool -genkeypair -v \
-keystore android/keystore/meshchatx-release.jks \
-alias meshchatx-release \
-keyalg RSA \
-keysize 4096 \
-validity 9125
Optional: create a separate SourceStamp key (recommended if you use SourceStamp):
keytool -genkeypair -v \
-keystore android/keystore/meshchatx-stamp.jks \
-alias meshchatx-stamp \
-keyalg RSA \
-keysize 4096 \
-validity 9125
Sign all *-unsigned.apk release artifacts:
ANDROID_HOME="$HOME/Android/sdk" \
SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \
SIGNING_KEY_ALIAS=meshchatx-release \
SIGNING_KEYSTORE_PASSWORD='<release-keystore-password>' \
SIGNING_KEY_PASSWORD='<release-key-password>' \
bash scripts/sign-android-apks.sh
Sign with SourceStamp enabled:
ANDROID_HOME="$HOME/Android/sdk" \
SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \
SIGNING_KEY_ALIAS=meshchatx-release \
SIGNING_KEYSTORE_PASSWORD='<release-keystore-password>' \
SIGNING_KEY_PASSWORD='<release-key-password>' \
ENABLE_SOURCESTAMP=true \
SOURCESTAMP_KEYSTORE_PATH=android/keystore/meshchatx-stamp.jks \
SOURCESTAMP_KEY_ALIAS=meshchatx-stamp \
SOURCESTAMP_KEYSTORE_PASSWORD='<stamp-keystore-password>' \
SOURCESTAMP_KEY_PASSWORD='<stamp-key-password>' \
bash scripts/sign-android-apks.sh
The helper script auto-detects the newest Android Build-Tools, runs zipalign, signs with apksigner, and verifies each output certificate/signature block.
Updating Android Python ABI Wheels (Python 3.11)
Use this workflow when a dependency (for example cryptography) requires custom Android wheels for the ABIs listed in app/build.gradle (ndk.abiFilters).
For armeabi-v7a, Chaquopy usually has no prebuilt NumPy wheel; scripts/build-android-wheels-local.sh builds NumPy from source and must cache the official chaquopy-openblas wheel (handled in the script) and run with ANDROID_HOME pointing at an SDK that includes Command-line Tools and NDK 27.3.13750724 (Chaquopy’s target/android-env.sh installs the NDK via sdkmanager if missing).
- Build wheels in a Podman Python 3.11 container to avoid host Python mismatches:
- Use
docker.io/library/python:3.11-bookworm. - Mount project root to
/workand Android SDK to/opt/android-sdk. - Export
ANDROID_HOMEandANDROID_SDK_ROOTto/opt/android-sdk. - Example container entry:
podman run --rm --network host -e ANDROID_HOME=/opt/android-sdk -e ANDROID_SDK_ROOT=/opt/android-sdk -v "/opt/android-sdk:/opt/android-sdk" -v "<repo>:/work" -w /work docker.io/library/python:3.11-bookworm bash
- Use
- Keep custom Chaquopy recipes in
android/chaquopy-recipes/<package>-<major>/:- Define package/version in
meta.yaml. - Store source patches in
patches/.
- Define package/version in
- Build the configured ABIs with Chaquopy
build-wheel.py(viascripts/build-android-wheels-local.sh) and place final wheels inandroid/vendor/. - Update
android/app/build.gradlepipinstalls to the new pinned version. - Rebuild with
./gradlew assembleDebugand verify split outputs (per enabled ABIs), includingapp-universal-debug.apk.
Local host build example (no shell startup files required):
cd /path/to/reticulum-meshchatX
ANDROID_HOME="$HOME/Android/sdk" \
ANDROID_SDK_ROOT="$HOME/Android/sdk" \
PYTHON_BIN="$(uv python find 3.11)" \
bash scripts/build-android-wheels-local.sh --abis armeabi-v7a
If uv does not have Python 3.11 yet, install it first:
uv python install 3.11
If the wheel build fails with No such file or directory: 'patchelf', install patchelf and re-run the same command.
Notes:
- For Rust-backed wheels (such as modern
cryptography), build inside the container with Rust toolchain available. - Keep recipe files and patches versioned; keep generated build artifacts untracked.
Custom Recipes and Patches
This project keeps Android-specific Chaquopy recipes in android/chaquopy-recipes/ to bridge gaps between desktop Python dependencies and Android wheel availability.
-
cryptography-46- Purpose: provide Android ABI wheels for
cryptography 46.0.7for each ABI in the wheel script (for examplearm64-v8a,x86_64,armeabi-v7a) because upstream Chaquopy index only provided older builds. patches/openssl_no_legacy.patch: disables OpenSSL legacy provider loading, which is unavailable in the bundled Android OpenSSL runtime.patches/pyo3_no_interpreter.patch: enables compatiblepyo3ABI settings for Chaquopy Python 3.11 Android builds.
- Purpose: provide Android ABI wheels for
-
aiohttp-3.13- Purpose: align Android with desktop dependency line (
aiohttp 3.13.3) by building fresh ABI wheels with Chaquopy. - No source patch is required; recipe pins the newer upstream version for Android wheel generation.
- Purpose: align Android with desktop dependency line (
-
psutil-7.2- Purpose: align Android with desktop dependency line (
psutil 7.2.2) while preserving Android runtime behavior. patches/chaquopy.patch: treatsandroidplatform as Linux in psutil internals and forces a safe partition enumeration path because/proc/filesystemscan be restricted by SELinux on some Android API levels.
- Purpose: align Android with desktop dependency line (
-
bcrypt-5- Purpose: tracks attempted upgrade path to desktop-equivalent bcrypt.
- Status: currently not enabled in Android app dependencies;
bcrypt==3.1.7remains pinned for stable APK builds.
License
This directory is part of the main project licensing split:
- project-owned portions: 0BSD
- original upstream MeshChat portions: MIT
See ../LICENSE for full text and notices.