pediatric-ai-scribe-v3/docs/mobile-build.md

4.2 KiB

Mobile build & release

Capacitor 6 wrapper. Android only today; iOS project exists but requires macOS

  • Xcode to produce an .ipa.

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)

Tag-triggered. Push any vX.Y.Z tag → .github/workflows/android-release.yml builds a signed APK on a GitHub runner and attaches it to the matching release.

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

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 6.2.0 --push

APK lands at the GitHub release; /releases/latest link in the login page resolves to it automatically. Obtanium subscribers (github.com/<owner>/<repo>) pick up the update on next poll.

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/.

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 (server URL entry)
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
.github/workflows/android-release.yml CI build