Files
MeshChatX/android

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_HOME for the Gradle invocation).
  • Android SDK with API 34 platform and Build-Tools 34 installed. Set ANDROID_HOME and ANDROID_SDK_ROOT to 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, install patchelf 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 legacy tools/bin/sdkmanager from 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 with NoClassDefFoundError: 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/sdkmanager under ANDROID_HOME (the inner folder is often named latest; see Googles 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.apk
  • app-x86_64-debug.apk
  • app-armeabi-v7a-debug.apk
  • app-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 (Chaquopys target/android-env.sh installs the NDK via sdkmanager if missing).

  1. 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 /work and Android SDK to /opt/android-sdk.
    • Export ANDROID_HOME and ANDROID_SDK_ROOT to /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
  2. Keep custom Chaquopy recipes in android/chaquopy-recipes/<package>-<major>/:
    • Define package/version in meta.yaml.
    • Store source patches in patches/.
  3. Build the configured ABIs with Chaquopy build-wheel.py (via scripts/build-android-wheels-local.sh) and place final wheels in android/vendor/.
  4. Update android/app/build.gradle pip installs to the new pinned version.
  5. Rebuild with ./gradlew assembleDebug and verify split outputs (per enabled ABIs), including app-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.7 for each ABI in the wheel script (for example arm64-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 compatible pyo3 ABI settings for Chaquopy Python 3.11 Android builds.
  • 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.
  • psutil-7.2

    • Purpose: align Android with desktop dependency line (psutil 7.2.2) while preserving Android runtime behavior.
    • patches/chaquopy.patch: treats android platform as Linux in psutil internals and forces a safe partition enumeration path because /proc/filesystems can be restricted by SELinux on some Android API levels.
  • bcrypt-5

    • Purpose: tracks attempted upgrade path to desktop-equivalent bcrypt.
    • Status: currently not enabled in Android app dependencies; bcrypt==3.1.7 remains 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.