# 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

```bash
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)

```bash
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.jks`
- `ANDROID_KEYSTORE_PASSWORD`
- `ANDROID_KEY_ALIAS` — `pedscribe`
- `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:

```bash
# 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)

```bash
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

```bash
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 |
