@stream-io/react-native-callingx 0.1.0-beta.7 → 0.1.1-beta.1
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/android/build.gradle +7 -1
- package/android/src/main/AndroidManifest.xml +31 -1
- package/android/src/main/java/io/getstream/rn/callingx/CallEventBroadcastReceiver.kt +17 -0
- package/android/src/main/java/io/getstream/rn/callingx/CallRegistrationStore.kt +145 -0
- package/android/src/main/java/io/getstream/rn/callingx/CallService.kt +301 -83
- package/android/src/main/java/io/getstream/rn/callingx/CallingxModuleImpl.kt +148 -390
- package/android/src/main/java/io/getstream/rn/callingx/StreamMessagingService.kt +48 -0
- package/android/src/main/java/io/getstream/rn/callingx/model/Call.kt +1 -0
- package/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt +188 -48
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt +14 -8
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt +12 -1
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverService.kt +7 -0
- package/android/src/main/java/io/getstream/rn/callingx/repo/CallRepository.kt +38 -19
- package/android/src/main/java/io/getstream/rn/callingx/repo/LegacyCallRepository.kt +64 -55
- package/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt +241 -195
- package/android/src/main/java/io/getstream/rn/callingx/utils/CallEventBus.kt +61 -0
- package/android/src/main/java/io/getstream/rn/callingx/utils/SettingsStore.kt +51 -0
- package/android/src/newarch/java/io/getstream/rn/callingx/CallingxModule.kt +12 -3
- package/android/src/oldarch/java/io/getstream/rn/callingx/CallingxModule.kt +13 -3
- package/dist/module/CallingxModule.js +20 -24
- package/dist/module/CallingxModule.js.map +1 -1
- package/dist/module/spec/NativeCallingx.js.map +1 -1
- package/dist/module/utils/constants.js +24 -14
- package/dist/module/utils/constants.js.map +1 -1
- package/dist/typescript/src/CallingxModule.d.ts +4 -2
- package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
- package/dist/typescript/src/spec/NativeCallingx.d.ts +7 -4
- package/dist/typescript/src/spec/NativeCallingx.d.ts.map +1 -1
- package/dist/typescript/src/types.d.ts +33 -5
- package/dist/typescript/src/types.d.ts.map +1 -1
- package/dist/typescript/src/utils/constants.d.ts +2 -3
- package/dist/typescript/src/utils/constants.d.ts.map +1 -1
- package/ios/AudioSessionManager.swift +2 -2
- package/ios/Callingx.mm +41 -17
- package/ios/CallingxImpl.swift +213 -83
- package/ios/Settings.swift +2 -2
- package/ios/UUIDStorage.swift +10 -10
- package/ios/VoipNotificationsManager.swift +8 -8
- package/package.json +4 -2
- package/src/CallingxModule.ts +20 -21
- package/src/spec/NativeCallingx.ts +10 -6
- package/src/types.ts +36 -4
- package/src/utils/constants.ts +23 -12
- /package/android/src/main/java/io/getstream/rn/callingx/{ResourceUtils.kt → utils/ResourceUtils.kt} +0 -0
- /package/android/src/main/java/io/getstream/rn/callingx/{Utils.kt → utils/Utils.kt} +0 -0
package/ios/CallingxImpl.swift
CHANGED
|
@@ -30,7 +30,7 @@ import stream_react_native_webrtc
|
|
|
30
30
|
// MARK: - Shared State
|
|
31
31
|
@objc public static var sharedProvider: CXProvider?
|
|
32
32
|
@objc public static var uuidStorage: UUIDStorage?
|
|
33
|
-
@objc public static
|
|
33
|
+
@objc public static var sharedInstance: CallingxImpl?
|
|
34
34
|
/// Events stored before the module instance exists (e.g. VoIP from killed state). Drained in getInitialEvents().
|
|
35
35
|
private static var delayedEvents: [[String: Any]] = []
|
|
36
36
|
|
|
@@ -42,6 +42,21 @@ import stream_react_native_webrtc
|
|
|
42
42
|
|
|
43
43
|
private var canSendEvents: Bool = false
|
|
44
44
|
private var isSetup: Bool = false
|
|
45
|
+
|
|
46
|
+
// Pending CXActions awaiting JS fulfillment
|
|
47
|
+
private var pendingAnswerActions: [String: (action: CXAnswerCallAction, enqueuedAt: DispatchTime)] = [:]
|
|
48
|
+
private var pendingEndActions: [String: (action: CXEndCallAction, enqueuedAt: DispatchTime)] = [:]
|
|
49
|
+
private let pendingActionsQueue = DispatchQueue(label: "io.getstream.callingx.pendingActions")
|
|
50
|
+
// a large timeout to accomodate for cold start + metro server load time
|
|
51
|
+
private let pendingActionTimeoutSeconds = 30
|
|
52
|
+
|
|
53
|
+
@objc public static func getSharedInstance() -> CallingxImpl {
|
|
54
|
+
if sharedInstance == nil {
|
|
55
|
+
sharedInstance = CallingxImpl()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return sharedInstance!
|
|
59
|
+
}
|
|
45
60
|
|
|
46
61
|
// MARK: - Initialization
|
|
47
62
|
@objc public override init() {
|
|
@@ -58,7 +73,14 @@ import stream_react_native_webrtc
|
|
|
58
73
|
)
|
|
59
74
|
|
|
60
75
|
CallingxImpl.sharedInstance = self
|
|
61
|
-
|
|
76
|
+
|
|
77
|
+
if CallingxImpl.uuidStorage == nil {
|
|
78
|
+
CallingxImpl.uuidStorage = UUIDStorage()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if CallingxImpl.sharedProvider == nil {
|
|
82
|
+
CallingxImpl.sharedProvider = CXProvider(configuration: Settings.getProviderConfiguration())
|
|
83
|
+
}
|
|
62
84
|
|
|
63
85
|
callKeepProvider = CallingxImpl.sharedProvider
|
|
64
86
|
callKeepProvider?.setDelegate(nil, queue: nil)
|
|
@@ -77,13 +99,7 @@ import stream_react_native_webrtc
|
|
|
77
99
|
|
|
78
100
|
// MARK: - Class Methods
|
|
79
101
|
@objc public static func initializeIfNeeded() {
|
|
80
|
-
|
|
81
|
-
uuidStorage = UUIDStorage()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if sharedProvider == nil {
|
|
85
|
-
sharedProvider = CXProvider(configuration: Settings.getProviderConfiguration())
|
|
86
|
-
}
|
|
102
|
+
_ = getSharedInstance() // ensures the shared instance is created and CXProvider delegate is set
|
|
87
103
|
}
|
|
88
104
|
|
|
89
105
|
@objc public static func reportNewIncomingCall(
|
|
@@ -107,7 +123,7 @@ import stream_react_native_webrtc
|
|
|
107
123
|
|
|
108
124
|
if storage.containsCid(callId) {
|
|
109
125
|
#if DEBUG
|
|
110
|
-
|
|
126
|
+
NSLog("%@","[Callingx][reportNewIncomingCall] callId already exists")
|
|
111
127
|
#endif
|
|
112
128
|
completion?()
|
|
113
129
|
resolve?(true)
|
|
@@ -127,7 +143,7 @@ import stream_react_native_webrtc
|
|
|
127
143
|
|
|
128
144
|
sharedProvider?.reportNewIncomingCall(with: uuid, update: callUpdate) { error in
|
|
129
145
|
#if DEBUG
|
|
130
|
-
|
|
146
|
+
NSLog("%@","[Callingx][reportNewIncomingCall] callId = \(callId), error = \(String(describing: error))")
|
|
131
147
|
#endif
|
|
132
148
|
|
|
133
149
|
let errorCode = error != nil ? CallingxImpl.getIncomingCallErrorCode(error!) : ""
|
|
@@ -146,21 +162,13 @@ import stream_react_native_webrtc
|
|
|
146
162
|
"payload": payload ?? ""
|
|
147
163
|
]
|
|
148
164
|
|
|
149
|
-
if let instance = sharedInstance {
|
|
150
|
-
|
|
151
|
-
} else {
|
|
152
|
-
let dictionary: [String: Any] = [
|
|
153
|
-
"eventName": CallingxEvents.didDisplayIncomingCall,
|
|
154
|
-
"params": body
|
|
155
|
-
]
|
|
156
|
-
DispatchQueue.main.async {
|
|
157
|
-
CallingxImpl.delayedEvents.append(dictionary)
|
|
158
|
-
}
|
|
165
|
+
if let instance = CallingxImpl.sharedInstance {
|
|
166
|
+
instance.sendEvent(CallingxEvents.didDisplayIncomingCall, body: body)
|
|
159
167
|
}
|
|
160
|
-
|
|
168
|
+
|
|
161
169
|
if error == nil {
|
|
162
170
|
#if DEBUG
|
|
163
|
-
|
|
171
|
+
NSLog("%@","[Callingx][reportNewIncomingCall] success callId = \(callId)")
|
|
164
172
|
#endif
|
|
165
173
|
resolve?(true)
|
|
166
174
|
} else {
|
|
@@ -204,12 +212,12 @@ import stream_react_native_webrtc
|
|
|
204
212
|
|
|
205
213
|
@objc public static func endCall(_ callId: String, reason: Int) {
|
|
206
214
|
#if DEBUG
|
|
207
|
-
|
|
215
|
+
NSLog("%@","[Callingx][endCall] callId = \(callId) reason = \(reason)")
|
|
208
216
|
#endif
|
|
209
217
|
|
|
210
218
|
guard let call = uuidStorage?.getCall(forCid: callId) else {
|
|
211
219
|
#if DEBUG
|
|
212
|
-
|
|
220
|
+
NSLog("%@","[Callingx][endCall] callId not found")
|
|
213
221
|
#endif
|
|
214
222
|
return
|
|
215
223
|
}
|
|
@@ -242,7 +250,7 @@ import stream_react_native_webrtc
|
|
|
242
250
|
// MARK: - Instance Methods
|
|
243
251
|
@objc public func requestTransaction(_ transaction: CXTransaction) {
|
|
244
252
|
#if DEBUG
|
|
245
|
-
|
|
253
|
+
NSLog("%@","[Callingx][requestTransaction] transaction = \(transaction)")
|
|
246
254
|
#endif
|
|
247
255
|
|
|
248
256
|
if callKeepCallController == nil {
|
|
@@ -252,7 +260,7 @@ import stream_react_native_webrtc
|
|
|
252
260
|
callKeepCallController?.request(transaction) { [weak self] error in
|
|
253
261
|
if let error = error {
|
|
254
262
|
#if DEBUG
|
|
255
|
-
|
|
263
|
+
NSLog("%@","[Callingx][requestTransaction] Error requesting transaction (\(transaction.actions)): (\(error))")
|
|
256
264
|
#endif
|
|
257
265
|
|
|
258
266
|
// Reset per-call action-source flags for all actions in the failed transaction
|
|
@@ -264,7 +272,7 @@ import stream_react_native_webrtc
|
|
|
264
272
|
}
|
|
265
273
|
} else {
|
|
266
274
|
#if DEBUG
|
|
267
|
-
|
|
275
|
+
NSLog("%@","[Callingx][requestTransaction] Requested transaction successfully")
|
|
268
276
|
#endif
|
|
269
277
|
|
|
270
278
|
if let startCallAction = transaction.actions.first as? CXStartCallAction {
|
|
@@ -285,7 +293,7 @@ import stream_react_native_webrtc
|
|
|
285
293
|
|
|
286
294
|
@objc public func sendEvent(_ name: String, body: [String: Any]?) {
|
|
287
295
|
#if DEBUG
|
|
288
|
-
|
|
296
|
+
NSLog("%@","[Callingx] sendEventWithNameWrapper: \(name)")
|
|
289
297
|
#endif
|
|
290
298
|
|
|
291
299
|
let sendEventAction = {
|
|
@@ -299,7 +307,7 @@ import stream_react_native_webrtc
|
|
|
299
307
|
} else {
|
|
300
308
|
CallingxImpl.delayedEvents.append(dictionary)
|
|
301
309
|
#if DEBUG
|
|
302
|
-
|
|
310
|
+
NSLog("%@","[Callingx] delayedEvents: \(CallingxImpl.delayedEvents)")
|
|
303
311
|
#endif
|
|
304
312
|
}
|
|
305
313
|
}
|
|
@@ -334,11 +342,21 @@ import stream_react_native_webrtc
|
|
|
334
342
|
|
|
335
343
|
Settings.setSettings(options)
|
|
336
344
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
345
|
+
// This is mostly needed for very first setup, as we need to override the default
|
|
346
|
+
// provider configuration which is set in the constructor.
|
|
347
|
+
// IMPORTANT: We override CXProvider instance only if there is no registered call, otherwise we may lose corrsponding call state/events from CallKit
|
|
348
|
+
if !CallingxImpl.hasRegisteredCall() {
|
|
349
|
+
let oldProvider = CallingxImpl.sharedProvider
|
|
350
|
+
let newProvider = CXProvider(configuration: Settings.getProviderConfiguration())
|
|
351
|
+
newProvider.setDelegate(self, queue: nil)
|
|
352
|
+
|
|
353
|
+
CallingxImpl.sharedProvider = newProvider
|
|
354
|
+
callKeepProvider = newProvider
|
|
355
|
+
|
|
356
|
+
oldProvider?.setDelegate(nil, queue: nil)
|
|
357
|
+
oldProvider?.invalidate()
|
|
358
|
+
}
|
|
359
|
+
|
|
342
360
|
isSetup = true
|
|
343
361
|
}
|
|
344
362
|
|
|
@@ -346,7 +364,7 @@ import stream_react_native_webrtc
|
|
|
346
364
|
var events: [[String: Any]] = []
|
|
347
365
|
let action = {
|
|
348
366
|
#if DEBUG
|
|
349
|
-
|
|
367
|
+
NSLog("%@","[Callingx][getInitialEvents] delayedEvents = \(CallingxImpl.delayedEvents)")
|
|
350
368
|
#endif
|
|
351
369
|
|
|
352
370
|
events = CallingxImpl.delayedEvents
|
|
@@ -368,12 +386,12 @@ import stream_react_native_webrtc
|
|
|
368
386
|
// MARK: - Call Management
|
|
369
387
|
@objc public func answerIncomingCall(_ callId: String) -> Bool {
|
|
370
388
|
#if DEBUG
|
|
371
|
-
|
|
389
|
+
NSLog("%@","[Callingx][answerIncomingCall] callId = \(callId)")
|
|
372
390
|
#endif
|
|
373
|
-
|
|
391
|
+
|
|
374
392
|
guard let call = CallingxImpl.uuidStorage?.getCall(forCid: callId) else {
|
|
375
393
|
#if DEBUG
|
|
376
|
-
|
|
394
|
+
NSLog("%@","[Callingx][answerIncomingCall] callId not found")
|
|
377
395
|
#endif
|
|
378
396
|
return false
|
|
379
397
|
}
|
|
@@ -381,7 +399,7 @@ import stream_react_native_webrtc
|
|
|
381
399
|
// Guard: already answered or ended — prevent duplicate CXAnswerCallAction transactions
|
|
382
400
|
if call.isAnswered || call.hasEnded {
|
|
383
401
|
#if DEBUG
|
|
384
|
-
|
|
402
|
+
NSLog("%@","[Callingx][answerIncomingCall] callId already answered/ended, skipping")
|
|
385
403
|
#endif
|
|
386
404
|
return true
|
|
387
405
|
}
|
|
@@ -430,7 +448,7 @@ import stream_react_native_webrtc
|
|
|
430
448
|
DispatchQueue.main.asyncAfter(deadline: popTime) { [weak self] in
|
|
431
449
|
guard let self = self, !self.isSetup else { return }
|
|
432
450
|
#if DEBUG
|
|
433
|
-
|
|
451
|
+
NSLog("%@","[Callingx] Displayed a call without a reachable app, ending the call: \(callId)")
|
|
434
452
|
#endif
|
|
435
453
|
CallingxImpl.endCall(callId, reason: CXCallEndedReason.failed.rawValue)
|
|
436
454
|
}
|
|
@@ -440,12 +458,12 @@ import stream_react_native_webrtc
|
|
|
440
458
|
|
|
441
459
|
@objc public func endCall(_ callId: String) -> Bool {
|
|
442
460
|
#if DEBUG
|
|
443
|
-
|
|
461
|
+
NSLog("%@","[Callingx][endCall] callId = \(callId)")
|
|
444
462
|
#endif
|
|
445
463
|
|
|
446
464
|
guard let call = CallingxImpl.uuidStorage?.getCall(forCid: callId) else {
|
|
447
465
|
#if DEBUG
|
|
448
|
-
|
|
466
|
+
NSLog("%@","[Callingx][endCall] callId not found")
|
|
449
467
|
#endif
|
|
450
468
|
return false
|
|
451
469
|
}
|
|
@@ -453,7 +471,7 @@ import stream_react_native_webrtc
|
|
|
453
471
|
// Guard: already ended — prevent duplicate CXEndCallAction transactions
|
|
454
472
|
if call.hasEnded {
|
|
455
473
|
#if DEBUG
|
|
456
|
-
|
|
474
|
+
NSLog("%@","[Callingx][endCall] callId already ended, skipping")
|
|
457
475
|
#endif
|
|
458
476
|
return true
|
|
459
477
|
}
|
|
@@ -471,7 +489,7 @@ import stream_react_native_webrtc
|
|
|
471
489
|
@objc public func isCallTracked(_ callId: String) -> Bool {
|
|
472
490
|
guard let uuid = CallingxImpl.uuidStorage?.getUUID(forCid: callId) else {
|
|
473
491
|
#if DEBUG
|
|
474
|
-
|
|
492
|
+
NSLog("%@","[Callingx][isCallTracked] callId not found")
|
|
475
493
|
#endif
|
|
476
494
|
return false
|
|
477
495
|
}
|
|
@@ -487,12 +505,12 @@ import stream_react_native_webrtc
|
|
|
487
505
|
|
|
488
506
|
@objc public func setCurrentCallActive(_ callId: String) -> Bool {
|
|
489
507
|
#if DEBUG
|
|
490
|
-
|
|
508
|
+
NSLog("%@","[Callingx][setCurrentCallActive] callId = \(callId)")
|
|
491
509
|
#endif
|
|
492
510
|
|
|
493
511
|
guard let call = CallingxImpl.uuidStorage?.getCall(forCid: callId) else {
|
|
494
512
|
#if DEBUG
|
|
495
|
-
|
|
513
|
+
NSLog("%@","[Callingx][setCurrentCallActive] callId not found")
|
|
496
514
|
#endif
|
|
497
515
|
return false
|
|
498
516
|
}
|
|
@@ -507,12 +525,12 @@ import stream_react_native_webrtc
|
|
|
507
525
|
|
|
508
526
|
@objc public func setMutedCall(_ callId: String, isMuted: Bool) -> Bool {
|
|
509
527
|
#if DEBUG
|
|
510
|
-
|
|
528
|
+
NSLog("%@","[Callingx][setMutedCall] muted = \(isMuted)")
|
|
511
529
|
#endif
|
|
512
530
|
|
|
513
531
|
guard let call = CallingxImpl.uuidStorage?.getCall(forCid: callId) else {
|
|
514
532
|
#if DEBUG
|
|
515
|
-
|
|
533
|
+
NSLog("%@","[Callingx][setMutedCall] callId not found")
|
|
516
534
|
#endif
|
|
517
535
|
return false
|
|
518
536
|
}
|
|
@@ -528,12 +546,12 @@ import stream_react_native_webrtc
|
|
|
528
546
|
|
|
529
547
|
@objc public func setOnHoldCall(_ callId: String, isOnHold: Bool) -> Bool {
|
|
530
548
|
#if DEBUG
|
|
531
|
-
|
|
549
|
+
NSLog("%@","[Callingx][setOnHold] uuidString = \(callId), shouldHold = \(isOnHold)")
|
|
532
550
|
#endif
|
|
533
551
|
|
|
534
552
|
guard let uuid = CallingxImpl.uuidStorage?.getUUID(forCid: callId) else {
|
|
535
553
|
#if DEBUG
|
|
536
|
-
|
|
554
|
+
NSLog("%@","[Callingx][setOnHoldCall] callId not found")
|
|
537
555
|
#endif
|
|
538
556
|
return false
|
|
539
557
|
}
|
|
@@ -553,14 +571,14 @@ import stream_react_native_webrtc
|
|
|
553
571
|
hasVideo: Bool
|
|
554
572
|
) {
|
|
555
573
|
#if DEBUG
|
|
556
|
-
|
|
574
|
+
NSLog("%@","[Callingx][startCall] uuidString = \(callId), phoneNumber = \(phoneNumber)")
|
|
557
575
|
#endif
|
|
558
576
|
|
|
559
577
|
guard let storage = CallingxImpl.uuidStorage else { return }
|
|
560
578
|
|
|
561
579
|
if (storage.containsCid(callId)) {
|
|
562
580
|
#if DEBUG
|
|
563
|
-
|
|
581
|
+
NSLog("%@","[Callingx][startCall] Call \(callId) is already registered")
|
|
564
582
|
#endif
|
|
565
583
|
return
|
|
566
584
|
}
|
|
@@ -584,12 +602,12 @@ import stream_react_native_webrtc
|
|
|
584
602
|
callerName: String
|
|
585
603
|
) -> Bool {
|
|
586
604
|
#if DEBUG
|
|
587
|
-
|
|
605
|
+
NSLog("%@","[Callingx][updateDisplay] uuidString = \(callId) displayName = \(callerName) uri = \(phoneNumber)")
|
|
588
606
|
#endif
|
|
589
607
|
|
|
590
608
|
guard let uuid = CallingxImpl.uuidStorage?.getUUID(forCid: callId) else {
|
|
591
609
|
#if DEBUG
|
|
592
|
-
|
|
610
|
+
NSLog("%@","[Callingx][updateDisplay] callId not found")
|
|
593
611
|
#endif
|
|
594
612
|
return false
|
|
595
613
|
}
|
|
@@ -608,12 +626,12 @@ import stream_react_native_webrtc
|
|
|
608
626
|
// MARK: - CXProviderDelegate
|
|
609
627
|
public func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
|
610
628
|
#if DEBUG
|
|
611
|
-
|
|
629
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performStartCallAction]")
|
|
612
630
|
#endif
|
|
613
631
|
|
|
614
632
|
guard let call = CallingxImpl.uuidStorage?.getCallByUUID(action.callUUID) else {
|
|
615
633
|
#if DEBUG
|
|
616
|
-
|
|
634
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performStartCallAction] callId not found")
|
|
617
635
|
#endif
|
|
618
636
|
action.fail()
|
|
619
637
|
return
|
|
@@ -637,14 +655,14 @@ import stream_react_native_webrtc
|
|
|
637
655
|
public func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
|
638
656
|
guard let call = CallingxImpl.uuidStorage?.getCallByUUID(action.callUUID) else {
|
|
639
657
|
#if DEBUG
|
|
640
|
-
|
|
658
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performAnswerCallAction] callId not found")
|
|
641
659
|
#endif
|
|
642
660
|
action.fail()
|
|
643
661
|
return
|
|
644
662
|
}
|
|
645
663
|
|
|
646
664
|
#if DEBUG
|
|
647
|
-
|
|
665
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performAnswerCallAction] isSelfAnswered: \(call.isSelfAnswered)")
|
|
648
666
|
#endif
|
|
649
667
|
|
|
650
668
|
getAudioDeviceModule()?.reset()
|
|
@@ -658,46 +676,85 @@ import stream_react_native_webrtc
|
|
|
658
676
|
|
|
659
677
|
call.resetSelfAnswered()
|
|
660
678
|
call.markConnected() // incoming: call is now connected
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
679
|
+
|
|
680
|
+
if source == "app" {
|
|
681
|
+
// App initiated this answer — no need to wait for JS, fulfill immediately
|
|
682
|
+
action.fulfill()
|
|
683
|
+
} else {
|
|
684
|
+
// System initiated — defer fulfillment until JS reports back via fulfillAnswerCallAction
|
|
685
|
+
let cid = call.cid
|
|
686
|
+
pendingActionsQueue.sync {
|
|
687
|
+
self.pendingAnswerActions[cid] = (action: action, enqueuedAt: DispatchTime.now())
|
|
688
|
+
}
|
|
689
|
+
// Safety timer: auto-fail if JS never responds.
|
|
690
|
+
// Answer timeout = call never connected
|
|
691
|
+
let timeout = DispatchTime.now() + DispatchTimeInterval.seconds(pendingActionTimeoutSeconds)
|
|
692
|
+
pendingActionsQueue.asyncAfter(deadline: timeout) { [weak self] in
|
|
693
|
+
if let pending = self?.pendingAnswerActions.removeValue(forKey: cid) {
|
|
694
|
+
#if DEBUG
|
|
695
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performAnswerCallAction] answer timeout for callId: \(cid)")
|
|
696
|
+
#endif
|
|
697
|
+
pending.action.fail()
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
665
701
|
}
|
|
666
|
-
|
|
702
|
+
|
|
667
703
|
public func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
|
|
668
704
|
guard let call = CallingxImpl.uuidStorage?.getCallByUUID(action.callUUID) else {
|
|
669
705
|
#if DEBUG
|
|
670
|
-
|
|
706
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performEndCallAction] callId not found")
|
|
671
707
|
#endif
|
|
672
|
-
|
|
708
|
+
// End actions represent explicit user intent to close call UI.
|
|
709
|
+
// Fulfill stale/duplicate end actions to avoid "Call Failed" UX.
|
|
710
|
+
action.fulfill()
|
|
673
711
|
return
|
|
674
712
|
}
|
|
675
|
-
|
|
713
|
+
|
|
676
714
|
#if DEBUG
|
|
677
|
-
|
|
715
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performEndCallAction] isSelfEnded: \(call.isSelfEnded)")
|
|
678
716
|
#endif
|
|
679
|
-
|
|
717
|
+
|
|
680
718
|
let source = call.isSelfEnded ? "app" : "sys"
|
|
681
719
|
sendEvent(CallingxEvents.performEndCallAction, body: [
|
|
682
720
|
"callId": call.cid,
|
|
683
721
|
"source": source
|
|
684
722
|
])
|
|
685
|
-
|
|
723
|
+
|
|
686
724
|
call.resetSelfEnded()
|
|
687
725
|
call.markEnded()
|
|
688
726
|
CallingxImpl.uuidStorage?.removeCid(call.cid)
|
|
689
|
-
|
|
690
|
-
|
|
727
|
+
|
|
728
|
+
if source == "app" {
|
|
729
|
+
// App initiated this end — no need to wait for JS, fulfill immediately
|
|
730
|
+
action.fulfill()
|
|
731
|
+
} else {
|
|
732
|
+
// System initiated — defer fulfillment until JS reports back via fulfillEndCallAction
|
|
733
|
+
let cid = call.cid
|
|
734
|
+
pendingActionsQueue.sync {
|
|
735
|
+
self.pendingEndActions[cid] = (action: action, enqueuedAt: DispatchTime.now())
|
|
736
|
+
}
|
|
737
|
+
// Safety timer: auto-fulfill if JS never responds.
|
|
738
|
+
let timeout = DispatchTime.now() + DispatchTimeInterval.seconds(pendingActionTimeoutSeconds)
|
|
739
|
+
pendingActionsQueue.asyncAfter(deadline: timeout) { [weak self] in
|
|
740
|
+
if let pending = self?.pendingEndActions.removeValue(forKey: cid) {
|
|
741
|
+
#if DEBUG
|
|
742
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performEndCallAction] end timeout for callId: \(cid)")
|
|
743
|
+
#endif
|
|
744
|
+
pending.action.fulfill()
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
691
748
|
}
|
|
692
749
|
|
|
693
750
|
public func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
|
|
694
751
|
#if DEBUG
|
|
695
|
-
|
|
752
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performSetHeldCallAction]")
|
|
696
753
|
#endif
|
|
697
754
|
|
|
698
755
|
guard let callId = CallingxImpl.uuidStorage?.getCid(forUUID: action.callUUID) else {
|
|
699
756
|
#if DEBUG
|
|
700
|
-
|
|
757
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performSetHeldCallAction] callId not found")
|
|
701
758
|
#endif
|
|
702
759
|
action.fail()
|
|
703
760
|
return
|
|
@@ -714,7 +771,7 @@ import stream_react_native_webrtc
|
|
|
714
771
|
public func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
|
715
772
|
guard let call = CallingxImpl.uuidStorage?.getCallByUUID(action.callUUID) else {
|
|
716
773
|
#if DEBUG
|
|
717
|
-
|
|
774
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performSetMutedCallAction] callId not found")
|
|
718
775
|
#endif
|
|
719
776
|
action.fail()
|
|
720
777
|
return
|
|
@@ -724,7 +781,7 @@ import stream_react_native_webrtc
|
|
|
724
781
|
call.resetSelfMuted()
|
|
725
782
|
|
|
726
783
|
#if DEBUG
|
|
727
|
-
|
|
784
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performSetMutedCallAction] \(action.isMuted) isAppInitiated: \(isAppInitiated)")
|
|
728
785
|
#endif
|
|
729
786
|
|
|
730
787
|
// Only send the event to JS when the mute was initiated by the system
|
|
@@ -743,12 +800,12 @@ import stream_react_native_webrtc
|
|
|
743
800
|
|
|
744
801
|
public func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {
|
|
745
802
|
#if DEBUG
|
|
746
|
-
|
|
803
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performPlayDTMFCallAction]")
|
|
747
804
|
#endif
|
|
748
805
|
|
|
749
806
|
guard let callId = CallingxImpl.uuidStorage?.getCid(forUUID: action.callUUID) else {
|
|
750
807
|
#if DEBUG
|
|
751
|
-
|
|
808
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:performPlayDTMFCallAction] callId not found")
|
|
752
809
|
#endif
|
|
753
810
|
action.fail()
|
|
754
811
|
return
|
|
@@ -764,7 +821,7 @@ import stream_react_native_webrtc
|
|
|
764
821
|
|
|
765
822
|
public func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
|
766
823
|
#if DEBUG
|
|
767
|
-
|
|
824
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:didActivateAudioSession] category=\(audioSession.category) mode=\(audioSession.mode)")
|
|
768
825
|
#endif
|
|
769
826
|
|
|
770
827
|
// When CallKit activates the AVAudioSession, inform WebRTC as well.
|
|
@@ -780,7 +837,7 @@ import stream_react_native_webrtc
|
|
|
780
837
|
|
|
781
838
|
public func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
|
782
839
|
#if DEBUG
|
|
783
|
-
|
|
840
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:didDeactivateAudioSession] category=\(audioSession.category) mode=\(audioSession.mode)")
|
|
784
841
|
#endif
|
|
785
842
|
|
|
786
843
|
// When CallKit deactivates the AVAudioSession, inform WebRTC as well.
|
|
@@ -796,24 +853,97 @@ import stream_react_native_webrtc
|
|
|
796
853
|
}
|
|
797
854
|
|
|
798
855
|
public func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
|
|
856
|
+
// note: in practice we should never be getting this callback as we already have a pending timeout set.
|
|
857
|
+
// in our tests callkit timesout and exectutes this method in approximately 60 seconds.
|
|
799
858
|
#if DEBUG
|
|
800
|
-
|
|
859
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:timedOutPerformingAction]")
|
|
801
860
|
#endif
|
|
861
|
+
|
|
862
|
+
guard let callAction = action as? CXCallAction else {
|
|
863
|
+
return
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
pendingActionsQueue.sync {
|
|
867
|
+
// cid mapping as soon as end is initiated, so cleanup by matching callUUID.
|
|
868
|
+
if let answerEntry = pendingAnswerActions.first(where: { $0.value.action.callUUID == callAction.callUUID }) {
|
|
869
|
+
pendingAnswerActions.removeValue(forKey: answerEntry.key)
|
|
870
|
+
let elapsedMs = elapsedMilliseconds(since: answerEntry.value.enqueuedAt)
|
|
871
|
+
#if DEBUG
|
|
872
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:timedOutPerformingAction] removed pending answer action for callId: \(answerEntry.key), elapsedMs=\(elapsedMs)")
|
|
873
|
+
#endif
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if let endEntry = pendingEndActions.first(where: { $0.value.action.callUUID == callAction.callUUID }) {
|
|
877
|
+
pendingEndActions.removeValue(forKey: endEntry.key)
|
|
878
|
+
let elapsedMs = elapsedMilliseconds(since: endEntry.value.enqueuedAt)
|
|
879
|
+
#if DEBUG
|
|
880
|
+
NSLog("%@","[Callingx][CXProviderDelegate][provider:timedOutPerformingAction] removed pending end action for callId: \(endEntry.key), elapsedMs=\(elapsedMs)")
|
|
881
|
+
#endif
|
|
882
|
+
}
|
|
883
|
+
}
|
|
802
884
|
}
|
|
803
885
|
|
|
804
886
|
public func providerDidReset(_ provider: CXProvider) {
|
|
805
887
|
#if DEBUG
|
|
806
|
-
|
|
888
|
+
NSLog("%@","[Callingx][providerDidReset]")
|
|
807
889
|
#endif
|
|
808
890
|
|
|
891
|
+
// Clear any pending actions to prevent memory leaks.
|
|
892
|
+
// After a provider reset, all pending CXActions are invalid.
|
|
893
|
+
pendingActionsQueue.sync {
|
|
894
|
+
pendingAnswerActions.removeAll()
|
|
895
|
+
pendingEndActions.removeAll()
|
|
896
|
+
}
|
|
897
|
+
|
|
809
898
|
sendEvent(CallingxEvents.providerReset, body: nil)
|
|
810
899
|
}
|
|
811
900
|
|
|
901
|
+
// MARK: - Pending Action Fulfillment
|
|
902
|
+
|
|
903
|
+
@objc public func fulfillAnswerCallAction(_ callId: String, didFail: Bool) {
|
|
904
|
+
pendingActionsQueue.sync { [weak self] in
|
|
905
|
+
guard let pending = self?.pendingAnswerActions.removeValue(forKey: callId) else {
|
|
906
|
+
#if DEBUG
|
|
907
|
+
NSLog("%@","[Callingx][fulfillAnswerCallAction] action not found for callId: \(callId)")
|
|
908
|
+
#endif
|
|
909
|
+
return
|
|
910
|
+
}
|
|
911
|
+
let elapsedMs = elapsedMilliseconds(since: pending.enqueuedAt)
|
|
912
|
+
#if DEBUG
|
|
913
|
+
NSLog("%@","[Callingx][fulfillAnswerCallAction] callId: \(callId), didFail: \(didFail), elapsedMs=\(elapsedMs)")
|
|
914
|
+
#endif
|
|
915
|
+
if didFail { pending.action.fail() } else { pending.action.fulfill() }
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
@objc public func fulfillEndCallAction(_ callId: String, didFail: Bool) {
|
|
920
|
+
pendingActionsQueue.sync { [weak self] in
|
|
921
|
+
guard let pending = self?.pendingEndActions.removeValue(forKey: callId) else {
|
|
922
|
+
#if DEBUG
|
|
923
|
+
NSLog("%@","[Callingx][fulfillEndCallAction] action not found for callId: \(callId)")
|
|
924
|
+
#endif
|
|
925
|
+
return
|
|
926
|
+
}
|
|
927
|
+
let elapsedMs = elapsedMilliseconds(since: pending.enqueuedAt)
|
|
928
|
+
#if DEBUG
|
|
929
|
+
NSLog("%@","[Callingx][fulfillEndCallAction] callId: \(callId), didFail: \(didFail), elapsedMs=\(elapsedMs)")
|
|
930
|
+
#endif
|
|
931
|
+
if didFail { pending.action.fail() } else { pending.action.fulfill() }
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
812
935
|
// MARK: - Helper Methods
|
|
936
|
+
private func elapsedMilliseconds(since start: DispatchTime) -> Int {
|
|
937
|
+
let nowNs = DispatchTime.now().uptimeNanoseconds
|
|
938
|
+
let startNs = start.uptimeNanoseconds
|
|
939
|
+
guard nowNs >= startNs else { return 0 }
|
|
940
|
+
return Int((nowNs - startNs) / 1_000_000)
|
|
941
|
+
}
|
|
942
|
+
|
|
813
943
|
private func getAudioDeviceModule() -> AudioDeviceModule? {
|
|
814
944
|
guard let adm = webRTCModule?.audioDeviceModule else {
|
|
815
945
|
#if DEBUG
|
|
816
|
-
|
|
946
|
+
NSLog("%@","[Callingx] WebRTCModule is not available. Ensure it was injected from the TurboModule host.")
|
|
817
947
|
#endif
|
|
818
948
|
return nil
|
|
819
949
|
}
|
package/ios/Settings.swift
CHANGED
|
@@ -11,7 +11,7 @@ import UIKit
|
|
|
11
11
|
|
|
12
12
|
public static func setSettings(_ options: [String: Any]?) {
|
|
13
13
|
#if DEBUG
|
|
14
|
-
|
|
14
|
+
NSLog("%@","[Settings][setSettings] options = \(String(describing: options))")
|
|
15
15
|
#endif
|
|
16
16
|
|
|
17
17
|
var settings: [String: Any] = getSettings()
|
|
@@ -39,7 +39,7 @@ import UIKit
|
|
|
39
39
|
|
|
40
40
|
public static func getProviderConfiguration() -> CXProviderConfiguration {
|
|
41
41
|
#if DEBUG
|
|
42
|
-
|
|
42
|
+
NSLog("%@","[Settings][getProviderConfiguration]")
|
|
43
43
|
#endif
|
|
44
44
|
|
|
45
45
|
let settings = getSettings()
|