diff --git a/TECHSTACK.md b/TECHSTACK.md index 88c6087..354c210 100644 --- a/TECHSTACK.md +++ b/TECHSTACK.md @@ -115,8 +115,9 @@ hash, per-user salt, timing-safe compare), opaque random session tokens, SHA-256 - **iOS build:** Capacitor 8 + **SPM** (CocoaPods retired); Xcode 26; a share extension + a broadcast extension. - **Android build:** JDK **21**, AGP 8.7 / Gradle 8.11, **minSdk 24** (raised for LiveKit), target SDK 36, edge-to-edge; the native-call plugin module also applies **kotlin-android** for the LiveKit media layer. - `mobile/android/` is generated fresh per build, so permissions **and the minSdk bump** are applied in CI by - `mobile/scripts/android-patch.js`. + `mobile/android/` is generated fresh per build, so permissions, the **minSdk bump**, and the **JitPack + repository** (`allprojects`/`dependencyResolutionManagement`) that LiveKit Android's transitive + `com.github.davidliu:audioswitch` fork needs are all applied in CI by `mobile/scripts/android-patch.js`. ## Desktop app (`desktop/`) — Electron @@ -202,7 +203,8 @@ Bump the **Last updated** date at the top and add a line to the Changelog below. - **2026-10-30 (rev 3)** — Android native call media (Increment 2a): added `io.livekit:livekit-android` 2.29.2 + `com.twilio:audioswitch` + Kotlin to the native-call plugin, a `microphone` foreground service, the - `ANDROID_NATIVE_MEDIA` server flag, and raised the Android app to minSdk 24. + `ANDROID_NATIVE_MEDIA` server flag, and raised the Android app to minSdk 24. Build fix: `android-patch.js` + now injects the **JitPack** repo (LiveKit's `com.github.davidliu:audioswitch` fork resolves nowhere else). - **2026-10-27 (rev 2)** — Added the **Identity & auth** section (sessions/cookies + bearer, refresh tokens, TOTP MFA, BizGaze IdP/SSO, roles & tenancy, API keys, directory, audit). - **2026-10-27** — Initial tech-stack document created. diff --git a/mobile/scripts/android-patch.js b/mobile/scripts/android-patch.js index c216fbf..260328d 100644 --- a/mobile/scripts/android-patch.js +++ b/mobile/scripts/android-patch.js @@ -37,6 +37,58 @@ try { } } catch (e) { console.log(' minSdk bump skipped:', e.message); } + // (0b) JitPack repository for LiveKit Android's transitive deps (Increment 2). LiveKit 2.29.2 pulls in + // com.github.davidliu:audioswitch — a JitPack-only fork pinned to a commit-hash version. Gradle resolves + // the FULL transitive graph of :app:debugRuntimeClasspath against the APP module's (+ allprojects) + // repositories, NOT the native-call plugin's, so a jitpack repo declared only in the plugin is ignored and + // the build dies with "Could not find com.github.davidliu:audioswitch". Inject jitpack where :app sees it: + // the generated root build.gradle's allprojects{} block (Capacitor's template) and, if a newer template + // uses it instead, settings.gradle's dependencyResolutionManagement{}. Idempotent (keyed on jitpack.io), + // tolerant (any miss just logs + continues — never fails the build). + try { + const path = require('path'); + const base = path.resolve(path.dirname(p), '..', '..', '..'); // android/ + const jitLine = " maven { url 'https://jitpack.io' } // bzcJitpack: LiveKit Android transitive deps"; + + // root build.gradle → allprojects { repositories { ... } } (classic Capacitor template) + const rootGradle = path.join(base, 'build.gradle'); + if (fs.existsSync(rootGradle)) { + let g = fs.readFileSync(rootGradle, 'utf8'); + if (g.includes('jitpack.io')) { + console.log(' jitpack already present in build.gradle'); + } else { + const re = /(allprojects\s*\{\s*repositories\s*\{)/; + if (re.test(g)) { + g = g.replace(re, '$1\n' + jitLine); + fs.writeFileSync(rootGradle, g); + console.log(' jitpack repo injected into allprojects (root build.gradle)'); + } else { + console.log(' allprojects{repositories{}} not found in build.gradle — jitpack root inject skipped'); + } + } + } else { + console.log(' root build.gradle not found — jitpack root inject skipped'); + } + + // settings.gradle → dependencyResolutionManagement { repositories { ... } } (newer templates, if any) + const settingsGradle = path.join(base, 'settings.gradle'); + if (fs.existsSync(settingsGradle)) { + let s2 = fs.readFileSync(settingsGradle, 'utf8'); + if (s2.includes('jitpack.io')) { + console.log(' jitpack already present in settings.gradle'); + } else { + const re2 = /(dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{)/; + if (re2.test(s2)) { + s2 = s2.replace(re2, '$1\n' + jitLine); + fs.writeFileSync(settingsGradle, s2); + console.log(' jitpack repo injected into dependencyResolutionManagement (settings.gradle)'); + } else { + console.log(' no dependencyResolutionManagement in settings.gradle — nothing to patch there'); + } + } + } + } catch (e) { console.log(' jitpack inject skipped:', e.message); } + // (1) Native-call FirebaseMessagingService swap. The native-call plugin declares its own MESSAGING_EVENT // service (BzCallMessagingService) that handles call-wake data messages AND forwards regular push to Capacitor // via super. Remove @capacitor/push-notifications' own MessagingService here — the APP manifest is the highest