@qusaieilouti99/call-manager 0.1.79 → 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.
- package/CallManager.podspec +5 -0
- package/ios/AudioManager.swift +28 -32
- package/ios/CallEngine.swift +93 -119
- package/ios/CallInfo.swift +7 -3
- package/ios/CallKitManager.swift +83 -53
- package/ios/CallManager.swift +10 -13
- package/ios/VoIPTokenManager.swift +64 -55
- package/package.json +1 -1
package/CallManager.podspec
CHANGED
|
@@ -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
|
|
package/ios/AudioManager.swift
CHANGED
|
@@ -7,16 +7,16 @@ protocol AudioManagerDelegate: AnyObject {
|
|
|
7
7
|
func audioManager(_ manager: AudioManager, didChangeDevices routeInfo: AudioRoutesInfo)
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
class AudioManager
|
|
10
|
+
class AudioManager {
|
|
11
11
|
private let logger = Logger(subsystem: "com.qusaieilouti99.callmanager", category: "AudioManager")
|
|
12
12
|
private weak var delegate: AudioManagerDelegate?
|
|
13
13
|
private var audioSession: AVAudioSession
|
|
14
14
|
private var lastRouteInfo: AudioRoutesInfo?
|
|
15
|
+
private var notificationObservers: [NSObjectProtocol] = []
|
|
15
16
|
|
|
16
17
|
init(delegate: AudioManagerDelegate) {
|
|
17
18
|
self.delegate = delegate
|
|
18
19
|
self.audioSession = AVAudioSession.sharedInstance()
|
|
19
|
-
super.init()
|
|
20
20
|
|
|
21
21
|
logger.info("🔊 AudioManager initializing...")
|
|
22
22
|
setupNotifications()
|
|
@@ -25,31 +25,36 @@ class AudioManager: NSObject {
|
|
|
25
25
|
|
|
26
26
|
deinit {
|
|
27
27
|
logger.info("🔊 AudioManager deinitializing...")
|
|
28
|
-
|
|
28
|
+
for observer in notificationObservers {
|
|
29
|
+
NotificationCenter.default.removeObserver(observer)
|
|
30
|
+
}
|
|
31
|
+
notificationObservers.removeAll()
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
// MARK: - Setup
|
|
32
34
|
private func setupNotifications() {
|
|
33
35
|
logger.info("🔊 Setting up audio session notifications...")
|
|
34
36
|
|
|
35
|
-
NotificationCenter.default.addObserver(
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
object: nil
|
|
47
|
-
|
|
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)
|
|
48
54
|
|
|
49
55
|
logger.info("🔊 ✅ Audio session notifications setup completed")
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
// MARK: - Public Methods
|
|
53
58
|
func configureForIncomingCall() {
|
|
54
59
|
logger.info("🔊 Configuring audio session for incoming call...")
|
|
55
60
|
|
|
@@ -103,7 +108,6 @@ class AudioManager: NSObject {
|
|
|
103
108
|
logger.debug("🔊 Current route inputs: \(currentRoute.inputs.map { $0.portType.rawValue })")
|
|
104
109
|
logger.debug("🔊 Current route outputs: \(currentRoute.outputs.map { $0.portType.rawValue })")
|
|
105
110
|
|
|
106
|
-
// Check for available inputs/outputs
|
|
107
111
|
if let availableInputs = audioSession.availableInputs {
|
|
108
112
|
logger.debug("🔊 Available inputs: \(availableInputs.map { $0.portType.rawValue })")
|
|
109
113
|
|
|
@@ -126,7 +130,6 @@ class AudioManager: NSObject {
|
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
// Determine current route
|
|
130
133
|
let currentRouteString = getCurrentAudioRoute()
|
|
131
134
|
|
|
132
135
|
let routeInfo = AudioRoutesInfo(devices: devices, currentRoute: currentRouteString)
|
|
@@ -153,11 +156,9 @@ class AudioManager: NSObject {
|
|
|
153
156
|
case "Bluetooth":
|
|
154
157
|
logger.debug("🔊 Setting to Bluetooth (system managed)...")
|
|
155
158
|
try audioSession.overrideOutputAudioPort(.none)
|
|
156
|
-
// Bluetooth routing is handled automatically by the system
|
|
157
159
|
case "Headset":
|
|
158
160
|
logger.debug("🔊 Setting to Headset (system managed)...")
|
|
159
161
|
try audioSession.overrideOutputAudioPort(.none)
|
|
160
|
-
// Headset routing is handled automatically by the system
|
|
161
162
|
default:
|
|
162
163
|
logger.warning("🔊 ⚠️ Unknown audio route: \(route)")
|
|
163
164
|
return
|
|
@@ -176,8 +177,6 @@ class AudioManager: NSObject {
|
|
|
176
177
|
|
|
177
178
|
func setMuted(_ muted: Bool) {
|
|
178
179
|
logger.info("🔊 Mute state changed to: \(muted)")
|
|
179
|
-
// Note: Muting is typically handled by CallKit automatically
|
|
180
|
-
// This is here for consistency with Android implementation
|
|
181
180
|
}
|
|
182
181
|
|
|
183
182
|
func cleanup() {
|
|
@@ -191,7 +190,6 @@ class AudioManager: NSObject {
|
|
|
191
190
|
}
|
|
192
191
|
}
|
|
193
192
|
|
|
194
|
-
// MARK: - Private Methods
|
|
195
193
|
private func getCurrentAudioRoute() -> String {
|
|
196
194
|
let currentRoute = audioSession.currentRoute
|
|
197
195
|
|
|
@@ -214,23 +212,22 @@ class AudioManager: NSObject {
|
|
|
214
212
|
}
|
|
215
213
|
|
|
216
214
|
logger.debug("🔊 No specific route found, defaulting to Earpiece")
|
|
217
|
-
return "Earpiece"
|
|
215
|
+
return "Earpiece"
|
|
218
216
|
}
|
|
219
217
|
|
|
220
218
|
private func notifyRouteChange() {
|
|
221
219
|
logger.debug("🔊 Notifying delegate about route change...")
|
|
222
220
|
let routeInfo = getAudioDevices()
|
|
223
|
-
delegate?.audioManager(self, didChangeRoute: routeInfo)
|
|
221
|
+
self.delegate?.audioManager(self, didChangeRoute: routeInfo)
|
|
224
222
|
}
|
|
225
223
|
|
|
226
224
|
private func notifyDeviceChange() {
|
|
227
225
|
logger.debug("🔊 Notifying delegate about device change...")
|
|
228
226
|
let routeInfo = getAudioDevices()
|
|
229
|
-
delegate?.audioManager(self, didChangeDevices: routeInfo)
|
|
227
|
+
self.delegate?.audioManager(self, didChangeDevices: routeInfo)
|
|
230
228
|
}
|
|
231
229
|
|
|
232
|
-
|
|
233
|
-
@objc private func audioRouteChanged(notification: Notification) {
|
|
230
|
+
private func handleAudioRouteChanged(notification: Notification) {
|
|
234
231
|
logger.info("🔊 Audio route changed notification received")
|
|
235
232
|
|
|
236
233
|
guard let userInfo = notification.userInfo,
|
|
@@ -255,7 +252,7 @@ class AudioManager: NSObject {
|
|
|
255
252
|
}
|
|
256
253
|
}
|
|
257
254
|
|
|
258
|
-
|
|
255
|
+
private func handleAudioSessionInterrupted(notification: Notification) {
|
|
259
256
|
logger.info("🔊 Audio session interrupted notification received")
|
|
260
257
|
|
|
261
258
|
guard let userInfo = notification.userInfo,
|
|
@@ -274,7 +271,6 @@ class AudioManager: NSObject {
|
|
|
274
271
|
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
|
|
275
272
|
if options.contains(.shouldResume) {
|
|
276
273
|
logger.info("🔊 Should resume audio session")
|
|
277
|
-
// Audio session will be reactivated by CallKit
|
|
278
274
|
}
|
|
279
275
|
}
|
|
280
276
|
@unknown default:
|
package/ios/CallEngine.swift
CHANGED
|
@@ -5,35 +5,28 @@ import UserNotifications
|
|
|
5
5
|
import UIKit
|
|
6
6
|
import OSLog
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
public class CallEngine {
|
|
9
9
|
static let shared = CallEngine()
|
|
10
10
|
|
|
11
|
-
// MARK: - Constants
|
|
12
11
|
private let logger = Logger(subsystem: "com.qusaieilouti99.callmanager", category: "CallEngine")
|
|
13
12
|
private let defaultTimeout: TimeInterval = 60.0
|
|
14
13
|
|
|
15
|
-
// MARK: - Core Components
|
|
16
14
|
private var callKitManager: CallKitManager?
|
|
17
15
|
private var audioManager: AudioManager?
|
|
18
16
|
|
|
19
|
-
// MARK: - State Management
|
|
20
17
|
private var activeCalls: [String: CallInfo] = [:]
|
|
21
18
|
private var callMetadata: [String: String] = [:]
|
|
22
19
|
private var currentCallId: String?
|
|
23
20
|
private var canMakeMultipleCalls: Bool = false
|
|
24
21
|
|
|
25
|
-
// MARK: - Event System
|
|
26
22
|
private var eventHandler: ((CallEventType, String) -> Void)?
|
|
27
23
|
private var cachedEvents: [(CallEventType, String)] = []
|
|
28
24
|
|
|
29
|
-
// MARK: - Internal Call End Listeners (for UI components)
|
|
30
25
|
private var callEndListeners: [(String) -> Void] = []
|
|
31
26
|
|
|
32
|
-
// MARK: - Initialization
|
|
33
27
|
private var isInitialized: Bool = false
|
|
34
28
|
|
|
35
|
-
private
|
|
36
|
-
super.init()
|
|
29
|
+
private init() {
|
|
37
30
|
logger.info("🚀 CallEngine singleton created")
|
|
38
31
|
}
|
|
39
32
|
|
|
@@ -52,7 +45,6 @@ import OSLog
|
|
|
52
45
|
logger.info("🚀 ✅ CallEngine initialized successfully")
|
|
53
46
|
}
|
|
54
47
|
|
|
55
|
-
// MARK: - Public API
|
|
56
48
|
public func setCanMakeMultipleCalls(_ allow: Bool) {
|
|
57
49
|
canMakeMultipleCalls = allow
|
|
58
50
|
logger.info("🚀 canMakeMultipleCalls set to: \(allow)")
|
|
@@ -60,7 +52,7 @@ import OSLog
|
|
|
60
52
|
|
|
61
53
|
public func getCurrentCallState() -> String {
|
|
62
54
|
logger.debug("🚀 Getting current call state...")
|
|
63
|
-
let callsArray = activeCalls.values.map { $0.toJSONObject() }
|
|
55
|
+
let callsArray = self.activeCalls.values.map { $0.toJSONObject() }
|
|
64
56
|
do {
|
|
65
57
|
let jsonData = try JSONSerialization.data(withJSONObject: callsArray, options: [])
|
|
66
58
|
let result = String(data: jsonData, encoding: .utf8) ?? "[]"
|
|
@@ -72,7 +64,6 @@ import OSLog
|
|
|
72
64
|
}
|
|
73
65
|
}
|
|
74
66
|
|
|
75
|
-
// MARK: - Incoming Call Management
|
|
76
67
|
public func reportIncomingCall(
|
|
77
68
|
callId: String,
|
|
78
69
|
callType: String,
|
|
@@ -83,29 +74,26 @@ import OSLog
|
|
|
83
74
|
logger.info("📞 Reporting incoming call: callId=\(callId), type=\(callType), name=\(displayName)")
|
|
84
75
|
|
|
85
76
|
if let metadata = metadata {
|
|
86
|
-
callMetadata[callId] = metadata
|
|
77
|
+
self.callMetadata[callId] = metadata
|
|
87
78
|
logger.info("📞 Metadata stored for call \(callId)")
|
|
88
79
|
}
|
|
89
80
|
|
|
90
|
-
|
|
91
|
-
if let incomingCall = activeCalls.values.first(where: { $0.state == .incoming }),
|
|
81
|
+
if let incomingCall = self.activeCalls.values.first(where: { $0.state == .incoming }),
|
|
92
82
|
incomingCall.callId != callId {
|
|
93
83
|
logger.warning("📞 ⚠️ Incoming call collision detected. Auto-rejecting new call: \(callId)")
|
|
94
84
|
rejectIncomingCallCollision(callId: callId, reason: "Another call is already incoming")
|
|
95
85
|
return
|
|
96
86
|
}
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
if let activeCall = activeCalls.values.first(where: { $0.state == .active || $0.state == .held }),
|
|
88
|
+
if let activeCall = self.activeCalls.values.first(where: { $0.state == .active || $0.state == .held }),
|
|
100
89
|
!canMakeMultipleCalls {
|
|
101
90
|
logger.warning("📞 ⚠️ Active call exists when receiving incoming call. Auto-rejecting: \(callId)")
|
|
102
91
|
rejectIncomingCallCollision(callId: callId, reason: "Another call is already active")
|
|
103
92
|
return
|
|
104
93
|
}
|
|
105
94
|
|
|
106
|
-
// Hold existing active calls if multiple calls not allowed
|
|
107
95
|
if !canMakeMultipleCalls {
|
|
108
|
-
for call in activeCalls.values where call.state == .active {
|
|
96
|
+
for call in self.activeCalls.values where call.state == .active {
|
|
109
97
|
logger.info("📞 Holding existing active call: \(call.callId)")
|
|
110
98
|
holdCallInternal(callId: call.callId, heldBySystem: false)
|
|
111
99
|
}
|
|
@@ -119,22 +107,22 @@ import OSLog
|
|
|
119
107
|
state: .incoming
|
|
120
108
|
)
|
|
121
109
|
|
|
122
|
-
activeCalls[callId] = callInfo
|
|
123
|
-
currentCallId = callId
|
|
124
|
-
logger.info("📞 Call added to active calls. Total: \(activeCalls.count)")
|
|
110
|
+
self.activeCalls[callId] = callInfo
|
|
111
|
+
self.currentCallId = callId
|
|
112
|
+
logger.info("📞 Call added to active calls. Total: \(self.activeCalls.count)")
|
|
125
113
|
|
|
126
|
-
callKitManager?.reportIncomingCall(callInfo: callInfo) { [weak self] error in
|
|
114
|
+
self.callKitManager?.reportIncomingCall(callInfo: callInfo) { [weak self] error in
|
|
115
|
+
guard let self = self else { return }
|
|
127
116
|
if let error = error {
|
|
128
|
-
self
|
|
129
|
-
self
|
|
117
|
+
self.logger.error("📞 ❌ Failed to report incoming call: \(error.localizedDescription)")
|
|
118
|
+
self.endCallInternal(callId: callId)
|
|
130
119
|
} else {
|
|
131
|
-
self
|
|
132
|
-
self
|
|
120
|
+
self.logger.info("📞 ✅ Successfully reported incoming call for \(callId)")
|
|
121
|
+
self.audioManager?.configureForIncomingCall()
|
|
133
122
|
}
|
|
134
123
|
}
|
|
135
124
|
}
|
|
136
125
|
|
|
137
|
-
// MARK: - Outgoing Call Management
|
|
138
126
|
public func startOutgoingCall(
|
|
139
127
|
callId: String,
|
|
140
128
|
callType: String,
|
|
@@ -144,7 +132,7 @@ import OSLog
|
|
|
144
132
|
logger.info("📞 Starting outgoing call: callId=\(callId), type=\(callType), target=\(targetName)")
|
|
145
133
|
|
|
146
134
|
if let metadata = metadata {
|
|
147
|
-
callMetadata[callId] = metadata
|
|
135
|
+
self.callMetadata[callId] = metadata
|
|
148
136
|
logger.info("📞 Metadata stored for call \(callId)")
|
|
149
137
|
}
|
|
150
138
|
|
|
@@ -157,9 +145,8 @@ import OSLog
|
|
|
157
145
|
return
|
|
158
146
|
}
|
|
159
147
|
|
|
160
|
-
// Hold existing active calls if multiple calls not allowed
|
|
161
148
|
if !canMakeMultipleCalls {
|
|
162
|
-
for call in activeCalls.values where call.state == .active {
|
|
149
|
+
for call in self.activeCalls.values where call.state == .active {
|
|
163
150
|
logger.info("📞 Holding existing active call: \(call.callId)")
|
|
164
151
|
holdCallInternal(callId: call.callId, heldBySystem: false)
|
|
165
152
|
}
|
|
@@ -173,17 +160,18 @@ import OSLog
|
|
|
173
160
|
state: .dialing
|
|
174
161
|
)
|
|
175
162
|
|
|
176
|
-
activeCalls[callId] = callInfo
|
|
177
|
-
currentCallId = callId
|
|
178
|
-
logger.info("📞 Call added to active calls. Total: \(activeCalls.count)")
|
|
163
|
+
self.activeCalls[callId] = callInfo
|
|
164
|
+
self.currentCallId = callId
|
|
165
|
+
logger.info("📞 Call added to active calls. Total: \(self.activeCalls.count)")
|
|
179
166
|
|
|
180
|
-
callKitManager?.startOutgoingCall(callInfo: callInfo) { [weak self] error in
|
|
167
|
+
self.callKitManager?.startOutgoingCall(callInfo: callInfo) { [weak self] error in
|
|
168
|
+
guard let self = self else { return }
|
|
181
169
|
if let error = error {
|
|
182
|
-
self
|
|
183
|
-
self
|
|
170
|
+
self.logger.error("📞 ❌ Failed to start outgoing call: \(error.localizedDescription)")
|
|
171
|
+
self.endCallInternal(callId: callId)
|
|
184
172
|
} else {
|
|
185
|
-
self
|
|
186
|
-
self
|
|
173
|
+
self.logger.info("📞 ✅ Successfully started outgoing call for \(callId)")
|
|
174
|
+
self.audioManager?.configureForOutgoingCall(isVideo: callType == "Video")
|
|
187
175
|
}
|
|
188
176
|
}
|
|
189
177
|
}
|
|
@@ -197,18 +185,17 @@ import OSLog
|
|
|
197
185
|
logger.info("📞 Starting call (direct active): callId=\(callId), type=\(callType), target=\(targetName)")
|
|
198
186
|
|
|
199
187
|
if let metadata = metadata {
|
|
200
|
-
callMetadata[callId] = metadata
|
|
188
|
+
self.callMetadata[callId] = metadata
|
|
201
189
|
logger.info("📞 Metadata stored for call \(callId)")
|
|
202
190
|
}
|
|
203
191
|
|
|
204
|
-
if activeCalls.keys.contains(callId) {
|
|
192
|
+
if self.activeCalls.keys.contains(callId) {
|
|
205
193
|
logger.warning("📞 ⚠️ Call \(callId) already exists, cannot start again")
|
|
206
194
|
return
|
|
207
195
|
}
|
|
208
196
|
|
|
209
|
-
// Hold existing active calls if multiple calls not allowed
|
|
210
197
|
if !canMakeMultipleCalls {
|
|
211
|
-
for call in activeCalls.values where call.state == .active {
|
|
198
|
+
for call in self.activeCalls.values where call.state == .active {
|
|
212
199
|
logger.info("📞 Holding existing active call: \(call.callId)")
|
|
213
200
|
holdCallInternal(callId: call.callId, heldBySystem: false)
|
|
214
201
|
}
|
|
@@ -222,17 +209,16 @@ import OSLog
|
|
|
222
209
|
state: .active
|
|
223
210
|
)
|
|
224
211
|
|
|
225
|
-
activeCalls[callId] = callInfo
|
|
226
|
-
currentCallId = callId
|
|
227
|
-
logger.info("📞 Call added as ACTIVE. Total: \(activeCalls.count)")
|
|
212
|
+
self.activeCalls[callId] = callInfo
|
|
213
|
+
self.currentCallId = callId
|
|
214
|
+
logger.info("📞 Call added as ACTIVE. Total: \(self.activeCalls.count)")
|
|
228
215
|
|
|
229
|
-
audioManager?.configureForActiveCall(isVideo: callType == "Video")
|
|
216
|
+
self.audioManager?.configureForActiveCall(isVideo: callType == "Video")
|
|
230
217
|
emitOutgoingCallAnsweredWithMetadata(callId: callId)
|
|
231
218
|
|
|
232
219
|
logger.info("📞 ✅ Call \(callId) started as ACTIVE")
|
|
233
220
|
}
|
|
234
221
|
|
|
235
|
-
// MARK: - Call Answer Management
|
|
236
222
|
public func callAnsweredFromJS(callId: String) {
|
|
237
223
|
logger.info("📞 Remote party answered: \(callId)")
|
|
238
224
|
coreCallAnswered(callId: callId, isLocalAnswer: false)
|
|
@@ -246,23 +232,22 @@ import OSLog
|
|
|
246
232
|
private func coreCallAnswered(callId: String, isLocalAnswer: Bool) {
|
|
247
233
|
logger.info("📞 Core call answered: callId=\(callId), isLocalAnswer=\(isLocalAnswer)")
|
|
248
234
|
|
|
249
|
-
guard var callInfo = activeCalls[callId] else {
|
|
235
|
+
guard var callInfo = self.activeCalls[callId] else {
|
|
250
236
|
logger.warning("📞 ⚠️ Cannot answer call \(callId) - not found in active calls")
|
|
251
237
|
return
|
|
252
238
|
}
|
|
253
239
|
|
|
254
240
|
let previousState = callInfo.state
|
|
255
241
|
callInfo.updateState(.active)
|
|
256
|
-
activeCalls[callId] = callInfo
|
|
257
|
-
currentCallId = callId
|
|
242
|
+
self.activeCalls[callId] = callInfo
|
|
243
|
+
self.currentCallId = callId
|
|
258
244
|
|
|
259
|
-
logger.info("📞 Call state updated: \(previousState.
|
|
245
|
+
logger.info("📞 Call state updated: \(previousState.stringValue) → \(CallState.active.stringValue)")
|
|
260
246
|
|
|
261
|
-
audioManager?.configureForActiveCall(isVideo: callInfo.callType == "Video")
|
|
247
|
+
self.audioManager?.configureForActiveCall(isVideo: callInfo.callType == "Video")
|
|
262
248
|
|
|
263
|
-
// Hold other active calls if multiple calls not allowed
|
|
264
249
|
if !canMakeMultipleCalls {
|
|
265
|
-
for call in activeCalls.values where call.callId != callId && call.state == .active {
|
|
250
|
+
for call in self.activeCalls.values where call.callId != callId && call.state == .active {
|
|
266
251
|
logger.info("📞 Holding other active call: \(call.callId)")
|
|
267
252
|
holdCallInternal(callId: call.callId, heldBySystem: false)
|
|
268
253
|
}
|
|
@@ -277,11 +262,10 @@ import OSLog
|
|
|
277
262
|
logger.info("📞 ✅ Call \(callId) successfully answered")
|
|
278
263
|
}
|
|
279
264
|
|
|
280
|
-
// MARK: - Call Control Methods
|
|
281
265
|
public func setOnHold(callId: String, onHold: Bool) {
|
|
282
266
|
logger.info("📞 Setting hold state: callId=\(callId), onHold=\(onHold)")
|
|
283
267
|
|
|
284
|
-
guard let callInfo = activeCalls[callId] else {
|
|
268
|
+
guard let callInfo = self.activeCalls[callId] else {
|
|
285
269
|
logger.warning("📞 ⚠️ Cannot set hold state for call \(callId) - not found")
|
|
286
270
|
return
|
|
287
271
|
}
|
|
@@ -291,23 +275,23 @@ import OSLog
|
|
|
291
275
|
} else if !onHold && callInfo.state == .held {
|
|
292
276
|
unholdCallInternal(callId: callId, resumedBySystem: false)
|
|
293
277
|
} else {
|
|
294
|
-
logger.warning("📞 ⚠️ Invalid hold operation: call \(callId) is in state \(callInfo.state.
|
|
278
|
+
logger.warning("📞 ⚠️ Invalid hold operation: call \(callId) is in state \(callInfo.state.stringValue)")
|
|
295
279
|
}
|
|
296
280
|
}
|
|
297
281
|
|
|
298
282
|
private func holdCallInternal(callId: String, heldBySystem: Bool) {
|
|
299
283
|
logger.info("📞 Holding call internally: callId=\(callId), heldBySystem=\(heldBySystem)")
|
|
300
284
|
|
|
301
|
-
guard var callInfo = activeCalls[callId], callInfo.state == .active else {
|
|
285
|
+
guard var callInfo = self.activeCalls[callId], callInfo.state == .active else {
|
|
302
286
|
logger.warning("📞 ⚠️ Cannot hold call \(callId) - not in active state")
|
|
303
287
|
return
|
|
304
288
|
}
|
|
305
289
|
|
|
306
290
|
callInfo.updateState(.held)
|
|
307
291
|
callInfo.wasHeldBySystem = heldBySystem
|
|
308
|
-
activeCalls[callId] = callInfo
|
|
292
|
+
self.activeCalls[callId] = callInfo
|
|
309
293
|
|
|
310
|
-
callKitManager?.setCallOnHold(callId: callId, onHold: true)
|
|
294
|
+
self.callKitManager?.setCallOnHold(callId: callId, onHold: true)
|
|
311
295
|
emitEvent(.callHeld, data: ["callId": callId])
|
|
312
296
|
|
|
313
297
|
logger.info("📞 ✅ Call \(callId) held successfully")
|
|
@@ -320,16 +304,16 @@ import OSLog
|
|
|
320
304
|
private func unholdCallInternal(callId: String, resumedBySystem: Bool) {
|
|
321
305
|
logger.info("📞 Unholding call internally: callId=\(callId), resumedBySystem=\(resumedBySystem)")
|
|
322
306
|
|
|
323
|
-
guard var callInfo = activeCalls[callId], callInfo.state == .held else {
|
|
307
|
+
guard var callInfo = self.activeCalls[callId], callInfo.state == .held else {
|
|
324
308
|
logger.warning("📞 ⚠️ Cannot unhold call \(callId) - not in held state")
|
|
325
309
|
return
|
|
326
310
|
}
|
|
327
311
|
|
|
328
312
|
callInfo.updateState(.active)
|
|
329
313
|
callInfo.wasHeldBySystem = false
|
|
330
|
-
activeCalls[callId] = callInfo
|
|
314
|
+
self.activeCalls[callId] = callInfo
|
|
331
315
|
|
|
332
|
-
callKitManager?.setCallOnHold(callId: callId, onHold: false)
|
|
316
|
+
self.callKitManager?.setCallOnHold(callId: callId, onHold: false)
|
|
333
317
|
emitEvent(.callUnheld, data: ["callId": callId])
|
|
334
318
|
|
|
335
319
|
logger.info("📞 ✅ Call \(callId) unheld successfully")
|
|
@@ -338,73 +322,70 @@ import OSLog
|
|
|
338
322
|
public func setMuted(callId: String, muted: Bool) {
|
|
339
323
|
logger.info("📞 Setting mute state: callId=\(callId), muted=\(muted)")
|
|
340
324
|
|
|
341
|
-
guard activeCalls[callId] != nil else {
|
|
325
|
+
guard self.activeCalls[callId] != nil else {
|
|
342
326
|
logger.warning("📞 ⚠️ Cannot set mute state for call \(callId) - not found")
|
|
343
327
|
return
|
|
344
328
|
}
|
|
345
329
|
|
|
346
|
-
audioManager?.setMuted(muted)
|
|
330
|
+
self.audioManager?.setMuted(muted)
|
|
347
331
|
let eventType: CallEventType = muted ? .callMuted : .callUnmuted
|
|
348
332
|
emitEvent(eventType, data: ["callId": callId])
|
|
349
333
|
logger.info("📞 ✅ Call \(callId) mute state changed to: \(muted)")
|
|
350
334
|
}
|
|
351
335
|
|
|
352
|
-
// MARK: - Call End Management
|
|
353
336
|
public func endCall(callId: String) {
|
|
354
337
|
logger.info("📞 Ending call: \(callId)")
|
|
355
338
|
endCallInternal(callId: callId)
|
|
356
339
|
}
|
|
357
340
|
|
|
358
341
|
public func endAllCalls() {
|
|
359
|
-
logger.info("📞 Ending all calls. Current active calls: \(activeCalls.count)")
|
|
342
|
+
logger.info("📞 Ending all calls. Current active calls: \(self.activeCalls.count)")
|
|
360
343
|
|
|
361
|
-
let callIds = Array(activeCalls.keys)
|
|
344
|
+
let callIds = Array(self.activeCalls.keys)
|
|
362
345
|
for callId in callIds {
|
|
363
346
|
logger.info("📞 Ending call: \(callId)")
|
|
364
347
|
endCallInternal(callId: callId)
|
|
365
348
|
}
|
|
366
349
|
|
|
367
|
-
activeCalls.removeAll()
|
|
368
|
-
callMetadata.removeAll()
|
|
369
|
-
currentCallId = nil
|
|
350
|
+
self.activeCalls.removeAll()
|
|
351
|
+
self.callMetadata.removeAll()
|
|
352
|
+
self.currentCallId = nil
|
|
370
353
|
|
|
371
|
-
audioManager?.cleanup()
|
|
354
|
+
self.audioManager?.cleanup()
|
|
372
355
|
logger.info("📞 ✅ All calls ended and cleanup completed")
|
|
373
356
|
}
|
|
374
357
|
|
|
375
358
|
private func endCallInternal(callId: String) {
|
|
376
359
|
logger.info("📞 Ending call internally: \(callId)")
|
|
377
360
|
|
|
378
|
-
guard var callInfo = activeCalls[callId] else {
|
|
361
|
+
guard var callInfo = self.activeCalls[callId] else {
|
|
379
362
|
logger.warning("📞 ⚠️ Call \(callId) not found in active calls")
|
|
380
363
|
return
|
|
381
364
|
}
|
|
382
365
|
|
|
383
|
-
let metadata = callMetadata.removeValue(forKey: callId)
|
|
366
|
+
let metadata = self.callMetadata.removeValue(forKey: callId)
|
|
384
367
|
callInfo.updateState(.ended)
|
|
385
|
-
activeCalls.removeValue(forKey: callId)
|
|
368
|
+
self.activeCalls.removeValue(forKey: callId)
|
|
386
369
|
|
|
387
|
-
if currentCallId == callId {
|
|
388
|
-
currentCallId = activeCalls.values.first { $0.state != .ended }?.callId
|
|
389
|
-
logger.info("📞 Current call ID updated to: \(currentCallId ?? "nil")")
|
|
370
|
+
if self.currentCallId == callId {
|
|
371
|
+
self.currentCallId = self.activeCalls.values.first { $0.state != .ended }?.callId
|
|
372
|
+
logger.info("📞 Current call ID updated to: \(self.currentCallId ?? "nil")")
|
|
390
373
|
}
|
|
391
374
|
|
|
392
|
-
callKitManager?.endCall(callId: callId)
|
|
375
|
+
self.callKitManager?.endCall(callId: callId)
|
|
393
376
|
|
|
394
|
-
if activeCalls.isEmpty {
|
|
377
|
+
if self.activeCalls.isEmpty {
|
|
395
378
|
logger.info("📞 No more active calls, cleaning up audio")
|
|
396
|
-
audioManager?.cleanup()
|
|
379
|
+
self.audioManager?.cleanup()
|
|
397
380
|
}
|
|
398
381
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
for listener in callEndListeners {
|
|
382
|
+
logger.info("📞 Notifying \(self.callEndListeners.count) internal call end listeners")
|
|
383
|
+
for listener in self.callEndListeners {
|
|
402
384
|
DispatchQueue.main.async {
|
|
403
385
|
listener(callId)
|
|
404
386
|
}
|
|
405
387
|
}
|
|
406
388
|
|
|
407
|
-
// Emit end event with metadata
|
|
408
389
|
var eventData: [String: Any] = ["callId": callId]
|
|
409
390
|
if let metadata = metadata {
|
|
410
391
|
eventData["metadata"] = metadata
|
|
@@ -412,50 +393,47 @@ import OSLog
|
|
|
412
393
|
}
|
|
413
394
|
emitEvent(.callEnded, data: eventData)
|
|
414
395
|
|
|
415
|
-
logger.info("📞 ✅ Call \(callId) ended successfully. Remaining calls: \(activeCalls.count)")
|
|
396
|
+
logger.info("📞 ✅ Call \(callId) ended successfully. Remaining calls: \(self.activeCalls.count)")
|
|
416
397
|
}
|
|
417
398
|
|
|
418
|
-
// MARK: - Display Information Update
|
|
419
399
|
public func updateDisplayCallInformation(callId: String, callerName: String) {
|
|
420
400
|
logger.info("📲 Updating display info: callId=\(callId), callerName=\(callerName)")
|
|
421
401
|
|
|
422
|
-
guard var callInfo = activeCalls[callId] else {
|
|
402
|
+
guard var callInfo = self.activeCalls[callId] else {
|
|
423
403
|
logger.warning("📲 ⚠️ Cannot update display info for call \(callId) - not found")
|
|
424
404
|
return
|
|
425
405
|
}
|
|
426
406
|
|
|
427
407
|
callInfo.updateDisplayName(callerName)
|
|
428
|
-
activeCalls[callId] = callInfo
|
|
408
|
+
self.activeCalls[callId] = callInfo
|
|
429
409
|
|
|
430
|
-
callKitManager?.updateCall(callId: callId, displayName: callerName)
|
|
410
|
+
self.callKitManager?.updateCall(callId: callId, displayName: callerName)
|
|
431
411
|
logger.info("📲 ✅ Display info updated successfully")
|
|
432
412
|
}
|
|
433
413
|
|
|
434
|
-
// MARK: - Audio Management
|
|
435
414
|
public func getAudioDevices() -> AudioRoutesInfo {
|
|
436
415
|
logger.debug("🔊 Getting audio devices...")
|
|
437
|
-
let result = audioManager?.getAudioDevices() ?? AudioRoutesInfo(devices: [], currentRoute: "Unknown")
|
|
416
|
+
let result = self.audioManager?.getAudioDevices() ?? AudioRoutesInfo(devices: [], currentRoute: "Unknown")
|
|
438
417
|
logger.debug("🔊 Audio devices result: \(result.devices), current: \(result.currentRoute)")
|
|
439
418
|
return result
|
|
440
419
|
}
|
|
441
420
|
|
|
442
421
|
public func setAudioRoute(_ route: String) {
|
|
443
422
|
logger.info("🔊 Setting audio route: \(route)")
|
|
444
|
-
audioManager?.setAudioRoute(route)
|
|
423
|
+
self.audioManager?.setAudioRoute(route)
|
|
445
424
|
}
|
|
446
425
|
|
|
447
|
-
// MARK: - Event System
|
|
448
426
|
public func setEventHandler(_ handler: ((CallEventType, String) -> Void)?) {
|
|
449
427
|
logger.info("📡 Setting event handler. Handler present: \(handler != nil)")
|
|
450
|
-
eventHandler = handler
|
|
428
|
+
self.eventHandler = handler
|
|
451
429
|
|
|
452
|
-
if let handler = handler, !cachedEvents.isEmpty {
|
|
453
|
-
logger.info("📡 Emitting \(cachedEvents.count) cached events")
|
|
454
|
-
for (type, data) in cachedEvents {
|
|
430
|
+
if let handler = handler, !self.cachedEvents.isEmpty {
|
|
431
|
+
logger.info("📡 Emitting \(self.cachedEvents.count) cached events")
|
|
432
|
+
for (type, data) in self.cachedEvents {
|
|
455
433
|
logger.debug("📡 Emitting cached event: \(type)")
|
|
456
434
|
handler(type, data)
|
|
457
435
|
}
|
|
458
|
-
cachedEvents.removeAll()
|
|
436
|
+
self.cachedEvents.removeAll()
|
|
459
437
|
logger.info("📡 ✅ All cached events emitted and cleared")
|
|
460
438
|
}
|
|
461
439
|
}
|
|
@@ -468,43 +446,41 @@ import OSLog
|
|
|
468
446
|
let jsonData = try JSONSerialization.data(withJSONObject: data, options: [])
|
|
469
447
|
let dataString = String(data: jsonData, encoding: .utf8) ?? "{}"
|
|
470
448
|
|
|
471
|
-
if let eventHandler = eventHandler {
|
|
449
|
+
if let eventHandler = self.eventHandler {
|
|
472
450
|
logger.debug("📡 Calling event handler with data: \(dataString)")
|
|
473
451
|
eventHandler(type, dataString)
|
|
474
452
|
} else {
|
|
475
453
|
logger.info("📡 No event handler, caching event: \(type)")
|
|
476
|
-
cachedEvents.append((type, dataString))
|
|
454
|
+
self.cachedEvents.append((type, dataString))
|
|
477
455
|
}
|
|
478
456
|
} catch {
|
|
479
457
|
logger.error("📡 ❌ Failed to serialize event data: \(error.localizedDescription)")
|
|
480
458
|
}
|
|
481
459
|
}
|
|
482
460
|
|
|
483
|
-
// MARK: - Internal Call End Listeners (for UI cleanup)
|
|
484
461
|
internal func registerCallEndListener(_ listener: @escaping (String) -> Void) {
|
|
485
|
-
callEndListeners.append(listener)
|
|
486
|
-
logger.info("🔗 Internal call end listener registered. Total: \(callEndListeners.count)")
|
|
462
|
+
self.callEndListeners.append(listener)
|
|
463
|
+
logger.info("🔗 Internal call end listener registered. Total: \(self.callEndListeners.count)")
|
|
487
464
|
}
|
|
488
465
|
|
|
489
466
|
internal func unregisterCallEndListener() {
|
|
490
|
-
callEndListeners.removeAll()
|
|
467
|
+
self.callEndListeners.removeAll()
|
|
491
468
|
logger.info("🔗 All internal call end listeners unregistered")
|
|
492
469
|
}
|
|
493
470
|
|
|
494
|
-
// MARK: - Utility Methods
|
|
495
471
|
public func getActiveCalls() -> [CallInfo] {
|
|
496
|
-
let calls = Array(activeCalls.values)
|
|
472
|
+
let calls = Array(self.activeCalls.values)
|
|
497
473
|
logger.debug("📊 Getting active calls. Count: \(calls.count)")
|
|
498
474
|
return calls
|
|
499
475
|
}
|
|
500
476
|
|
|
501
477
|
public func getCurrentCallId() -> String? {
|
|
502
|
-
logger.debug("📊 Current call ID: \(currentCallId ?? "nil")")
|
|
503
|
-
return currentCallId
|
|
478
|
+
logger.debug("📊 Current call ID: \(self.currentCallId ?? "nil")")
|
|
479
|
+
return self.currentCallId
|
|
504
480
|
}
|
|
505
481
|
|
|
506
482
|
public func isCallActive() -> Bool {
|
|
507
|
-
let hasActiveCalls = activeCalls.values.contains { call in
|
|
483
|
+
let hasActiveCalls = self.activeCalls.values.contains { call in
|
|
508
484
|
call.state == .active || call.state == .incoming || call.state == .dialing || call.state == .held
|
|
509
485
|
}
|
|
510
486
|
logger.debug("📊 Is call active: \(hasActiveCalls)")
|
|
@@ -512,7 +488,7 @@ import OSLog
|
|
|
512
488
|
}
|
|
513
489
|
|
|
514
490
|
private func validateOutgoingCallRequest() -> Bool {
|
|
515
|
-
let hasConflictingCalls = activeCalls.values.contains { call in
|
|
491
|
+
let hasConflictingCalls = self.activeCalls.values.contains { call in
|
|
516
492
|
call.state == .incoming || call.state == .active
|
|
517
493
|
}
|
|
518
494
|
let isValid = !hasConflictingCalls
|
|
@@ -522,7 +498,7 @@ import OSLog
|
|
|
522
498
|
|
|
523
499
|
private func rejectIncomingCallCollision(callId: String, reason: String) {
|
|
524
500
|
logger.warning("📞 ⚠️ Rejecting call collision: \(callId), reason: \(reason)")
|
|
525
|
-
callMetadata.removeValue(forKey: callId)
|
|
501
|
+
self.callMetadata.removeValue(forKey: callId)
|
|
526
502
|
emitEvent(.callRejected, data: [
|
|
527
503
|
"callId": callId,
|
|
528
504
|
"reason": reason
|
|
@@ -532,12 +508,12 @@ import OSLog
|
|
|
532
508
|
private func emitCallAnsweredWithMetadata(callId: String) {
|
|
533
509
|
logger.info("📡 Emitting call answered event with metadata: \(callId)")
|
|
534
510
|
|
|
535
|
-
guard let callInfo = activeCalls[callId] else {
|
|
511
|
+
guard let callInfo = self.activeCalls[callId] else {
|
|
536
512
|
logger.warning("📡 ⚠️ Cannot emit call answered - call not found: \(callId)")
|
|
537
513
|
return
|
|
538
514
|
}
|
|
539
515
|
|
|
540
|
-
let metadata = callMetadata[callId]
|
|
516
|
+
let metadata = self.callMetadata[callId]
|
|
541
517
|
|
|
542
518
|
var eventData: [String: Any] = [
|
|
543
519
|
"callId": callId,
|
|
@@ -560,12 +536,12 @@ import OSLog
|
|
|
560
536
|
private func emitOutgoingCallAnsweredWithMetadata(callId: String) {
|
|
561
537
|
logger.info("📡 Emitting outgoing call answered event with metadata: \(callId)")
|
|
562
538
|
|
|
563
|
-
guard let callInfo = activeCalls[callId] else {
|
|
539
|
+
guard let callInfo = self.activeCalls[callId] else {
|
|
564
540
|
logger.warning("📡 ⚠️ Cannot emit outgoing call answered - call not found: \(callId)")
|
|
565
541
|
return
|
|
566
542
|
}
|
|
567
543
|
|
|
568
|
-
let metadata = callMetadata[callId]
|
|
544
|
+
let metadata = self.callMetadata[callId]
|
|
569
545
|
|
|
570
546
|
var eventData: [String: Any] = [
|
|
571
547
|
"callId": callId,
|
|
@@ -586,7 +562,6 @@ import OSLog
|
|
|
586
562
|
}
|
|
587
563
|
}
|
|
588
564
|
|
|
589
|
-
// MARK: - CallKitManagerDelegate
|
|
590
565
|
extension CallEngine: CallKitManagerDelegate {
|
|
591
566
|
func callKitManager(_ manager: CallKitManager, didAnswerCall callId: String) {
|
|
592
567
|
logger.info("📲 CallKit delegate: answer call \(callId)")
|
|
@@ -613,7 +588,6 @@ extension CallEngine: CallKitManagerDelegate {
|
|
|
613
588
|
}
|
|
614
589
|
}
|
|
615
590
|
|
|
616
|
-
// MARK: - AudioManagerDelegate
|
|
617
591
|
extension CallEngine: AudioManagerDelegate {
|
|
618
592
|
func audioManager(_ manager: AudioManager, didChangeRoute routeInfo: AudioRoutesInfo) {
|
|
619
593
|
logger.info("🔊 Audio manager delegate: route changed to \(routeInfo.currentRoute)")
|
package/ios/CallInfo.swift
CHANGED
|
@@ -23,13 +23,13 @@ public struct CallInfo {
|
|
|
23
23
|
self.wasHeldBySystem = false
|
|
24
24
|
self.isManuallySilenced = false
|
|
25
25
|
|
|
26
|
-
Self.logger.info("📱 CallInfo created: callId=\(callId), type=\(callType), name=\(displayName), state=\(state.
|
|
26
|
+
Self.logger.info("📱 CallInfo created: callId=\(callId), type=\(callType), name=\(displayName), state=\(state.stringValue)")
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
mutating func updateState(_ newState: CallState) {
|
|
30
30
|
let oldState = self.state
|
|
31
31
|
self.state = newState
|
|
32
|
-
Self.logger.info("📱 CallInfo state changed: callId=\(callId), \(oldState.
|
|
32
|
+
Self.logger.info("📱 CallInfo state changed: callId=\(callId), \(oldState.stringValue) → \(newState.stringValue)")
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
mutating func updateDisplayName(_ newName: String) {
|
|
@@ -43,7 +43,7 @@ public struct CallInfo {
|
|
|
43
43
|
"callId": callId,
|
|
44
44
|
"callType": callType,
|
|
45
45
|
"displayName": displayName,
|
|
46
|
-
"state": state.
|
|
46
|
+
"state": state.stringValue,
|
|
47
47
|
"timestamp": timestamp,
|
|
48
48
|
"wasHeldBySystem": wasHeldBySystem
|
|
49
49
|
]
|
|
@@ -63,4 +63,8 @@ public enum CallState: String, CaseIterable {
|
|
|
63
63
|
case active = "ACTIVE"
|
|
64
64
|
case held = "HELD"
|
|
65
65
|
case ended = "ENDED"
|
|
66
|
+
|
|
67
|
+
var stringValue: String {
|
|
68
|
+
return self.rawValue
|
|
69
|
+
}
|
|
66
70
|
}
|
package/ios/CallKitManager.swift
CHANGED
|
@@ -9,7 +9,7 @@ protocol CallKitManagerDelegate: AnyObject {
|
|
|
9
9
|
func callKitManager(_ manager: CallKitManager, didSetMuted callId: String, muted: Bool)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
class CallKitManager
|
|
12
|
+
class CallKitManager {
|
|
13
13
|
private let logger = Logger(subsystem: "com.qusaieilouti99.callmanager", category: "CallKitManager")
|
|
14
14
|
private let provider: CXProvider
|
|
15
15
|
private let callController = CXCallController()
|
|
@@ -30,7 +30,6 @@ class CallKitManager: NSObject {
|
|
|
30
30
|
configuration.supportedHandleTypes = [.phoneNumber, .generic]
|
|
31
31
|
configuration.includesCallsInRecents = true
|
|
32
32
|
|
|
33
|
-
// Audio configuration
|
|
34
33
|
if let ringtonePath = Bundle.main.path(forResource: "ringtone", ofType: "caf") {
|
|
35
34
|
configuration.ringtoneSoundURL = URL(fileURLWithPath: ringtonePath)
|
|
36
35
|
logger.info("📲 Custom ringtone configured")
|
|
@@ -39,14 +38,10 @@ class CallKitManager: NSObject {
|
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
provider = CXProvider(configuration: configuration)
|
|
42
|
-
|
|
43
|
-
super.init()
|
|
44
|
-
|
|
45
|
-
provider.setDelegate(self, queue: nil)
|
|
41
|
+
provider.setDelegate(CallKitProviderDelegate(manager: self), queue: nil)
|
|
46
42
|
logger.info("📲 ✅ CallKitManager initialized successfully")
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
// MARK: - Public Methods
|
|
50
45
|
func reportIncomingCall(callInfo: CallInfo, completion: @escaping (Error?) -> Void) {
|
|
51
46
|
logger.info("📲 Reporting incoming call: \(callInfo.callId)")
|
|
52
47
|
|
|
@@ -68,15 +63,16 @@ class CallKitManager: NSObject {
|
|
|
68
63
|
|
|
69
64
|
logger.info("📲 Call update configured: name=\(callInfo.displayName), hasVideo=\(update.hasVideo)")
|
|
70
65
|
|
|
71
|
-
activeCallIds.insert(callInfo.callId)
|
|
72
|
-
logger.info("📲 Added to active calls. Total active: \(activeCallIds.count)")
|
|
66
|
+
self.activeCallIds.insert(callInfo.callId)
|
|
67
|
+
logger.info("📲 Added to active calls. Total active: \(self.activeCallIds.count)")
|
|
73
68
|
|
|
74
69
|
provider.reportNewIncomingCall(with: callUUID, update: update) { [weak self] error in
|
|
70
|
+
guard let self = self else { return }
|
|
75
71
|
if let error = error {
|
|
76
|
-
self
|
|
77
|
-
self
|
|
72
|
+
self.logger.error("📲 ❌ Failed to report incoming call: \(error.localizedDescription)")
|
|
73
|
+
self.activeCallIds.remove(callInfo.callId)
|
|
78
74
|
} else {
|
|
79
|
-
self
|
|
75
|
+
self.logger.info("📲 ✅ Successfully reported incoming call: \(callInfo.callId)")
|
|
80
76
|
}
|
|
81
77
|
completion(error)
|
|
82
78
|
}
|
|
@@ -100,15 +96,16 @@ class CallKitManager: NSObject {
|
|
|
100
96
|
|
|
101
97
|
let transaction = CXTransaction(action: startCallAction)
|
|
102
98
|
|
|
103
|
-
activeCallIds.insert(callInfo.callId)
|
|
104
|
-
logger.info("📲 Added to active calls. Total active: \(activeCallIds.count)")
|
|
99
|
+
self.activeCallIds.insert(callInfo.callId)
|
|
100
|
+
logger.info("📲 Added to active calls. Total active: \(self.activeCallIds.count)")
|
|
105
101
|
|
|
106
102
|
callController.request(transaction) { [weak self] error in
|
|
103
|
+
guard let self = self else { return }
|
|
107
104
|
if let error = error {
|
|
108
|
-
self
|
|
109
|
-
self
|
|
105
|
+
self.logger.error("📲 ❌ Failed to start outgoing call: \(error.localizedDescription)")
|
|
106
|
+
self.activeCallIds.remove(callInfo.callId)
|
|
110
107
|
} else {
|
|
111
|
-
self
|
|
108
|
+
self.logger.info("📲 ✅ Successfully started outgoing call: \(callInfo.callId)")
|
|
112
109
|
}
|
|
113
110
|
completion(error)
|
|
114
111
|
}
|
|
@@ -118,7 +115,7 @@ class CallKitManager: NSObject {
|
|
|
118
115
|
logger.info("📲 Ending call: \(callId)")
|
|
119
116
|
|
|
120
117
|
guard let callUUID = UUID(uuidString: callId),
|
|
121
|
-
activeCallIds.contains(callId) else {
|
|
118
|
+
self.activeCallIds.contains(callId) else {
|
|
122
119
|
logger.warning("📲 ⚠️ Cannot end call \(callId) - not found or invalid UUID")
|
|
123
120
|
return
|
|
124
121
|
}
|
|
@@ -127,11 +124,12 @@ class CallKitManager: NSObject {
|
|
|
127
124
|
let transaction = CXTransaction(action: endCallAction)
|
|
128
125
|
|
|
129
126
|
callController.request(transaction) { [weak self] error in
|
|
127
|
+
guard let self = self else { return }
|
|
130
128
|
if let error = error {
|
|
131
|
-
self
|
|
129
|
+
self.logger.error("📲 ❌ Failed to end call: \(error.localizedDescription)")
|
|
132
130
|
} else {
|
|
133
|
-
self
|
|
134
|
-
self
|
|
131
|
+
self.logger.info("📲 ✅ Successfully ended call: \(callId)")
|
|
132
|
+
self.activeCallIds.remove(callId)
|
|
135
133
|
}
|
|
136
134
|
}
|
|
137
135
|
}
|
|
@@ -140,7 +138,7 @@ class CallKitManager: NSObject {
|
|
|
140
138
|
logger.info("📲 Setting call \(callId) hold state to: \(onHold)")
|
|
141
139
|
|
|
142
140
|
guard let callUUID = UUID(uuidString: callId),
|
|
143
|
-
activeCallIds.contains(callId) else {
|
|
141
|
+
self.activeCallIds.contains(callId) else {
|
|
144
142
|
logger.warning("📲 ⚠️ Cannot set hold for call \(callId) - not found or invalid UUID")
|
|
145
143
|
return
|
|
146
144
|
}
|
|
@@ -149,10 +147,11 @@ class CallKitManager: NSObject {
|
|
|
149
147
|
let transaction = CXTransaction(action: holdAction)
|
|
150
148
|
|
|
151
149
|
callController.request(transaction) { [weak self] error in
|
|
150
|
+
guard let self = self else { return }
|
|
152
151
|
if let error = error {
|
|
153
|
-
self
|
|
152
|
+
self.logger.error("📲 ❌ Failed to set call on hold: \(error.localizedDescription)")
|
|
154
153
|
} else {
|
|
155
|
-
self
|
|
154
|
+
self.logger.info("📲 ✅ Successfully set call \(callId) hold state to: \(onHold)")
|
|
156
155
|
}
|
|
157
156
|
}
|
|
158
157
|
}
|
|
@@ -161,7 +160,7 @@ class CallKitManager: NSObject {
|
|
|
161
160
|
logger.info("📲 Updating call \(callId) display name to: \(displayName)")
|
|
162
161
|
|
|
163
162
|
guard let callUUID = UUID(uuidString: callId),
|
|
164
|
-
activeCallIds.contains(callId) else {
|
|
163
|
+
self.activeCallIds.contains(callId) else {
|
|
165
164
|
logger.warning("📲 ⚠️ Cannot update call \(callId) - not found or invalid UUID")
|
|
166
165
|
return
|
|
167
166
|
}
|
|
@@ -173,66 +172,97 @@ class CallKitManager: NSObject {
|
|
|
173
172
|
provider.reportCall(with: callUUID, updated: update)
|
|
174
173
|
logger.info("📲 ✅ Updated call \(callId) display name to: \(displayName)")
|
|
175
174
|
}
|
|
175
|
+
|
|
176
|
+
fileprivate func handleProviderReset() {
|
|
177
|
+
logger.info("📲 🔄 Provider did reset - clearing all active calls")
|
|
178
|
+
self.activeCallIds.removeAll()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
fileprivate func handleAnswerCall(callId: String) {
|
|
182
|
+
logger.info("📲 📞 Provider perform answer call action")
|
|
183
|
+
logger.info("📲 Answering call: \(callId)")
|
|
184
|
+
self.delegate?.callKitManager(self, didAnswerCall: callId)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
fileprivate func handleEndCall(callId: String) {
|
|
188
|
+
logger.info("📲 📞 Provider perform end call action")
|
|
189
|
+
logger.info("📲 Ending call: \(callId)")
|
|
190
|
+
self.activeCallIds.remove(callId)
|
|
191
|
+
self.delegate?.callKitManager(self, didEndCall: callId)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fileprivate func handleSetHeld(callId: String, onHold: Bool) {
|
|
195
|
+
logger.info("📲 📞 Provider perform set held call action: \(onHold)")
|
|
196
|
+
logger.info("📲 Setting call \(callId) hold state to: \(onHold)")
|
|
197
|
+
self.delegate?.callKitManager(self, didSetHeld: callId, onHold: onHold)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
fileprivate func handleSetMuted(callId: String, muted: Bool) {
|
|
201
|
+
logger.info("📲 📞 Provider perform set muted call action: \(muted)")
|
|
202
|
+
logger.info("📲 Setting call \(callId) mute state to: \(muted)")
|
|
203
|
+
self.delegate?.callKitManager(self, didSetMuted: callId, muted: muted)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fileprivate func handleStartCall() {
|
|
207
|
+
logger.info("📲 📞 Provider perform start call action")
|
|
208
|
+
logger.info("📲 ✅ Start call action fulfilled")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
fileprivate func handleAudioSessionActivated() {
|
|
212
|
+
logger.info("📲 🔊 Provider did activate audio session")
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
fileprivate func handleAudioSessionDeactivated() {
|
|
216
|
+
logger.info("📲 🔊 Provider did deactivate audio session")
|
|
217
|
+
}
|
|
176
218
|
}
|
|
177
219
|
|
|
178
|
-
|
|
179
|
-
|
|
220
|
+
private class CallKitProviderDelegate: NSObject, CXProviderDelegate {
|
|
221
|
+
private weak var manager: CallKitManager?
|
|
222
|
+
|
|
223
|
+
init(manager: CallKitManager) {
|
|
224
|
+
self.manager = manager
|
|
225
|
+
super.init()
|
|
226
|
+
}
|
|
227
|
+
|
|
180
228
|
func providerDidReset(_ provider: CXProvider) {
|
|
181
|
-
|
|
182
|
-
activeCallIds.removeAll()
|
|
229
|
+
manager?.handleProviderReset()
|
|
183
230
|
}
|
|
184
231
|
|
|
185
232
|
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
|
186
|
-
logger.info("📲 📞 Provider perform answer call action")
|
|
187
233
|
let callId = action.callUUID.uuidString
|
|
188
|
-
|
|
189
|
-
delegate?.callKitManager(self, didAnswerCall: callId)
|
|
234
|
+
manager?.handleAnswerCall(callId: callId)
|
|
190
235
|
action.fulfill()
|
|
191
|
-
logger.info("📲 ✅ Answer action fulfilled")
|
|
192
236
|
}
|
|
193
237
|
|
|
194
238
|
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
|
195
|
-
logger.info("📲 📞 Provider perform end call action")
|
|
196
239
|
let callId = action.callUUID.uuidString
|
|
197
|
-
|
|
198
|
-
activeCallIds.remove(callId)
|
|
199
|
-
delegate?.callKitManager(self, didEndCall: callId)
|
|
240
|
+
manager?.handleEndCall(callId: callId)
|
|
200
241
|
action.fulfill()
|
|
201
|
-
logger.info("📲 ✅ End call action fulfilled")
|
|
202
242
|
}
|
|
203
243
|
|
|
204
244
|
func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
|
|
205
|
-
logger.info("📲 📞 Provider perform set held call action: \(action.isOnHold)")
|
|
206
245
|
let callId = action.callUUID.uuidString
|
|
207
|
-
|
|
208
|
-
delegate?.callKitManager(self, didSetHeld: callId, onHold: action.isOnHold)
|
|
246
|
+
manager?.handleSetHeld(callId: callId, onHold: action.isOnHold)
|
|
209
247
|
action.fulfill()
|
|
210
|
-
logger.info("📲 ✅ Set held action fulfilled")
|
|
211
248
|
}
|
|
212
249
|
|
|
213
250
|
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
|
214
|
-
logger.info("📲 📞 Provider perform set muted call action: \(action.isMuted)")
|
|
215
251
|
let callId = action.callUUID.uuidString
|
|
216
|
-
|
|
217
|
-
delegate?.callKitManager(self, didSetMuted: callId, muted: action.isMuted)
|
|
252
|
+
manager?.handleSetMuted(callId: callId, muted: action.isMuted)
|
|
218
253
|
action.fulfill()
|
|
219
|
-
logger.info("📲 ✅ Set muted action fulfilled")
|
|
220
254
|
}
|
|
221
255
|
|
|
222
256
|
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
|
223
|
-
|
|
224
|
-
logger.info("📲 Starting outgoing call with handle: \(action.handle.value)")
|
|
257
|
+
manager?.handleStartCall()
|
|
225
258
|
action.fulfill()
|
|
226
|
-
logger.info("📲 ✅ Start call action fulfilled")
|
|
227
259
|
}
|
|
228
260
|
|
|
229
261
|
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
|
230
|
-
|
|
231
|
-
logger.info("📲 Audio session category: \(audioSession.category.rawValue)")
|
|
232
|
-
logger.info("📲 Audio session mode: \(audioSession.mode.rawValue)")
|
|
262
|
+
manager?.handleAudioSessionActivated()
|
|
233
263
|
}
|
|
234
264
|
|
|
235
265
|
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
|
236
|
-
|
|
266
|
+
manager?.handleAudioSessionDeactivated()
|
|
237
267
|
}
|
|
238
268
|
}
|
package/ios/CallManager.swift
CHANGED
|
@@ -17,7 +17,6 @@ public class CallManager: HybridCallManagerSpec {
|
|
|
17
17
|
CallEngine.shared.initialize()
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
// MARK: - HybridCallManagerSpec Implementation
|
|
21
20
|
public func endCall(callId: String) throws {
|
|
22
21
|
logger.info("🎯📞 endCall requested for callId: \(callId)")
|
|
23
22
|
ensureInitialized()
|
|
@@ -35,7 +34,6 @@ public class CallManager: HybridCallManagerSpec {
|
|
|
35
34
|
public func silenceRingtone() throws {
|
|
36
35
|
logger.info("🎯🔇 silenceRingtone requested")
|
|
37
36
|
ensureInitialized()
|
|
38
|
-
// Ringtone silencing is handled by CallKit automatically in iOS
|
|
39
37
|
logger.info("🎯🔇 ✅ silenceRingtone completed (handled by CallKit)")
|
|
40
38
|
}
|
|
41
39
|
|
|
@@ -57,7 +55,6 @@ public class CallManager: HybridCallManagerSpec {
|
|
|
57
55
|
public func keepScreenAwake(keepAwake: Bool) throws {
|
|
58
56
|
logger.info("🎯💡 keepScreenAwake requested: \(keepAwake)")
|
|
59
57
|
ensureInitialized()
|
|
60
|
-
// Screen wake management is handled by CallKit automatically
|
|
61
58
|
logger.info("🎯💡 ✅ keepScreenAwake completed (handled by CallKit)")
|
|
62
59
|
}
|
|
63
60
|
|
|
@@ -65,17 +62,17 @@ public class CallManager: HybridCallManagerSpec {
|
|
|
65
62
|
logger.info("🎯📡 addListener called")
|
|
66
63
|
ensureInitialized()
|
|
67
64
|
|
|
68
|
-
CallEngine.shared.setEventHandler { eventType, payload in
|
|
69
|
-
self
|
|
65
|
+
CallEngine.shared.setEventHandler { [weak self] eventType, payload in
|
|
66
|
+
self?.logger.debug("🎯📡 Event emitted: \(eventType), payload length: \(payload.count)")
|
|
70
67
|
listener(eventType, payload)
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
logger.info("🎯📡 ✅ Event handler registered")
|
|
74
71
|
|
|
75
|
-
return {
|
|
76
|
-
self
|
|
72
|
+
return { [weak self] in
|
|
73
|
+
self?.logger.info("🎯📡 Removing event handler...")
|
|
77
74
|
CallEngine.shared.setEventHandler(nil)
|
|
78
|
-
self
|
|
75
|
+
self?.logger.info("🎯📡 ✅ Event handler removed")
|
|
79
76
|
}
|
|
80
77
|
}
|
|
81
78
|
|
|
@@ -131,17 +128,17 @@ public class CallManager: HybridCallManagerSpec {
|
|
|
131
128
|
logger.info("🎯🔑 registerVoIPTokenListener called")
|
|
132
129
|
ensureInitialized()
|
|
133
130
|
|
|
134
|
-
VoIPTokenManager.shared.registerTokenListener { token in
|
|
135
|
-
self
|
|
131
|
+
VoIPTokenManager.shared.registerTokenListener { [weak self] token in
|
|
132
|
+
self?.logger.info("🎯🔑 VoIP token received, length: \(token.count)")
|
|
136
133
|
listener(token)
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
logger.info("🎯🔑 ✅ VoIP token listener registered")
|
|
140
137
|
|
|
141
|
-
return {
|
|
142
|
-
self
|
|
138
|
+
return { [weak self] in
|
|
139
|
+
self?.logger.info("🎯🔑 Removing VoIP token listener...")
|
|
143
140
|
VoIPTokenManager.shared.unregisterTokenListener()
|
|
144
|
-
self
|
|
141
|
+
self?.logger.info("🎯🔑 ✅ VoIP token listener removed")
|
|
145
142
|
}
|
|
146
143
|
}
|
|
147
144
|
}
|
|
@@ -2,7 +2,7 @@ import Foundation
|
|
|
2
2
|
import PushKit
|
|
3
3
|
import OSLog
|
|
4
4
|
|
|
5
|
-
class VoIPTokenManager
|
|
5
|
+
class VoIPTokenManager {
|
|
6
6
|
static let shared = VoIPTokenManager()
|
|
7
7
|
|
|
8
8
|
private let logger = Logger(subsystem: "com.qusaieilouti99.callmanager", category: "VoIPTokenManager")
|
|
@@ -10,8 +10,7 @@ class VoIPTokenManager: NSObject {
|
|
|
10
10
|
private var tokenListener: ((String) -> Void)?
|
|
11
11
|
private var cachedToken: String?
|
|
12
12
|
|
|
13
|
-
private
|
|
14
|
-
super.init()
|
|
13
|
+
private init() {
|
|
15
14
|
logger.info("🔑 VoIPTokenManager initializing...")
|
|
16
15
|
setupPushKit()
|
|
17
16
|
}
|
|
@@ -19,17 +18,16 @@ class VoIPTokenManager: NSObject {
|
|
|
19
18
|
private func setupPushKit() {
|
|
20
19
|
logger.info("🔑 Setting up PushKit registry...")
|
|
21
20
|
pushRegistry = PKPushRegistry(queue: DispatchQueue.main)
|
|
22
|
-
pushRegistry?.delegate = self
|
|
21
|
+
pushRegistry?.delegate = VoIPPushDelegate(manager: self)
|
|
23
22
|
pushRegistry?.desiredPushTypes = [.voIP]
|
|
24
23
|
logger.info("🔑 PushKit registry setup completed successfully")
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
func registerTokenListener(_ listener: @escaping (String) -> Void) {
|
|
28
27
|
logger.info("🔑 Registering VoIP token listener...")
|
|
29
|
-
tokenListener = listener
|
|
28
|
+
self.tokenListener = listener
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
if let cachedToken = cachedToken {
|
|
30
|
+
if let cachedToken = self.cachedToken {
|
|
33
31
|
logger.info("🔑 Returning cached VoIP token: \(cachedToken.prefix(10))...")
|
|
34
32
|
listener(cachedToken)
|
|
35
33
|
} else {
|
|
@@ -39,7 +37,54 @@ class VoIPTokenManager: NSObject {
|
|
|
39
37
|
|
|
40
38
|
func unregisterTokenListener() {
|
|
41
39
|
logger.info("🔑 Unregistering VoIP token listener")
|
|
42
|
-
tokenListener = nil
|
|
40
|
+
self.tokenListener = nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fileprivate func handleTokenUpdate(deviceToken: String) {
|
|
44
|
+
logger.info("🔑 ✅ VoIP Device Token received: \(deviceToken.prefix(10))...\(deviceToken.suffix(10))")
|
|
45
|
+
|
|
46
|
+
self.cachedToken = deviceToken
|
|
47
|
+
logger.info("🔑 Token cached successfully")
|
|
48
|
+
|
|
49
|
+
if let tokenListener = self.tokenListener {
|
|
50
|
+
logger.info("🔑 Calling token listener with new token")
|
|
51
|
+
tokenListener(deviceToken)
|
|
52
|
+
} else {
|
|
53
|
+
logger.info("🔑 No token listener registered, token will be returned when listener is added")
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fileprivate func handleIncomingPush(payload: [String: Any]) {
|
|
58
|
+
logger.info("🔔 Received VoIP push notification")
|
|
59
|
+
logger.debug("🔔 Full payload: \(payload)")
|
|
60
|
+
|
|
61
|
+
if let notificationType = payload["type"] as? String {
|
|
62
|
+
logger.info("🔔 Notification type: \(notificationType)")
|
|
63
|
+
switch notificationType {
|
|
64
|
+
case "Call":
|
|
65
|
+
logger.info("🔔 Processing incoming call notification...")
|
|
66
|
+
handleIncomingCall(payload: payload)
|
|
67
|
+
case "EndCall":
|
|
68
|
+
logger.info("🔔 Processing end call notification...")
|
|
69
|
+
handleEndCall(payload: payload)
|
|
70
|
+
default:
|
|
71
|
+
logger.warning("🔔 ⚠️ Unknown VoIP notification type: \(notificationType)")
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
logger.info("🔔 No type specified, assuming incoming call...")
|
|
75
|
+
handleIncomingCall(payload: payload)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
logger.info("🔔 ✅ VoIP push notification processing completed")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fileprivate func handleTokenInvalidation() {
|
|
82
|
+
logger.warning("🔑 ⚠️ VoIP push token invalidated")
|
|
83
|
+
self.cachedToken = nil
|
|
84
|
+
if let tokenListener = self.tokenListener {
|
|
85
|
+
logger.info("🔑 Notifying listener about token invalidation")
|
|
86
|
+
tokenListener("")
|
|
87
|
+
}
|
|
43
88
|
}
|
|
44
89
|
|
|
45
90
|
private func handleIncomingCall(payload: [String: Any]) {
|
|
@@ -98,61 +143,25 @@ class VoIPTokenManager: NSObject {
|
|
|
98
143
|
}
|
|
99
144
|
}
|
|
100
145
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
|
104
|
-
logger.info("🔑 VoIP push credentials updated for type: \(type)")
|
|
146
|
+
private class VoIPPushDelegate: NSObject, PKPushRegistryDelegate {
|
|
147
|
+
private weak var manager: VoIPTokenManager?
|
|
105
148
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
cachedToken = deviceToken
|
|
111
|
-
logger.info("🔑 Token cached successfully")
|
|
149
|
+
init(manager: VoIPTokenManager) {
|
|
150
|
+
self.manager = manager
|
|
151
|
+
super.init()
|
|
152
|
+
}
|
|
112
153
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
tokenListener(deviceToken)
|
|
117
|
-
} else {
|
|
118
|
-
logger.info("🔑 No token listener registered, token will be returned when listener is added")
|
|
119
|
-
}
|
|
154
|
+
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
|
155
|
+
let deviceToken = pushCredentials.token.map { String(format: "%02.2hhx", $0) }.joined()
|
|
156
|
+
manager?.handleTokenUpdate(deviceToken: deviceToken)
|
|
120
157
|
}
|
|
121
158
|
|
|
122
159
|
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
|
|
123
|
-
|
|
124
|
-
logger.debug("🔔 Full payload: \(payload.dictionaryPayload)")
|
|
125
|
-
|
|
126
|
-
let payloadDict = payload.dictionaryPayload
|
|
127
|
-
|
|
128
|
-
// Handle different notification types
|
|
129
|
-
if let notificationType = payloadDict["type"] as? String {
|
|
130
|
-
logger.info("🔔 Notification type: \(notificationType)")
|
|
131
|
-
switch notificationType {
|
|
132
|
-
case "Call":
|
|
133
|
-
logger.info("🔔 Processing incoming call notification...")
|
|
134
|
-
handleIncomingCall(payload: payloadDict)
|
|
135
|
-
case "EndCall":
|
|
136
|
-
logger.info("🔔 Processing end call notification...")
|
|
137
|
-
handleEndCall(payload: payloadDict)
|
|
138
|
-
default:
|
|
139
|
-
logger.warning("🔔 ⚠️ Unknown VoIP notification type: \(notificationType)")
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
logger.info("🔔 No type specified, assuming incoming call...")
|
|
143
|
-
handleIncomingCall(payload: payloadDict)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
logger.info("🔔 ✅ VoIP push notification processing completed")
|
|
160
|
+
manager?.handleIncomingPush(payload: payload.dictionaryPayload)
|
|
147
161
|
completion()
|
|
148
162
|
}
|
|
149
163
|
|
|
150
164
|
func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
|
|
151
|
-
|
|
152
|
-
cachedToken = nil
|
|
153
|
-
if let tokenListener = tokenListener {
|
|
154
|
-
logger.info("🔑 Notifying listener about token invalidation")
|
|
155
|
-
tokenListener("")
|
|
156
|
-
}
|
|
165
|
+
manager?.handleTokenInvalidation()
|
|
157
166
|
}
|
|
158
167
|
}
|