@@ -782,6 +782,10 @@ public class SiprixVoipSdkPlugin: NSObject, FlutterPlugin {
782782 func handleModuleUnInitialize( _ args : ArgsMap , result: @escaping FlutterResult ) {
783783 let err = _siprixModule. unInitialize ( )
784784 _initialized = false
785+
786+ _pushKitProvider = nil
787+ _callKitProvider = nil
788+
785789 sendResult ( err, result: result)
786790 }
787791
@@ -1708,7 +1712,9 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
17081712 private var _cxProvider : CXProvider !
17091713 private var _cxCallCtrl : CXCallController
17101714 private var _callsList : [ CallModel ] = [ ]
1711-
1715+ private var _audioSessionConfigured = false
1716+ private var _callPendingAnswer : CallModel ? = nil
1717+
17121718 static let kECallNotFound : Int32 = - 1040
17131719 static let kEConfRequires2Calls : Int32 = - 1055
17141720
@@ -1719,7 +1725,31 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
17191725 createCxProvider ( singleCallMode, includeInRecents: includeInRecents)
17201726 _siprixModule. writeLog ( " CxProvider: created " )
17211727 }
1722-
1728+
1729+ //--------------------------------------------------------
1730+ //Audio Session Management
1731+
1732+ private func ensureAudioSessionConfigured( ) {
1733+ #if os(iOS)
1734+ guard !_audioSessionConfigured else { return }
1735+
1736+ let audioSession = AVAudioSession . sharedInstance ( )
1737+
1738+ do {
1739+ try audioSession. setCategory ( . playAndRecord, options: [ . allowBluetooth, . allowBluetoothA2DP] )
1740+ try audioSession. setMode ( . voiceChat)
1741+ try audioSession. setActive ( true )
1742+
1743+ _audioSessionConfigured = true
1744+ _siprixModule. writeLog ( " CxProvider: Manual audio session activation successful " )
1745+
1746+ } catch {
1747+ _siprixModule. writeLog ( " CxProvider: Manual audio config warning (might be ok): \( error) " )
1748+ _audioSessionConfigured = true
1749+ }
1750+ #endif
1751+ }
1752+
17231753 //--------------------------------------------------------
17241754 //Event handlers
17251755
@@ -1754,14 +1784,17 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
17541784
17551785 self . _cxProvider. reportCall ( with: call. uuid, endedAt: nil , reason: reason)
17561786 }
1757- //Remove call item from collection
1787+
1788+ if _callPendingAnswer? . id == call. id {
1789+ _callPendingAnswer = nil
1790+ _siprixModule. writeLog ( " CxProvider: cleared pending answer for terminated call \( call. id) " )
1791+ }
1792+
17581793 _callsList. remove ( at: callIdx!)
17591794 _siprixModule. writeLog ( " CxProvider: onSipTerminated remove callId: \( call. id) <=> \( call. uuid) " )
17601795 }
17611796
17621797 func onSipConnected( _ callId: Int , withVideo: Bool ) {
1763- _siprixModule. activate ( AVAudioSession . sharedInstance ( ) )
1764-
17651798 let call = self . _callsList. first ( where: { $0. id == callId} )
17661799 if ( call == nil ) { return }
17671800
@@ -1788,22 +1821,26 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
17881821 }
17891822
17901823 func onSipIncoming( _ callId: Int , withVideo: Bool , hdrFrom: String , hdrTo: String ) {
1824+ _siprixModule. writeLog ( " CxProvider: onSipIncoming CALLED - callId: \( callId) from: \( hdrFrom) " )
17911825 let call = CallModel ( callId: callId, withVideo: withVideo, from: hdrFrom)
17921826 _callsList. append ( call)
1793-
1827+ _siprixModule. writeLog ( " CxProvider: onSipIncoming - call added to list, uuid: \( call. uuid) " )
1828+
17941829 reportNewIncomingCall ( call)
1795- _siprixModule. writeLog ( " CxProvider: onSipIncoming - added new call with uuid: \( call. uuid) " )
1830+ _siprixModule. writeLog ( " CxProvider: onSipIncoming - reportNewIncomingCall completed for uuid: \( call. uuid) " )
17961831 }
1797-
1832+
17981833 public func onPushIncoming( ) -> String {
17991834 _siprixModule. handleIncomingPush ( )
18001835
1836+ _siprixModule. writeLog ( " CxProvider: onPushIncoming CALLED " )
18011837 let call = CallModel ( callId: kInvalidId, withVideo: true , from: " SiprixPushKit " )
18021838 _callsList. append ( call)
1803-
1839+ _siprixModule. writeLog ( " CxProvider: onPushIncoming - call added with uuid: \( call. uuid) , id:kInvalidId " )
1840+
18041841 reportNewIncomingCall ( call)
1805-
1806- _siprixModule. writeLog ( " CxProvider: onPushIncoming - added new call with uuid: \( call. uuid) " )
1842+
1843+ _siprixModule. writeLog ( " CxProvider: onPushIncoming - returning uuid: \( call. uuid) " )
18071844 return call. uuid. uuidString
18081845 }
18091846
@@ -1823,8 +1860,9 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
18231860
18241861 func proceedCxAnswerAction( _ call: CallModel ) {
18251862 let err = _siprixModule. callAccept ( Int32 ( call. id) , withVideo: call. withVideo)
1826- if ( err == kErrorCodeEOK) { call. cxAnswerAction? . fulfill ( ) }
1827- else { call. cxAnswerAction? . fail ( ) }
1863+ if ( err != kErrorCodeEOK) {
1864+ call. cxAnswerAction? . fail ( )
1865+ }
18281866 _siprixModule. writeLog ( " CxProvider: proceedCxAnswerAction err: \( err) sipCallId: \( call. id) uuid: \( call. uuid) ) " )
18291867 }
18301868
@@ -1850,22 +1888,35 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
18501888
18511889 public func sipAppUpdateCallDetails( _ callKit_callUUID: UUID , callId: Int ? ,
18521890 localizedName: String ? , genericHandle: String ? , withVideo: Bool ? ) {
1891+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails CALLED - uuid: \( callKit_callUUID) callId: \( String ( describing: callId) ) " )
18531892 let call = self . getCallByUUID ( callKit_callUUID)
18541893 if ( call == nil ) {
1855- self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails uuid: \( callKit_callUUID) call not found " )
1894+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - call NOT FOUND for uuid: \( callKit_callUUID) " )
18561895 return
18571896 }
1858-
1897+
1898+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - found call, state: id= \( call!. id) , answered= \( call!. answeredByCallKit) , rejected= \( call!. rejectedByCallKit) " )
1899+
18591900 if ( callId != nil ) {
18601901 //INVITE received - match SIP callId and UUID
1861- self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails uuid : \( callKit_callUUID ) set sipCallId :\( callId! ) " )
1902+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - matching SIP callId : \( callId! ) with CallKit uuid :\( callKit_callUUID ) " )
18621903 call!. setSipCallId ( callId: callId!, withVideo: withVideo)
1863-
1904+
18641905 if ( call!. rejectedByCallKit) {
1906+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - call was rejected, proceeding with end action " )
18651907 self . proceedCxEndAction ( call!)
18661908 }
18671909 else if ( call!. answeredByCallKit) {
1868- self . proceedCxAnswerAction ( call!)
1910+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - call was answered! _audioSessionConfigured= \( _audioSessionConfigured) " )
1911+ if _audioSessionConfigured {
1912+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - audio ready, accepting IMMEDIATELY " )
1913+ self . proceedCxAnswerAction ( call!)
1914+ } else {
1915+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - audio not ready, deferring until didActivate " )
1916+ _callPendingAnswer = call
1917+ }
1918+ } else {
1919+ self . _siprixModule. writeLog ( " CxProvider: sipAppUpdateCallDetails - call not answered yet (waiting for user) " )
18691920 }
18701921 }
18711922
@@ -2067,22 +2118,38 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
20672118 }
20682119
20692120 func provider( _: CXProvider , perform action: CXAnswerCallAction ) {
2121+ _siprixModule. writeLog ( " CxProvider: CXAnswerCallAction CALLED - uuid: \( action. callUUID) " )
20702122 let call = getCallByUUID ( action. callUUID)
20712123 if ( call == nil ) {
2072- _siprixModule. writeLog ( " CxProvider: CXAnswer uuid: \( action. callUUID) not found " )
2124+ _siprixModule. writeLog ( " CxProvider: Call NOT FOUND for uuid: \( action. callUUID) " )
20732125 action. fail ( )
20742126 return
20752127 }
2076-
2128+
2129+ _siprixModule. writeLog ( " CxProvider: Found call - sipCallId: \( call!. id) uuid: \( call!. uuid) fromTo: \( call!. fromTo) " )
20772130 call!. cxAnswerAction = action
2078-
2131+
20792132 if ( call!. id == kInvalidId) {
2133+ _siprixModule. writeLog ( " CxProvider: KILLED APP SCENARIO - SIP INVITE not received yet, setting answeredByCallKit=true " )
20802134 call!. answeredByCallKit = true
2081- _siprixModule. writeLog ( " CxProvider: CXAnswer uuid: \( action. callUUID) SIP hasn't received yet " )
20822135 } else {
2083- _siprixModule. writeLog ( " CxProvider: CXAnswer uuid: \( action. callUUID) callId: \( call!. id) " )
2084- proceedCxAnswerAction ( call!)
2136+ _siprixModule. writeLog ( " CxProvider: FOREGROUND SCENARIO - SIP callId: \( call!. id) known, setting _callPendingAnswer " )
2137+ _callPendingAnswer = call
2138+
2139+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 0.5 ) { [ weak self] in
2140+ guard let self = self else { return }
2141+ if let pendingCall = self . _callPendingAnswer, pendingCall. id == call!. id,
2142+ self . _callsList. contains ( where: { $0. id == pendingCall. id } ) {
2143+ self . _siprixModule. writeLog ( " CxProvider: FALLBACK TIMER FIRED - didActivate didn't fire, proceeding with answer anyway " )
2144+ self . ensureAudioSessionConfigured ( )
2145+ self . proceedCxAnswerAction ( pendingCall)
2146+ self . _callPendingAnswer = nil
2147+ }
2148+ }
20852149 }
2150+
2151+ action. fulfill ( )
2152+ _siprixModule. writeLog ( " CxProvider: Fulfilled CXAnswerCallAction - CallKit will now activate audio " )
20862153 }
20872154
20882155 func provider( _: CXProvider , perform action: CXPlayDTMFCallAction ) {
@@ -2147,12 +2214,44 @@ class SiprixCxProvider : NSObject, CXProviderDelegate {
21472214 }
21482215
21492216 func provider( _ provider: CXProvider , didActivate audioSession: AVAudioSession ) {
2150- _siprixModule. writeLog ( " CxProvider: didActivate " )
2217+ _siprixModule. writeLog ( " CxProvider: _callPendingAnswer: \( _callPendingAnswer? . id ?? - 999 ) " )
2218+ _siprixModule. writeLog ( " CxProvider: _audioSessionConfigured (before): \( _audioSessionConfigured) " )
2219+ _siprixModule. writeLog ( " CxProvider: audioSession.isInputAvailable: \( audioSession. isInputAvailable) isOtherAudioPlaying: \( audioSession. isOtherAudioPlaying) " )
2220+
2221+ #if os(iOS)
2222+ if !audioSession. isInputAvailable || audioSession. isOtherAudioPlaying {
2223+ _siprixModule. writeLog ( " CxProvider: Audio session interrupted/blocked - forcing reset " )
2224+ do {
2225+ try audioSession. setActive ( false )
2226+ try audioSession. setActive ( true )
2227+ _siprixModule. writeLog ( " CxProvider: Audio session reset successful " )
2228+ } catch {
2229+ _siprixModule. writeLog ( " CxProvider: Audio session reset failed: \( error) " )
2230+ }
2231+ }
2232+ #endif
2233+
2234+ ensureAudioSessionConfigured ( )
21512235 _siprixModule. activate ( audioSession)
2236+ _siprixModule. writeLog ( " CxProvider: _audioSessionConfigured (after): \( _audioSessionConfigured) " )
2237+
2238+ if let call = _callPendingAnswer {
2239+ _siprixModule. writeLog ( " CxProvider: Found _callPendingAnswer: callId: \( call. id) uuid: \( call. uuid) " )
2240+ let callStillExists = _callsList. contains ( where: { $0. id == call. id } )
2241+ if callStillExists {
2242+ _siprixModule. writeLog ( " CxProvider: Call still exists, proceeding with accept " )
2243+ proceedCxAnswerAction ( call)
2244+ } else {
2245+ _siprixModule. writeLog ( " CxProvider: Call no longer exists (cancelled) " )
2246+ }
2247+ _callPendingAnswer = nil
2248+ }
2249+ _siprixModule. writeLog ( " CxProvider: ======================================== didActivate END " )
21522250 }
21532251
21542252 func provider( _: CXProvider , didDeactivate audioSession: AVAudioSession ) {
21552253 _siprixModule. writeLog ( " CxProvider: didDeactivate " )
2254+ _audioSessionConfigured = false // Reset flag when deactivated
21562255 _siprixModule. deactivate ( audioSession)
21572256 }
21582257
0 commit comments