mobile-build.md:
- Removed "PedsHub Quiz" sections. That app lives in a separate
repo (quiz/mobile/) and has its own build pipeline. Docs here
are PedScribe-only now.
- Reorganized around CI as the primary flow, local build as
fallback. Added explicit secret names, JDK requirement, single-
quote-password caveat, QEMU/argon2 note.
- File-map section at the end so the native sources are
discoverable without grepping.
CONTRIBUTING.md:
- Cut the narrative prose. Dev-facing tables + single-line
commands only. Decision-tree removed (the table suffices).
- Release pipeline and mobile build link out rather than
duplicating content.
109 lines
3.8 KiB
Markdown
109 lines
3.8 KiB
Markdown
# Mobile build & release
|
|
|
|
Capacitor 6 wrapper. Android only today; iOS project exists but requires macOS
|
|
+ Xcode to produce an `.ipa`.
|
|
|
|
## 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)
|
|
|
|
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_BASE64` — `base64 -w0 ~/pedscribe-release.jks`
|
|
- `ANDROID_KEYSTORE_PASSWORD`
|
|
- `ANDROID_KEY_ALIAS` — `pedscribe`
|
|
- `ANDROID_KEY_PASSWORD`
|
|
|
|
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 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)
|
|
|
|
```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/`.
|
|
|
|
### 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
|
|
|
|
| Path | Purpose |
|
|
|---|---|
|
|
| `mobile/capacitor.config.json` | app ID, 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 |
|