# Android (Capacitor) ## Requirements - Node >= 22 - JDK 17+ (21 used in practice) - Android SDK with platform 36 + build-tools 36.0.0 - SDK location: `/usr/lib/android-sdk`, also set in `android/local.properties` ## Config - [`capacitor.config.ts`](../../capacitor.config.ts) — `appId: chat.vojo.app`, `webDir: dist` - `android/` — generated Android Studio project, `targetSdkVersion 36`, `compileSdkVersion 36`, `minSdkVersion 24` ## Build scripts ```bash npm run build:android:debug # full chain: build → sync → debug APK npm run build:android:release # full chain: build → sync → release APK npm run build:android:aab # full chain: build → sync → release AAB npm run android:sync # sync dist/ → android assets npm run android:apk:debug # gradle debug build only ``` **APK output**: `android/app/build/outputs/apk/debug/app-debug.apk` ## Versioning `versionCode` and `versionName` auto-derived from `package.json` version: ``` versionCode = major * 1_000_000 + minor * 1_000 + patch ``` ## Key architecture decisions - **Bundled build.** `dist/` is copied into the APK — not loaded remotely in a WebView. - **Service Worker stays active.** Critical for authenticated Matrix media (MSC3916 / Matrix spec v1.11+). DO NOT disable. `resolveServiceWorkerRequests` default `true`. - **Edge-to-edge.** `EdgeToEdge.enable()` in `MainActivity.java` + `windowLayoutInDisplayCutoutMode: shortEdges`. - **External links.** Opened via `@capacitor/browser` plugin — see [`src/app/utils/capacitor.ts`](../../src/app/utils/capacitor.ts). - **Safe-area coloring.** `body` background-color reads `--vojo-safe-area-bg` (set on `:root` in [`src/app/styles/global.css.ts`](../../src/app/styles/global.css.ts), default `#0d0e11` = chat-list tone). [`Room.tsx`](../../src/app/features/room/Room.tsx) retunes the var to `#181a20` (chat-surface tone) while a chat is mounted so the status-bar / gesture-bar zones never show a seam against the active surface. - **Safe-area insets — top / left / right only on `#root`.** Bottom inset is intentionally **not** applied at `#root` so the app renders edge-to-edge under the Android gesture pill / 3-button bar / iOS home indicator (mirrors WhatsApp / Telegram). Components that anchor interactive UI at the screen bottom MUST add `padding-bottom: var(--vojo-safe-bottom)` themselves — covered: chat composer ([`RoomView.css.ts`](../../src/app/features/room/RoomView.css.ts)), PageNav inner column ([`Page.tsx`](../../src/app/components/page/Page.tsx) → catches SelfRow / WorkspaceFooter / etc.), bottom call rail ([`HorseshoeContainer.css.ts`](../../src/app/pages/HorseshoeContainer.css.ts)), AuthFooter ([`auth/styles.css.ts`](../../src/app/pages/auth/styles.css.ts)). New screens with a bottom CTA must follow this rule or the button lands behind a system 3-button nav bar. ## VSCode tasks See [`.vscode/tasks.json`](../../.vscode/tasks.json): - `Deploy to vojo.chat` (Ctrl+Shift+D) — web deploy - `Deploy to Android (ADB)` (Ctrl+Shift+A) — build + `adb install` ## Push string resources (generated) Push notification text for Android is generated from `public/locales/{en,ru}.json` (namespace `Push`) by `scripts/gen-push-strings.mjs`. The Gradle build runs this automatically via `GeneratePushStringsTask` registered in `android/app/build.gradle` through AGP `addGeneratedSourceDirectory` — output goes to `build/generated/res/push//values{,-ru}/push_strings.xml`. No manual step needed; `./gradlew assembleDebug` handles it. The task requires `node` in `PATH`. Terminal builds and CI inherit it from the shell. **macOS Android Studio with nvm/fnm:** the GUI app may not see nvm-managed node. Workaround: set `NODE_BIN=/path/to/node` in `android/gradle.properties` (the task reads it via `project.findProperty('NODE_BIN')`) or launch AS from a shell that sources your node manager (`open -a "Android Studio"`). ## ADB wireless workflow 1. On the phone, enable Wireless debugging, tap "Pair device with pairing code" — note IP, port, 6-digit code. 2. `adb pair : ` 3. `adb connect :` The pair port and the connect port are different — don't mix them up.