vojo/docs/ai/android.md

4.1 KiB

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.tsappId: chat.vojo.app, webDir: dist
  • android/ — generated Android Studio project, targetSdkVersion 36, compileSdkVersion 36, minSdkVersion 24

Build scripts

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.
  • Safe-area coloring. body background-color reads --vojo-safe-area-bg (set on :root in src/app/styles/global.css.ts, default #0d0e11 = chat-list tone). 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), PageNav inner column (Page.tsx → catches SelfRow / WorkspaceFooter / etc.), bottom call rail (HorseshoeContainer.css.ts), AuthFooter (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:

  • 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/<variant>/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 <ip>:<pair-port> <code>
  3. adb connect <ip>:<connect-port>

The pair port and the connect port are different — don't mix them up.