Files
BizGaze_Remote/mobile/plugins/audio-route/ios/Sources/AudioRoutePlugin/AudioRoutePlugin.swift
T
Sravan c6f236ecf9 speaker: make AudioRoute a real Capacitor plugin PACKAGE so it registers (device log: has:0)
Device telemetry proved the app-embedded AudioRoute class never registered (not in
Capacitor.Plugins) — appending a CAPBridgedPlugin to AppDelegate.swift gets stripped/undiscovered
in release builds. The plugins that DO register (Share, Camera, Filesystem) are all npm packages
wired by cap sync. So AudioRoute is now a local plugin package (mobile/plugins/audio-route,
file: dep in mobile/package.json) with a podspec + CAPBridgedPlugin Swift — cap sync adds its pod
and Capacitor registers it like the others. load() sets the launch speaker default; setSpeaker({on})
overrides the output port. inject-audio.js no longer injects the plugin class (would duplicate);
it keeps only the AppDelegate launch default as a fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 17:06:16 +05:30

36 lines
1.6 KiB
Swift

import Foundation
import Capacitor
import AVFoundation
// Earpiece <-> speaker toggle for calls. Registered by cap sync as a normal Capacitor plugin package, so it
// appears as window.Capacitor.Plugins.AudioRoute (unlike an app-embedded class, which gets stripped/not
// discovered in release builds). setSpeaker({on}) overrides the AVAudioSession output port.
@objc(AudioRoutePlugin)
public class AudioRoutePlugin: CAPPlugin, CAPBridgedPlugin {
public let identifier = "AudioRoutePlugin"
public let jsName = "AudioRoute"
public let pluginMethods: [CAPPluginMethod] = [
CAPPluginMethod(name: "setSpeaker", returnType: CAPPluginReturnPromise)
]
override public func load() {
// Default calls to the loudspeaker when the plugin initialises (iOS uses the quiet earpiece otherwise).
let session = AVAudioSession.sharedInstance()
try? session.setCategory(.playAndRecord, mode: .voiceChat, options: [.defaultToSpeaker, .allowBluetooth, .allowBluetoothA2DP])
try? session.setActive(true)
}
@objc func setSpeaker(_ call: CAPPluginCall) {
let on = call.getBool("on") ?? true
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
try session.setActive(true)
try session.overrideOutputAudioPort(on ? .speaker : .none)
call.resolve(["route": on ? "speaker" : "earpiece"])
} catch {
call.reject(error.localizedDescription)
}
}
}