feat(ios): user-toggleable speakerphone output

Add a "speaker output" override so users can route audio to the built-in
speaker instead of the earpiece when no headphones/Bluetooth are connected.
Previously the receiver was the only fallback on the default Voice Chat preset.

The toggle inserts .defaultToSpeaker into the AVAudioSession category options
(skipped for the A2DP mode, where it would break Bluetooth routing). It yields
to connected BT/wired output and is orthogonal to preset matching. Exposed both
as a call-bar button in VoiceControlsView and a persisted Settings toggle.
This commit is contained in:
2026-06-21 15:47:20 +02:00
parent 0c6b1a36cf
commit 5be6d8430d
3 changed files with 48 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ import ReplayKit
struct VoiceControlsView: View {
@Bindable var session: SessionState
@StateObject private var router = IOSAudioRouter.shared
var body: some View {
HStack(spacing: 20) {
@@ -37,6 +38,19 @@ struct VoiceControlsView: View {
Spacer()
// Speaker output toggle force the built-in speaker instead of the earpiece when
// no headphones/BT are connected. Mirrors the persisted Settings Audio toggle.
Button {
router.setForceSpeaker(!router.forceSpeaker)
} label: {
Image(systemName: router.forceSpeaker ? "speaker.wave.2.fill" : "speaker.fill")
.font(.title3)
.foregroundStyle(router.forceSpeaker ? Color.accentColor : .primary)
}
.accessibilityLabel(router.forceSpeaker
? "Speaker on — turn off to use the earpiece"
: "Speaker off — turn on for speakerphone")
// Self mute (disabled when not in voice)
Button {
session.setMute(!session.voiceState.selfMuted, deafened: session.voiceState.selfDeafened)