pediatric-ai-scribe-v3/docs/mobile-build.md
Daniel e1266c6d38
Some checks failed
Forgejo Android APK / Build signed APK (push) Has been cancelled
ci: route Android APK through Forgejo releases
2026-05-11 04:21:15 +02:00

5.5 KiB

Mobile Build And Release

Capacitor 6 wrapper around the hosted Ped-AI web app. The launcher defaults to https://app.pedshub.com, lets the user change the server URL, and stores that URL locally. Android is buildable on Linux. The iOS project exists but requires macOS and Xcode to produce an .ipa.

This is not a separate native clinical app. The native shell provides WebView hosting, microphone permission plumbing, secure storage, and mobile packaging for the same authenticated web app.

One-time setup

Keystore

keytool -genkeypair -v -keystore ~/pedscribe-release.jks \
  -keyalg RSA -keysize 2048 -validity 10000 -alias pedscribe

Store the password in a password manager. Back up the .jks file off the machine. Losing it = can't sign updates; Play Store requires signature continuity (unless you're on Play App Signing).

Android Studio (optional, IDE workflow only)

export CAPACITOR_ANDROID_STUDIO_PATH="/snap/android-studio/current/bin/studio.sh"
npx cap open android

CI build (preferred)

Push-triggered. Any push to main/feature branches and any vX.Y.Z tag push → .forgejo/workflows/android-apk.yml builds a signed APK on the Forgejo runner.

Tagged builds additionally publish the artifact to the matching Forgejo release as pedscribe-<tag>.apk so Obtainium can track updates.

Required repo secrets (set once, via Settings → Secrets and variables → Actions or gh secret set):

  • ANDROID_KEYSTORE_BASE64base64 -w0 ~/pedscribe-release.jks
  • ANDROID_KEYSTORE_PASSWORD
  • ANDROID_KEY_ALIASpedscribe
  • ANDROID_KEY_PASSWORD
  • GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_B64 — base64 of your Google Play service account JSON (optional). If present, the same tag build also runs bundleRelease and uploads the AAB to Play's internal track.

Optional Play Store flow:

  • Service account must have permissions to edit releases on the app in Play.
  • Build task is bundleRelease, tracked as com.pedshub.scribe.
  • Upload lane is fastlane/android publish_internal (under mobile/android/fastlane).

Tag a release:

# conventional-commits prefix auto-tags (see CONTRIBUTING.md)
git commit -m "feat: ..."  && git push      # auto-version workflow bumps minor
git commit -m "fix: ..."   && git push      # auto-version workflow bumps patch

# or force an exact version
scripts/release.sh X.Y.Z --push

APK lands on the Forgejo release. Obtainium can still track git.danvics.com/danvics/pediatric-ai-scribe-v3 releases automatically. Play Store upload is handled automatically for tagged builds only when GOOGLE_PLAY_SERVICE_ACCOUNT_JSON_B64 is configured.

Local build (fallback / debugging)

cd mobile
npm install
npx cap sync android
cd android
./gradlew assembleRelease \
  -Pandroid.injected.signing.store.file=$HOME/pedscribe-release.jks \
  -Pandroid.injected.signing.store.password='<pass>' \
  -Pandroid.injected.signing.key.alias=pedscribe \
  -Pandroid.injected.signing.key.password='<pass>'

Output: android/app/build/outputs/apk/release/app-release.apk For Play Store, swap assembleReleasebundleRelease; output: .aab under bundle/release/.

If web assets or Capacitor config changed, run npx cap sync android from mobile/ before building.

Single-quote the password

Keystore passwords with shell metacharacters (), $, !, space, etc.) must be single-quoted. Backslash line continuations get eaten by some terminal paste handlers — prefer one-line commands.

Reinstall on device

adb install -r android/app/build/outputs/apk/release/app-release.apk

-r keeps app data (saved server URL, auth token in Keystore, IndexedDB).

Gotchas

  • JDK 17 only. Newer JDK (21/25) breaks Android Gradle Plugin. Set org.gradle.java.home=/usr/lib/jvm/java-17-openjdk-amd64 in ~/.gradle/gradle.properties if the system default is different.
  • QEMU multi-arch Docker builds fail with SIGILL on native modules (argon2). Docker Hub workflow is x86-only. Use a native ARM runner if you need ARM64.
  • npx cap must run inside mobile/, not repo root.
  • Foreground recording on Android 14+ requires foregroundServiceType="microphone" in AndroidManifest.xml plus the 3-arg startForeground(id, notif, TYPE_MICROPHONE). Already applied.
  • Mic "denied" after permission grant — WebView intercepts the prompt. Fix: long-press app icon → App info → Permissions → Microphone → Allow.

Files

Note on com.pedshub.scribe: that's the Android applicationId — the OS-level unique identifier for this app. Chosen by reverse-DNS of pedshub.com. It is not a reference to the separate PedsHub Quiz app; they share a prefix by coincidence. Don't rename it — Android treats applicationId as the primary key; renaming breaks Play Store update continuity and forces every installed user to uninstall + reinstall.

Path Purpose
mobile/capacitor.config.json appId, name, WebView config, plugin opts
mobile/src/ launcher HTML and server URL entry, defaulting to https://app.pedshub.com
mobile/android/app/src/main/java/com/pedshub/scribe/MainActivity.java JS bridge + WebView mic permission
mobile/android/app/src/main/java/com/pedshub/scribe/AudioRecordingService.java foreground service for background recording
mobile/android/app/src/main/AndroidManifest.xml permissions, intents, backup rules
.forgejo/workflows/android-apk.yml CI build
mobile/android/fastlane/Fastfile internal Play track upload lane