fix(ios audio): stop dropping call audio on tap — never reconfigure session mid-call

Root cause of 'tapping stops the Bluetooth audio': bzUnlockAudio fires on EVERY tap
during a call and called bzApplyRoute -> setSpeaker(false) -> configureDevice, whose
setCategory+setActive(true) reconfigures the AVAudioSession mid-call and interrupts
WebKit's audio unit, dropping the call audio. Fix: bzApplyRoute on iOS no longer touches
the session at all (only wires the route-icon listener); iOS keeps auto-routing. build batch152.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-22 19:30:35 +05:30
parent 5cee67e974
commit 2f368643ad
+8 -6
View File
@@ -1158,7 +1158,7 @@
</head> </head>
<body> <body>
<script src="/icons.js?v=6"></script> <script src="/icons.js?v=6"></script>
<script>window.__BUILD='2026-07-22-batch151';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD); <script>window.__BUILD='2026-07-22-batch152';console.log('%cBiz Connect','color:#1F3B73;font-weight:bold','build '+window.__BUILD);
// Emoji are rendered with the OS's own (colour) emoji font — instant, zero network. // Emoji are rendered with the OS's own (colour) emoji font — instant, zero network.
// //
// We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from // We used to run Twemoji over every emoji, which swapped each one for an <img> pulled INDIVIDUALLY from
@@ -4371,11 +4371,13 @@ function nativeAudioRoute(){ try{ var C=window.Capacitor; return (C && C.getPlat
function bzApplyRoute(route){ function bzApplyRoute(route){
var ar=nativeAudioRoute(); var ar=nativeAudioRoute();
// iOS: the WKWebView owns call audio and auto-routes it — a connected Bluetooth/wired headset, else the // iOS: the WKWebView owns call audio and auto-routes it — a connected Bluetooth/wired headset, else the
// loudspeaker. PROVEN on device: the app can force NEITHER the earpiece NOR the loudspeaker-over-active-BT // loudspeaker. PROVEN on device: the app can force NEITHER the earpiece NOR the loudspeaker-over-active-BT.
// (WebKit re-asserts within ~1s). So we don't fight it: always use the default port (setSpeaker(false)) and // CRITICAL: do NOT call setSpeaker / reconfigure the AVAudioSession here. This runs on EVERY tap during a
// let iOS pick; the button is a live INDICATOR of the actual output. Native-grade routing (earpiece, hold // call (via bzUnlockAudio), and setCategory/setActive mid-call interrupts WebKit's audio unit and DROPS the
// speaker over BT, CallKit) would require a native WebRTC stack (e.g. the LiveKit iOS SDK), not the webview. // call audio (esp. on Bluetooth). We only wire up the route listener so the button icon tracks the real
if(ar){ bzInitNativeRoute(); try{ ar.setSpeaker({ on:false }); }catch(_){} } // output; the route itself is left entirely to iOS. Native-grade routing (earpiece, hold speaker over BT,
// CallKit) would require a native WebRTC stack (e.g. the LiveKit iOS SDK), not the webview.
if(ar){ bzInitNativeRoute(); }
} }
// The speaker toggle is usable if the browser supports output switching (setSinkId) OR the native iOS route // The speaker toggle is usable if the browser supports output switching (setSinkId) OR the native iOS route
// plugin is present. // plugin is present.