feat(mobile): Android app shell with Capacitor

The web client already has a full mobile UI (MobileShell and 17 sibling
screens) and ships as a PWA, so what was missing to get an APK was a native
container, not an interface.

The WebView loads the instance over the network instead of bundling a copy of
the web build. Bundling would mean republishing an APK on every site deploy —
there is no electron-updater equivalent here — and it would move the page to
https://localhost, turning every same-origin /api and /ws call into a
cross-origin one. The cost is no offline mode, which for a realtime chat app
costs nothing: public/index.html covers the unreachable-server case.

packages/mobile sits outside the pnpm workspace on purpose. Capacitor's CLI and
Gradle both assume a flat node_modules, and keeping it out leaves pnpm-lock.yaml
and the existing web/desktop CI untouched by it.

Icons are generated from the existing brand art. The launch screen is a
layer-list over the brand background rather than Capacitor's per-density
splash.png set, which is light and flashed white before the WebView painted.

CI builds an *unsigned* APK. The signing key stays on the VPS and is applied
there — an APK signature is what proves an update came from us, so the key has
no reason to exist on a second machine.

Background voice (foreground service) is not in this commit: a plain WebView is
throttled when the screen goes off and the call drops. That is the next phase.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 13:09:15 -03:00
co-authored by Claude Opus 5
parent aee78c930d
commit f808f2f535
52 changed files with 2388 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
ext {
minSdkVersion = 23
compileSdkVersion = 35
targetSdkVersion = 35
androidxActivityVersion = '1.9.2'
androidxAppCompatVersion = '1.7.0'
androidxCoordinatorLayoutVersion = '1.2.0'
androidxCoreVersion = '1.15.0'
androidxFragmentVersion = '1.8.4'
coreSplashScreenVersion = '1.0.1'
androidxWebkitVersion = '1.12.1'
junitVersion = '4.13.2'
androidxJunitVersion = '1.2.1'
androidxEspressoCoreVersion = '3.6.1'
cordovaAndroidVersion = '10.1.1'
}