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_BASE64—base64 -w0 ~/pedscribe-release.jksANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIAS—pedscribeANDROID_KEY_PASSWORDGOOGLE_PLAY_SERVICE_ACCOUNT_JSON_B64— base64 of your Google Play service account JSON (optional). If present, the same tag build also runsbundleReleaseand uploads the AAB to Play'sinternaltrack.
Optional Play Store flow:
- Service account must have permissions to edit releases on the app in Play.
- Build task is
bundleRelease, tracked ascom.pedshub.scribe. - Upload lane is
fastlane/android publish_internal(undermobile/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 assembleRelease → bundleRelease; 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-amd64in~/.gradle/gradle.propertiesif 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 capmust run insidemobile/, not repo root.- Foreground recording on Android 14+ requires
foregroundServiceType="microphone"inAndroidManifest.xmlplus the 3-argstartForeground(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 |