7df9ebf22c
Sync labels / sync (push) Failing after 1m9s
Publish Container Image / Build and publish container image (push) Has been cancelled
Deploy Website / Deploy to GitHub Pages (push) Has been cancelled
Deploy Website / Build website and demo (push) Has been cancelled
Deploy viewer / Deploy viewer proxy to Cloudflare Workers (push) Has been cancelled
Deploy tiles / Deploy planetary tile proxy to Cloudflare Workers (push) Has been cancelled
Deploy collab / Deploy collaboration relay to Cloudflare Workers (push) Has been cancelled
CI / Dependency audit (push) Has been cancelled
CI / E2E smoke (Playwright) (push) Has been cancelled
CI / Validate CITATION.cff (push) Has been cancelled
CI / Build and test (push) Has been cancelled
147 lines
5.7 KiB
Markdown
147 lines
5.7 KiB
Markdown
# Android
|
||
|
||
GeoLibre runs as a native Android app built from the same React codebase via
|
||
**Tauri v2 mobile** — no separate app. The webview UI is bundled in the APK, so
|
||
the app shell works offline; map tiles and the heavier engines are fetched on
|
||
demand (same as the desktop build).
|
||
|
||
## What works on Android vs desktop
|
||
|
||
The Android build ships the full map workspace, Add Data, the Vector tools
|
||
(Turf.js / in-browser GeoPandas via Pyodide), the SQL Workspace (DuckDB-WASM and
|
||
the in-browser PGlite/PostGIS engine), the Python Console (Pyodide), geocoding,
|
||
statistics, the AI assistant, story maps, and plugins.
|
||
|
||
Tools that depend on a **local desktop process** are hidden on mobile, because
|
||
Android has no Python sidecar or local helper binaries:
|
||
|
||
- Processing → **Whitebox**, **Raster**, **Conversion**, **AI Segmentation**
|
||
(all need the Python sidecar)
|
||
- Add Data → **PostgreSQL** (served by the local Martin tile server)
|
||
|
||
These are gated by a user-agent `isMobile()` check so they never appear and then
|
||
fail. Everything else runs client-side.
|
||
|
||
## Toolchain setup (one time)
|
||
|
||
You need the Android SDK + NDK, a JDK (17 or 21 — newer JDKs can break the
|
||
Android Gradle Plugin), and the Rust Android targets. The cleanest, sudo-free
|
||
layout keeps everything under a user-writable SDK at `~/Android/Sdk`.
|
||
|
||
```bash
|
||
# 1. JDK 17/21 (or reuse Android Studio's bundled JBR at /opt/android-studio/jbr)
|
||
export JAVA_HOME=/path/to/jdk-21
|
||
|
||
# 2. Android SDK components (sdkmanager ships with Android Studio cmdline-tools)
|
||
export ANDROID_HOME="$HOME/Android/Sdk"
|
||
yes | sdkmanager --sdk_root="$ANDROID_HOME" --licenses
|
||
sdkmanager --sdk_root="$ANDROID_HOME" \
|
||
"platform-tools" "platforms;android-34" \
|
||
"build-tools;34.0.0" "ndk;27.3.13750724"
|
||
export NDK_HOME="$ANDROID_HOME/ndk/27.3.13750724" # Tauri needs NDK_HOME
|
||
|
||
# 3. Rust + the four Android targets (install rustup if you don't have it)
|
||
rustup target add aarch64-linux-android armv7-linux-androideabi \
|
||
i686-linux-android x86_64-linux-android
|
||
```
|
||
|
||
NDK **r27 (LTS)** is the supported line for Tauri v2. Add the four `export`s to
|
||
your shell profile so every session has them.
|
||
|
||
## Build
|
||
|
||
```bash
|
||
cd apps/geolibre-desktop
|
||
npx tauri android init # generate src-tauri/gen/android (once)
|
||
npx tauri android build --apk --split-per-abi # release APKs, one per ABI (~40 MB each)
|
||
```
|
||
|
||
- `gen/android` is generated (git-ignored) and regenerated on demand.
|
||
- Build **release**, not `--debug`: the stripped, size-optimized Cargo profile
|
||
makes each APK ~40 MB; a debug build is ~200 MB (unstripped `.so` with
|
||
debuginfo).
|
||
- `--split-per-abi` emits one APK per architecture instead of a single ~150 MB
|
||
universal APK. Install the **`arm64-v8a`** one on real phones.
|
||
- Output:
|
||
`src-tauri/gen/android/app/build/outputs/apk/<abi>/release/app-<abi>-release-unsigned.apk`.
|
||
|
||
The app is named **GeoLibre** on Android (the desktop build is "GeoLibre
|
||
Desktop"), set via `src-tauri/tauri.android.conf.json`, which also drops the
|
||
Python backend from the Android bundle.
|
||
|
||
## Signing
|
||
|
||
Release APKs are unsigned. To install one, sign it (a debug key is fine for
|
||
testing; use a real key for distribution):
|
||
|
||
```bash
|
||
BT="$ANDROID_HOME/build-tools/34.0.0"
|
||
KS="$HOME/.android/debug.keystore" # auto-created by Android tooling; or make your own
|
||
"$BT/zipalign" -p -f 4 app-arm64-v8a-release-unsigned.apk aligned.apk
|
||
"$BT/apksigner" sign --ks "$KS" --ks-pass pass:android \
|
||
--ks-key-alias androiddebugkey --key-pass pass:android \
|
||
--out geolibre-arm64.apk aligned.apk
|
||
"$BT/apksigner" verify geolibre-arm64.apk
|
||
```
|
||
|
||
For a real upload/release key:
|
||
|
||
```bash
|
||
keytool -genkeypair -v -keystore upload.jks -alias upload -keyalg RSA \
|
||
-keysize 2048 -validity 10000
|
||
```
|
||
|
||
## Continuous integration
|
||
|
||
`.github/workflows/android.yml` builds **signed**, per-ABI release APKs on each
|
||
published GitHub release (and on demand via the "Run workflow" button) and
|
||
uploads them as the `geolibre-android-release-apks` artifact. It signs with your release keystore when these repository secrets are
|
||
set, and otherwise falls back to a throwaway debug key so the artifact is still
|
||
installable for testing:
|
||
|
||
- `ANDROID_KEYSTORE_BASE64` — `base64 -w0 upload.jks`
|
||
- `ANDROID_KEYSTORE_PASSWORD`
|
||
- `ANDROID_KEY_ALIAS`
|
||
- `ANDROID_KEY_PASSWORD`
|
||
|
||
## Install / test
|
||
|
||
### On a phone
|
||
|
||
1. Enable **Developer options** (tap Build number 7×) and **USB debugging**.
|
||
2. Sideload the signed APK:
|
||
```bash
|
||
adb install -r geolibre-arm64.apk
|
||
```
|
||
Or copy the APK to the phone and tap it (allow "install unknown apps").
|
||
|
||
For live development with hot reload, connect the device and run
|
||
`npm run tauri android dev`.
|
||
|
||
### On an emulator
|
||
|
||
```bash
|
||
sdkmanager --sdk_root="$ANDROID_HOME" \
|
||
"emulator" "system-images;android-34;google_apis_playstore;x86_64"
|
||
avdmanager create avd -n geolibre \
|
||
-k "system-images;android-34;google_apis_playstore;x86_64" -d pixel_7
|
||
emulator -avd geolibre
|
||
adb install -r geolibre-arm64.apk
|
||
```
|
||
|
||
> If you ever rebuild with a **different** signing key, uninstall the old copy
|
||
> first (`adb uninstall org.geolibre.desktop`) — Android rejects updates whose
|
||
> signature changed. The package id stays `org.geolibre.desktop` even though the
|
||
> visible name is "GeoLibre".
|
||
|
||
## Known limitations / follow-ups
|
||
|
||
- Local-file sources (MBTiles, local rasters, project files) assume real
|
||
filesystem paths; Android scoped storage returns content URIs, so those flows
|
||
need adapting before they work natively.
|
||
- The **Download Offline Area** tool relies on a service worker, which the Tauri
|
||
builds (desktop and Android) don't use — it's a PWA feature. Native offline
|
||
basemap caching (bundled/downloaded MBTiles/PMTiles) is a future enhancement.
|
||
- Earth Engine OAuth uses a desktop loopback/multi-window flow; a mobile
|
||
deep-link redirect is future work.
|