# 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](#updating-android-python-abi-wheels-python-311)). 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](https://developer.android.com/studio#command-tools), extract it so you have `cmdline-tools/latest/bin/sdkmanager` under `ANDROID_HOME` (the inner folder is often named `latest`; see Google’s layout for that zip). - Accept licenses (writes under the SDK; use sudo if the SDK is root-owned): ```bash yes | path/to/cmdline-tools/latest/bin/sdkmanager --licenses ``` - Install missing packages if the build still complains (platform 34, build-tools 34, etc.): ```bash path/to/cmdline-tools/latest/bin/sdkmanager "platforms;android-34" "build-tools;34.0.0" ``` Build from the `android/` directory: ```bash ./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: ```bash ./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: ```bash ./gradlew --no-daemon :app:assembleRelease ``` Create a release signing key (one time): ```bash 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): ```bash keytool -genkeypair -v \ -keystore android/keystore/meshchatx-stamp.jks \ -alias meshchatx-stamp \ -keyalg RSA \ -keysize 4096 \ -validity 9125 ``` Sign all `*-unsigned.apk` release artifacts: ```bash ANDROID_HOME="$HOME/Android/sdk" \ SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \ SIGNING_KEY_ALIAS=meshchatx-release \ SIGNING_KEYSTORE_PASSWORD='' \ SIGNING_KEY_PASSWORD='' \ bash scripts/sign-android-apks.sh ``` Sign with SourceStamp enabled: ```bash ANDROID_HOME="$HOME/Android/sdk" \ SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \ SIGNING_KEY_ALIAS=meshchatx-release \ SIGNING_KEYSTORE_PASSWORD='' \ SIGNING_KEY_PASSWORD='' \ ENABLE_SOURCESTAMP=true \ SOURCESTAMP_KEYSTORE_PATH=android/keystore/meshchatx-stamp.jks \ SOURCESTAMP_KEY_ALIAS=meshchatx-stamp \ SOURCESTAMP_KEYSTORE_PASSWORD='' \ SOURCESTAMP_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). 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 ":/work" -w /work docker.io/library/python:3.11-bookworm bash` 2. Keep custom Chaquopy recipes in `android/chaquopy-recipes/-/`: - 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): ```bash 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: ```bash 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`](../LICENSE) for full text and notices.