@qusaieilouti99/call-manager 0.1.78 → 0.1.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CallManager.podspec +5 -0
  2. package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallManager.kt +11 -0
  3. package/ios/AudioManager.swift +281 -0
  4. package/ios/CallEngine.swift +609 -0
  5. package/ios/CallInfo.swift +70 -0
  6. package/ios/CallKitManager.swift +268 -0
  7. package/ios/CallManager.swift +142 -3
  8. package/ios/VoIPTokenManager.swift +167 -0
  9. package/lib/module/CallManager.nitro.js.map +1 -1
  10. package/lib/typescript/src/CallManager.nitro.d.ts +2 -0
  11. package/lib/typescript/src/CallManager.nitro.d.ts.map +1 -1
  12. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +75 -0
  13. package/nitrogen/generated/android/c++/JHybridCallManagerSpec.cpp +20 -0
  14. package/nitrogen/generated/android/c++/JHybridCallManagerSpec.hpp +2 -0
  15. package/nitrogen/generated/android/kotlin/com/margelo/nitro/qusaieilouti99/callmanager/Func_void_std__string.kt +80 -0
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/qusaieilouti99/callmanager/HybridCallManagerSpec.kt +13 -0
  17. package/nitrogen/generated/android/qusaieilouti99_callmanagerOnLoad.cpp +2 -0
  18. package/nitrogen/generated/ios/CallManager-Swift-Cxx-Bridge.cpp +8 -0
  19. package/nitrogen/generated/ios/CallManager-Swift-Cxx-Bridge.hpp +22 -0
  20. package/nitrogen/generated/ios/c++/HybridCallManagerSpecSwift.hpp +14 -0
  21. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  22. package/nitrogen/generated/ios/swift/HybridCallManagerSpec.swift +2 -0
  23. package/nitrogen/generated/ios/swift/HybridCallManagerSpec_cxx.swift +31 -0
  24. package/nitrogen/generated/shared/c++/HybridCallManagerSpec.cpp +2 -0
  25. package/nitrogen/generated/shared/c++/HybridCallManagerSpec.hpp +2 -0
  26. package/package.json +1 -1
  27. package/src/CallManager.nitro.ts +8 -0
@@ -20,6 +20,11 @@ Pod::Spec.new do |s|
20
20
  "cpp/**/*.{hpp,cpp}",
21
21
  ]
22
22
 
23
+ s.pod_target_xcconfig = {
24
+ # C++ compiler flags, mainly for folly.
25
+ "GCC_PREPROCESSOR_DEFINITIONS" => "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES"
26
+ }
27
+
23
28
  s.dependency 'React-jsi'
24
29
  s.dependency 'React-callinvoker'
25
30
 
@@ -97,4 +97,15 @@ class CallManager : HybridCallManagerSpec() {
97
97
  ensureInitialized()
98
98
  CallEngine.setMuted(callId, muted)
99
99
  }
100
+
101
+ override fun updateDisplayCallInformation(callId: String, callerName: String): Unit {
102
+ // do nothing for now
103
+ }
104
+
105
+ override fun registerVoIPTokenListener(listener: (payload: String) -> Unit): () -> Unit
106
+ Log.d(TAG, "registerVoIPTokenListener called")
107
+ return {
108
+ Log.d(TAG, "registerVoIPTokenListener removed.")
109
+ }
110
+ }
100
111
  }
@@ -0,0 +1,281 @@
1
+ import Foundation
2
+ import AVFoundation
3
+ import OSLog
4
+
5
+ protocol AudioManagerDelegate: AnyObject {
6
+ func audioManager(_ manager: AudioManager, didChangeRoute routeInfo: AudioRoutesInfo)
7
+ func audioManager(_ manager: AudioManager, didChangeDevices routeInfo: AudioRoutesInfo)
8
+ }
9
+
10
+ class AudioManager {
11
+ private let logger = Logger(subsystem: "com.qusaieilouti99.callmanager", category: "AudioManager")
12
+ private weak var delegate: AudioManagerDelegate?
13
+ private var audioSession: AVAudioSession
14
+ private var lastRouteInfo: AudioRoutesInfo?
15
+ private var notificationObservers: [NSObjectProtocol] = []
16
+
17
+ init(delegate: AudioManagerDelegate) {
18
+ self.delegate = delegate
19
+ self.audioSession = AVAudioSession.sharedInstance()
20
+
21
+ logger.info("🔊 AudioManager initializing...")
22
+ setupNotifications()
23
+ logger.info("🔊 ✅ AudioManager initialized successfully")
24
+ }
25
+
26
+ deinit {
27
+ logger.info("🔊 AudioManager deinitializing...")
28
+ for observer in notificationObservers {
29
+ NotificationCenter.default.removeObserver(observer)
30
+ }
31
+ notificationObservers.removeAll()
32
+ }
33
+
34
+ private func setupNotifications() {
35
+ logger.info("🔊 Setting up audio session notifications...")
36
+
37
+ let routeChangeObserver = NotificationCenter.default.addObserver(
38
+ forName: AVAudioSession.routeChangeNotification,
39
+ object: nil,
40
+ queue: nil
41
+ ) { [weak self] notification in
42
+ self?.handleAudioRouteChanged(notification: notification)
43
+ }
44
+ notificationObservers.append(routeChangeObserver)
45
+
46
+ let interruptionObserver = NotificationCenter.default.addObserver(
47
+ forName: AVAudioSession.interruptionNotification,
48
+ object: nil,
49
+ queue: nil
50
+ ) { [weak self] notification in
51
+ self?.handleAudioSessionInterrupted(notification: notification)
52
+ }
53
+ notificationObservers.append(interruptionObserver)
54
+
55
+ logger.info("🔊 ✅ Audio session notifications setup completed")
56
+ }
57
+
58
+ func configureForIncomingCall() {
59
+ logger.info("🔊 Configuring audio session for incoming call...")
60
+
61
+ do {
62
+ try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: [.allowBluetooth, .allowBluetoothA2DP])
63
+ try audioSession.setActive(true)
64
+ logger.info("🔊 ✅ Audio session configured for incoming call")
65
+ } catch {
66
+ logger.error("🔊 ❌ Failed to configure audio session for incoming call: \(error.localizedDescription)")
67
+ }
68
+ }
69
+
70
+ func configureForOutgoingCall(isVideo: Bool) {
71
+ logger.info("🔊 Configuring audio session for outgoing call (video: \(isVideo))...")
72
+
73
+ do {
74
+ let options: AVAudioSession.CategoryOptions = isVideo ?
75
+ [.allowBluetooth, .allowBluetoothA2DP, .defaultToSpeaker] :
76
+ [.allowBluetooth, .allowBluetoothA2DP]
77
+
78
+ try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: options)
79
+ try audioSession.setActive(true)
80
+ logger.info("🔊 ✅ Audio session configured for outgoing call (video: \(isVideo))")
81
+ } catch {
82
+ logger.error("🔊 ❌ Failed to configure audio session for outgoing call: \(error.localizedDescription)")
83
+ }
84
+ }
85
+
86
+ func configureForActiveCall(isVideo: Bool) {
87
+ logger.info("🔊 Configuring audio session for active call (video: \(isVideo))...")
88
+
89
+ do {
90
+ let options: AVAudioSession.CategoryOptions = isVideo ?
91
+ [.allowBluetooth, .allowBluetoothA2DP, .defaultToSpeaker] :
92
+ [.allowBluetooth, .allowBluetoothA2DP]
93
+
94
+ try audioSession.setCategory(.playAndRecord, mode: .voiceChat, options: options)
95
+ try audioSession.setActive(true)
96
+ logger.info("🔊 ✅ Audio session configured for active call (video: \(isVideo))")
97
+ } catch {
98
+ logger.error("🔊 ❌ Failed to configure audio session for active call: \(error.localizedDescription)")
99
+ }
100
+ }
101
+
102
+ func getAudioDevices() -> AudioRoutesInfo {
103
+ logger.debug("🔊 Getting available audio devices...")
104
+
105
+ let currentRoute = audioSession.currentRoute
106
+ var devices: [String] = ["Earpiece", "Speaker"]
107
+
108
+ logger.debug("🔊 Current route inputs: \(currentRoute.inputs.map { $0.portType.rawValue })")
109
+ logger.debug("🔊 Current route outputs: \(currentRoute.outputs.map { $0.portType.rawValue })")
110
+
111
+ if let availableInputs = audioSession.availableInputs {
112
+ logger.debug("🔊 Available inputs: \(availableInputs.map { $0.portType.rawValue })")
113
+
114
+ for input in availableInputs {
115
+ switch input.portType {
116
+ case .bluetoothHFP, .bluetoothA2DP, .bluetoothLE:
117
+ if !devices.contains("Bluetooth") {
118
+ devices.append("Bluetooth")
119
+ logger.debug("🔊 Added Bluetooth device")
120
+ }
121
+ case .headphones, .headsetMic, .wiredHeadphones:
122
+ if !devices.contains("Headset") {
123
+ devices.append("Headset")
124
+ logger.debug("🔊 Added Headset device")
125
+ }
126
+ default:
127
+ logger.debug("🔊 Other input type: \(input.portType.rawValue)")
128
+ break
129
+ }
130
+ }
131
+ }
132
+
133
+ let currentRouteString = getCurrentAudioRoute()
134
+
135
+ let routeInfo = AudioRoutesInfo(devices: devices, currentRoute: currentRouteString)
136
+ lastRouteInfo = routeInfo
137
+
138
+ logger.info("🔊 Available audio devices: \(devices), current: \(currentRouteString)")
139
+ return routeInfo
140
+ }
141
+
142
+ func setAudioRoute(_ route: String) {
143
+ logger.info("🔊 Setting audio route to: \(route)")
144
+
145
+ let previousRoute = getCurrentAudioRoute()
146
+ logger.debug("🔊 Previous route: \(previousRoute)")
147
+
148
+ do {
149
+ switch route {
150
+ case "Speaker":
151
+ logger.debug("🔊 Overriding to speaker...")
152
+ try audioSession.overrideOutputAudioPort(.speaker)
153
+ case "Earpiece":
154
+ logger.debug("🔊 Overriding to earpiece...")
155
+ try audioSession.overrideOutputAudioPort(.none)
156
+ case "Bluetooth":
157
+ logger.debug("🔊 Setting to Bluetooth (system managed)...")
158
+ try audioSession.overrideOutputAudioPort(.none)
159
+ case "Headset":
160
+ logger.debug("🔊 Setting to Headset (system managed)...")
161
+ try audioSession.overrideOutputAudioPort(.none)
162
+ default:
163
+ logger.warning("🔊 ⚠️ Unknown audio route: \(route)")
164
+ return
165
+ }
166
+
167
+ let newRoute = getCurrentAudioRoute()
168
+ logger.info("🔊 Audio route changed: \(previousRoute) → \(newRoute)")
169
+
170
+ if previousRoute != newRoute {
171
+ notifyRouteChange()
172
+ }
173
+ } catch {
174
+ logger.error("🔊 ❌ Failed to set audio route to \(route): \(error.localizedDescription)")
175
+ }
176
+ }
177
+
178
+ func setMuted(_ muted: Bool) {
179
+ logger.info("🔊 Mute state changed to: \(muted)")
180
+ }
181
+
182
+ func cleanup() {
183
+ logger.info("🔊 Cleaning up audio session...")
184
+
185
+ do {
186
+ try audioSession.setActive(false, options: .notifyOthersOnDeactivation)
187
+ logger.info("🔊 ✅ Audio session deactivated successfully")
188
+ } catch {
189
+ logger.error("🔊 ❌ Failed to deactivate audio session: \(error.localizedDescription)")
190
+ }
191
+ }
192
+
193
+ private func getCurrentAudioRoute() -> String {
194
+ let currentRoute = audioSession.currentRoute
195
+
196
+ for output in currentRoute.outputs {
197
+ let routeType = output.portType
198
+ logger.debug("🔊 Checking output port: \(routeType.rawValue)")
199
+
200
+ switch routeType {
201
+ case .bluetoothHFP, .bluetoothA2DP, .bluetoothLE:
202
+ return "Bluetooth"
203
+ case .builtInSpeaker:
204
+ return "Speaker"
205
+ case .headphones, .headsetMic, .wiredHeadphones:
206
+ return "Headset"
207
+ case .builtInReceiver:
208
+ return "Earpiece"
209
+ default:
210
+ continue
211
+ }
212
+ }
213
+
214
+ logger.debug("🔊 No specific route found, defaulting to Earpiece")
215
+ return "Earpiece"
216
+ }
217
+
218
+ private func notifyRouteChange() {
219
+ logger.debug("🔊 Notifying delegate about route change...")
220
+ let routeInfo = getAudioDevices()
221
+ self.delegate?.audioManager(self, didChangeRoute: routeInfo)
222
+ }
223
+
224
+ private func notifyDeviceChange() {
225
+ logger.debug("🔊 Notifying delegate about device change...")
226
+ let routeInfo = getAudioDevices()
227
+ self.delegate?.audioManager(self, didChangeDevices: routeInfo)
228
+ }
229
+
230
+ private func handleAudioRouteChanged(notification: Notification) {
231
+ logger.info("🔊 Audio route changed notification received")
232
+
233
+ guard let userInfo = notification.userInfo,
234
+ let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
235
+ let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
236
+ logger.warning("🔊 ⚠️ Could not parse route change reason")
237
+ return
238
+ }
239
+
240
+ logger.info("🔊 Route change reason: \(reason)")
241
+
242
+ switch reason {
243
+ case .newDeviceAvailable, .oldDeviceUnavailable:
244
+ logger.info("🔊 Audio device availability changed: \(reason)")
245
+ notifyDeviceChange()
246
+ case .override, .categoryChange:
247
+ logger.info("🔊 Audio route override or category change: \(reason)")
248
+ notifyRouteChange()
249
+ default:
250
+ logger.info("🔊 Other audio route change reason: \(reason)")
251
+ notifyRouteChange()
252
+ }
253
+ }
254
+
255
+ private func handleAudioSessionInterrupted(notification: Notification) {
256
+ logger.info("🔊 Audio session interrupted notification received")
257
+
258
+ guard let userInfo = notification.userInfo,
259
+ let interruptionTypeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
260
+ let interruptionType = AVAudioSession.InterruptionType(rawValue: interruptionTypeValue) else {
261
+ logger.warning("🔊 ⚠️ Could not parse interruption type")
262
+ return
263
+ }
264
+
265
+ switch interruptionType {
266
+ case .began:
267
+ logger.info("🔊 Audio session interruption began")
268
+ case .ended:
269
+ logger.info("🔊 Audio session interruption ended")
270
+ if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
271
+ let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
272
+ if options.contains(.shouldResume) {
273
+ logger.info("🔊 Should resume audio session")
274
+ }
275
+ }
276
+ @unknown default:
277
+ logger.warning("🔊 ⚠️ Unknown interruption type")
278
+ break
279
+ }
280
+ }
281
+ }