expo-callkit-telecom 0.2.0 → 0.2.2
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/README.md +11 -0
- package/android/src/main/java/expo/modules/callkittelecom/ExpoCallKitTelecomModule.kt +254 -282
- package/android/src/main/java/expo/modules/callkittelecom/IncomingCallActivity.kt +27 -34
- package/android/src/main/java/expo/modules/callkittelecom/events/CallEventEmitter.kt +21 -32
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallAudioManager.kt +53 -74
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallManager.kt +95 -156
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallNotificationManager.kt +71 -84
- package/android/src/main/java/expo/modules/callkittelecom/managers/CaptureSessionManager.kt +1 -3
- package/android/src/main/java/expo/modules/callkittelecom/managers/DialtonePlayer.kt +6 -4
- package/android/src/main/java/expo/modules/callkittelecom/managers/FulfillRequestManager.kt +21 -21
- package/android/src/main/java/expo/modules/callkittelecom/managers/VoIPPushManager.kt +3 -5
- package/android/src/main/java/expo/modules/callkittelecom/models/CallModels.kt +28 -36
- package/android/src/main/java/expo/modules/callkittelecom/services/CallNotificationReceiver.kt +7 -8
- package/android/src/main/java/expo/modules/callkittelecom/services/ExpoCallKitTelecomMessagingService.kt +9 -16
- package/android/src/main/java/expo/modules/callkittelecom/store/CallStore.kt +32 -68
- package/android/src/main/java/expo/modules/callkittelecom/utils/CallKitTelecomLog.kt +6 -17
- package/android/src/main/java/expo/modules/callkittelecom/utils/PermissionUtils.kt +7 -7
- package/ios/Managers/CaptureSessionManager.swift +1 -1
- package/package.json +1 -1
|
@@ -27,6 +27,9 @@ import expo.modules.callkittelecom.models.CallSessionStatus
|
|
|
27
27
|
import expo.modules.callkittelecom.models.IncomingCallEvent
|
|
28
28
|
import expo.modules.callkittelecom.store.CallStore
|
|
29
29
|
import expo.modules.callkittelecom.utils.CallKitTelecomLog
|
|
30
|
+
import java.time.Instant
|
|
31
|
+
import java.util.UUID
|
|
32
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
30
33
|
import kotlinx.coroutines.CancellationException
|
|
31
34
|
import kotlinx.coroutines.CoroutineName
|
|
32
35
|
import kotlinx.coroutines.CoroutineScope
|
|
@@ -39,9 +42,6 @@ import kotlinx.coroutines.delay
|
|
|
39
42
|
import kotlinx.coroutines.isActive
|
|
40
43
|
import kotlinx.coroutines.launch
|
|
41
44
|
import kotlinx.coroutines.selects.select
|
|
42
|
-
import java.time.Instant
|
|
43
|
-
import java.util.UUID
|
|
44
|
-
import java.util.concurrent.ConcurrentHashMap
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Central Android call lifecycle manager using Core-Telecom Jetpack.
|
|
@@ -70,8 +70,8 @@ class CallManager private constructor() {
|
|
|
70
70
|
/**
|
|
71
71
|
* Channels for dispatching actions into active Core-Telecom call scopes.
|
|
72
72
|
*
|
|
73
|
-
* Scope methods are called from within the addCall block via channel-based
|
|
74
|
-
*
|
|
73
|
+
* Scope methods are called from within the addCall block via channel-based dispatch rather than
|
|
74
|
+
* storing and calling CallControlScope references externally.
|
|
75
75
|
*/
|
|
76
76
|
private class CallActions {
|
|
77
77
|
val setActive = Channel<Unit>(Channel.CONFLATED)
|
|
@@ -81,8 +81,8 @@ class CallManager private constructor() {
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/**
|
|
84
|
-
* Encapsulates the coroutine job, action channels, and timeout for a single call.
|
|
85
|
-
*
|
|
84
|
+
* Encapsulates the coroutine job, action channels, and timeout for a single call. Consolidates
|
|
85
|
+
* what was previously three separate maps.
|
|
86
86
|
*/
|
|
87
87
|
private class CallController(
|
|
88
88
|
val job: Job,
|
|
@@ -116,12 +116,15 @@ class CallManager private constructor() {
|
|
|
116
116
|
|
|
117
117
|
callsManager = CallsManager(context)
|
|
118
118
|
callsManager.registerAppWithTelecom(
|
|
119
|
-
CallsManager.CAPABILITY_BASELINE or CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING
|
|
119
|
+
CallsManager.CAPABILITY_BASELINE or CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING
|
|
120
120
|
)
|
|
121
121
|
|
|
122
|
-
incomingCallTimeoutMs =
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
incomingCallTimeoutMs =
|
|
123
|
+
readTimeoutMs("ExpoCallKitTelecomIncomingCallTimeout", incomingCallTimeoutMs)
|
|
124
|
+
outgoingCallTimeoutMs =
|
|
125
|
+
readTimeoutMs("ExpoCallKitTelecomOutgoingCallTimeout", outgoingCallTimeoutMs)
|
|
126
|
+
fulfillAnswerTimeoutMs =
|
|
127
|
+
readTimeoutMs("ExpoCallKitTelecomFulfillAnswerCallTimeout", fulfillAnswerTimeoutMs)
|
|
125
128
|
|
|
126
129
|
CallAudioManager.initialize(context)
|
|
127
130
|
CallAudioManager.onRequestEndpointChange = { endpoint ->
|
|
@@ -139,10 +142,7 @@ class CallManager private constructor() {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
/** Reads a timeout from Android manifest metadata (seconds) and returns milliseconds. */
|
|
142
|
-
private fun readTimeoutMs(
|
|
143
|
-
key: String,
|
|
144
|
-
defaultMs: Long,
|
|
145
|
-
): Long =
|
|
145
|
+
private fun readTimeoutMs(key: String, defaultMs: Long): Long =
|
|
146
146
|
try {
|
|
147
147
|
val defaultSeconds = (defaultMs / 1000).toInt()
|
|
148
148
|
val appInfo =
|
|
@@ -159,10 +159,7 @@ class CallManager private constructor() {
|
|
|
159
159
|
// region Call Timeout
|
|
160
160
|
|
|
161
161
|
/** Starts a call timeout that marks non-connected calls as unanswered. */
|
|
162
|
-
private fun startCallTimeout(
|
|
163
|
-
id: UUID,
|
|
164
|
-
timeoutMs: Long,
|
|
165
|
-
) {
|
|
162
|
+
private fun startCallTimeout(id: UUID, timeoutMs: Long) {
|
|
166
163
|
cancelCallTimeout(id)
|
|
167
164
|
CallKitTelecomLog.d(TAG) { "Starting call timeout - id: $id, timeout: ${timeoutMs}ms" }
|
|
168
165
|
|
|
@@ -203,7 +200,11 @@ class CallManager private constructor() {
|
|
|
203
200
|
return Uri.fromParts(PhoneAccount.SCHEME_SIP, email, null)
|
|
204
201
|
}
|
|
205
202
|
|
|
206
|
-
return Uri.fromParts(
|
|
203
|
+
return Uri.fromParts(
|
|
204
|
+
PhoneAccount.SCHEME_SIP,
|
|
205
|
+
"${participant.id}@callkit-telecom.local",
|
|
206
|
+
null,
|
|
207
|
+
)
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
// region Start Outgoing Call
|
|
@@ -217,13 +218,12 @@ class CallManager private constructor() {
|
|
|
217
218
|
* - preps audio for call
|
|
218
219
|
* - calls addCall with DIRECTION_OUTGOING
|
|
219
220
|
*/
|
|
220
|
-
fun startOutgoingCall(
|
|
221
|
-
recipient: CallParticipant,
|
|
222
|
-
options: CallOptions,
|
|
223
|
-
): String {
|
|
221
|
+
fun startOutgoingCall(recipient: CallParticipant, options: CallOptions): String {
|
|
224
222
|
val existingSession = CallStore.firstSession()
|
|
225
223
|
if (existingSession != null) {
|
|
226
|
-
CallKitTelecomLog.w(TAG) {
|
|
224
|
+
CallKitTelecomLog.w(TAG) {
|
|
225
|
+
"Cannot start outgoing call - session already exists: ${existingSession.id}"
|
|
226
|
+
}
|
|
227
227
|
throw IllegalStateException("A call session already exists")
|
|
228
228
|
}
|
|
229
229
|
|
|
@@ -314,7 +314,9 @@ class CallManager private constructor() {
|
|
|
314
314
|
fun reportIncomingCall(event: IncomingCallEvent) {
|
|
315
315
|
val existingSession = CallStore.firstSession()
|
|
316
316
|
if (existingSession != null) {
|
|
317
|
-
CallKitTelecomLog.w(TAG) {
|
|
317
|
+
CallKitTelecomLog.w(TAG) {
|
|
318
|
+
"Cannot report incoming call - session already exists: ${existingSession.id}"
|
|
319
|
+
}
|
|
318
320
|
throw IllegalStateException("A call session already exists")
|
|
319
321
|
}
|
|
320
322
|
|
|
@@ -418,15 +420,12 @@ class CallManager private constructor() {
|
|
|
418
420
|
|
|
419
421
|
activeCalls[callId]?.actions?.setActive?.trySend(Unit)
|
|
420
422
|
|
|
421
|
-
val callerName =
|
|
422
|
-
CallStore
|
|
423
|
-
.session(callId)
|
|
424
|
-
?.remoteParticipants
|
|
425
|
-
?.firstOrNull()
|
|
426
|
-
?.displayName
|
|
423
|
+
val callerName = CallStore.session(callId)?.remoteParticipants?.firstOrNull()?.displayName
|
|
427
424
|
CallNotificationManager.showOngoingCall(context, callId, callerName, now.toEpochMilli())
|
|
428
425
|
|
|
429
|
-
CallKitTelecomLog.d(TAG) {
|
|
426
|
+
CallKitTelecomLog.d(TAG) {
|
|
427
|
+
"Fulfilled incoming call - callId: $callId, requestId: $requestId"
|
|
428
|
+
}
|
|
430
429
|
return true
|
|
431
430
|
}
|
|
432
431
|
|
|
@@ -443,12 +442,7 @@ class CallManager private constructor() {
|
|
|
443
442
|
|
|
444
443
|
activeCalls[id]?.actions?.setActive?.trySend(Unit)
|
|
445
444
|
|
|
446
|
-
val callerName =
|
|
447
|
-
CallStore
|
|
448
|
-
.session(id)
|
|
449
|
-
?.remoteParticipants
|
|
450
|
-
?.firstOrNull()
|
|
451
|
-
?.displayName
|
|
445
|
+
val callerName = CallStore.session(id)?.remoteParticipants?.firstOrNull()?.displayName
|
|
452
446
|
CallNotificationManager.showOngoingCall(context, id, callerName, now.toEpochMilli())
|
|
453
447
|
}
|
|
454
448
|
|
|
@@ -463,17 +457,13 @@ class CallManager private constructor() {
|
|
|
463
457
|
}
|
|
464
458
|
|
|
465
459
|
/** Reports externally-ended call with explicit reason (`onCallReportedEnded` path). */
|
|
466
|
-
fun reportCallEnded(
|
|
467
|
-
id: UUID,
|
|
468
|
-
reason: CallEndedReason,
|
|
469
|
-
) {
|
|
460
|
+
fun reportCallEnded(id: UUID, reason: CallEndedReason) {
|
|
470
461
|
CallKitTelecomLog.d(TAG) { "Reporting call ended - id: $id, reason: ${reason.value}" }
|
|
471
462
|
finishCall(id, emitEnded = false, reportedReason = reason)
|
|
472
463
|
}
|
|
473
464
|
|
|
474
465
|
/**
|
|
475
466
|
* Shared call-finalization routine.
|
|
476
|
-
*
|
|
477
467
|
* - Cancels timeouts and pending fulfill requests
|
|
478
468
|
* - Disconnects Core-Telecom scope (which causes addCall to return)
|
|
479
469
|
* - Emits ended/reported-ended events as requested
|
|
@@ -499,11 +489,7 @@ class CallManager private constructor() {
|
|
|
499
489
|
// so the DisconnectCause is properly delivered to the Telecom framework.
|
|
500
490
|
// The finally block provides safety-net cleanup if needed.
|
|
501
491
|
if (sendDisconnect) {
|
|
502
|
-
activeCalls
|
|
503
|
-
.remove(id)
|
|
504
|
-
?.actions
|
|
505
|
-
?.disconnect
|
|
506
|
-
?.trySend(disconnectCauseFor(reportedReason))
|
|
492
|
+
activeCalls.remove(id)?.actions?.disconnect?.trySend(disconnectCauseFor(reportedReason))
|
|
507
493
|
}
|
|
508
494
|
|
|
509
495
|
if (existingSession.status != CallSessionStatus.ENDED) {
|
|
@@ -517,10 +503,7 @@ class CallManager private constructor() {
|
|
|
517
503
|
if (reportedReason != null) {
|
|
518
504
|
CallEventEmitter.send(
|
|
519
505
|
CallEvents.CALL_REPORTED_ENDED,
|
|
520
|
-
mapOf(
|
|
521
|
-
"id" to id.toString(),
|
|
522
|
-
"reason" to reportedReason.value,
|
|
523
|
-
),
|
|
506
|
+
mapOf("id" to id.toString(), "reason" to reportedReason.value),
|
|
524
507
|
)
|
|
525
508
|
}
|
|
526
509
|
|
|
@@ -531,20 +514,24 @@ class CallManager private constructor() {
|
|
|
531
514
|
CallAudioManager.onAudioDeactivated(remainingSessions)
|
|
532
515
|
}
|
|
533
516
|
|
|
534
|
-
CallKitTelecomLog.d(TAG) {
|
|
517
|
+
CallKitTelecomLog.d(TAG) {
|
|
518
|
+
"Call finished - id: $id, emitEnded: $emitEnded, reason: ${reportedReason?.value}"
|
|
519
|
+
}
|
|
535
520
|
}
|
|
536
521
|
|
|
537
522
|
/**
|
|
538
523
|
* Safety-net cleanup called from the addCall finally block.
|
|
539
524
|
*
|
|
540
|
-
* Ensures all resources are released even if finishCall didn't run due to
|
|
541
|
-
*
|
|
525
|
+
* Ensures all resources are released even if finishCall didn't run due to an unexpected
|
|
526
|
+
* exception or cancellation.
|
|
542
527
|
*/
|
|
543
528
|
private fun cleanupCallIfNeeded(id: UUID) {
|
|
544
529
|
activeCalls.remove(id)
|
|
545
530
|
|
|
546
531
|
val session = CallStore.session(id) ?: return
|
|
547
|
-
CallKitTelecomLog.w(TAG) {
|
|
532
|
+
CallKitTelecomLog.w(TAG) {
|
|
533
|
+
"Safety-net cleanup for call - id: $id, status: ${session.status.value}"
|
|
534
|
+
}
|
|
548
535
|
|
|
549
536
|
DialtonePlayer.stop()
|
|
550
537
|
FulfillRequestManager.cancelForCall(id)
|
|
@@ -569,12 +556,10 @@ class CallManager private constructor() {
|
|
|
569
556
|
CallEndedReason.UNANSWERED -> DisconnectCause(DisconnectCause.MISSED)
|
|
570
557
|
|
|
571
558
|
CallEndedReason.ANSWERED_ELSEWHERE,
|
|
572
|
-
CallEndedReason.DECLINED_ELSEWHERE
|
|
573
|
-
-> DisconnectCause(DisconnectCause.REMOTE)
|
|
559
|
+
CallEndedReason.DECLINED_ELSEWHERE -> DisconnectCause(DisconnectCause.REMOTE)
|
|
574
560
|
|
|
575
561
|
CallEndedReason.FAILED,
|
|
576
|
-
CallEndedReason.UNKNOWN
|
|
577
|
-
-> DisconnectCause(DisconnectCause.LOCAL)
|
|
562
|
+
CallEndedReason.UNKNOWN -> DisconnectCause(DisconnectCause.LOCAL)
|
|
578
563
|
|
|
579
564
|
null -> DisconnectCause(DisconnectCause.LOCAL)
|
|
580
565
|
}
|
|
@@ -584,18 +569,12 @@ class CallManager private constructor() {
|
|
|
584
569
|
// region Mute Support
|
|
585
570
|
|
|
586
571
|
/** Sets local mute state and emits `onSetMutedAction`. */
|
|
587
|
-
fun setMuted(
|
|
588
|
-
id: UUID,
|
|
589
|
-
muted: Boolean,
|
|
590
|
-
) {
|
|
572
|
+
fun setMuted(id: UUID, muted: Boolean) {
|
|
591
573
|
CallKitTelecomLog.d(TAG) { "Setting mute state - id: $id, muted: $muted" }
|
|
592
574
|
CallStore.updateMuted(id, muted)
|
|
593
575
|
CallEventEmitter.send(
|
|
594
576
|
CallEvents.SET_MUTED_ACTION,
|
|
595
|
-
mapOf(
|
|
596
|
-
"id" to id.toString(),
|
|
597
|
-
"isMuted" to muted,
|
|
598
|
-
),
|
|
577
|
+
mapOf("id" to id.toString(), "isMuted" to muted),
|
|
599
578
|
)
|
|
600
579
|
}
|
|
601
580
|
|
|
@@ -604,10 +583,7 @@ class CallManager private constructor() {
|
|
|
604
583
|
// region Video Support
|
|
605
584
|
|
|
606
585
|
/** Reports video enabled state change and emits `onVideoChanged`. */
|
|
607
|
-
fun reportVideo(
|
|
608
|
-
id: UUID,
|
|
609
|
-
enabled: Boolean,
|
|
610
|
-
) {
|
|
586
|
+
fun reportVideo(id: UUID, enabled: Boolean) {
|
|
611
587
|
CallKitTelecomLog.d(TAG) { "Setting video state - id: $id, enabled: $enabled" }
|
|
612
588
|
CallStore.update(id) { session ->
|
|
613
589
|
session.copy(options = session.options.copy(hasVideo = enabled))
|
|
@@ -617,10 +593,7 @@ class CallManager private constructor() {
|
|
|
617
593
|
|
|
618
594
|
CallEventEmitter.send(
|
|
619
595
|
CallEvents.VIDEO_CHANGED,
|
|
620
|
-
mapOf(
|
|
621
|
-
"id" to id.toString(),
|
|
622
|
-
"hasVideo" to enabled,
|
|
623
|
-
),
|
|
596
|
+
mapOf("id" to id.toString(), "hasVideo" to enabled),
|
|
624
597
|
)
|
|
625
598
|
}
|
|
626
599
|
|
|
@@ -629,10 +602,7 @@ class CallManager private constructor() {
|
|
|
629
602
|
// region Hold Support
|
|
630
603
|
|
|
631
604
|
/** Sets hold state, updates Core-Telecom scope state, and emits `onSetHeldAction`. */
|
|
632
|
-
fun setHeld(
|
|
633
|
-
id: UUID,
|
|
634
|
-
onHold: Boolean,
|
|
635
|
-
) {
|
|
605
|
+
fun setHeld(id: UUID, onHold: Boolean) {
|
|
636
606
|
CallKitTelecomLog.d(TAG) { "Setting hold state - id: $id, onHold: $onHold" }
|
|
637
607
|
CallStore.updateHeld(id, onHold)
|
|
638
608
|
|
|
@@ -644,10 +614,7 @@ class CallManager private constructor() {
|
|
|
644
614
|
|
|
645
615
|
CallEventEmitter.send(
|
|
646
616
|
CallEvents.SET_HELD_ACTION,
|
|
647
|
-
mapOf(
|
|
648
|
-
"id" to id.toString(),
|
|
649
|
-
"isOnHold" to onHold,
|
|
650
|
-
),
|
|
617
|
+
mapOf("id" to id.toString(), "isOnHold" to onHold),
|
|
651
618
|
)
|
|
652
619
|
}
|
|
653
620
|
|
|
@@ -656,22 +623,11 @@ class CallManager private constructor() {
|
|
|
656
623
|
// region DTMF Support
|
|
657
624
|
|
|
658
625
|
/** Records requested DTMF digits and emits `onDTMF`. */
|
|
659
|
-
fun playDTMF(
|
|
660
|
-
id: UUID,
|
|
661
|
-
digits: String,
|
|
662
|
-
) {
|
|
626
|
+
fun playDTMF(id: UUID, digits: String) {
|
|
663
627
|
CallKitTelecomLog.d(TAG) { "Playing DTMF - id: $id, length: ${digits.length}" }
|
|
664
|
-
CallStore.update(id) { session ->
|
|
665
|
-
session.copy(dtmfDigits = digits)
|
|
666
|
-
}
|
|
628
|
+
CallStore.update(id) { session -> session.copy(dtmfDigits = digits) }
|
|
667
629
|
|
|
668
|
-
CallEventEmitter.send(
|
|
669
|
-
CallEvents.DTMF,
|
|
670
|
-
mapOf(
|
|
671
|
-
"id" to id.toString(),
|
|
672
|
-
"digits" to digits,
|
|
673
|
-
),
|
|
674
|
-
)
|
|
630
|
+
CallEventEmitter.send(CallEvents.DTMF, mapOf("id" to id.toString(), "digits" to digits))
|
|
675
631
|
}
|
|
676
632
|
|
|
677
633
|
// endregion
|
|
@@ -681,8 +637,8 @@ class CallManager private constructor() {
|
|
|
681
637
|
/**
|
|
682
638
|
* Launches a Core-Telecom call scope with shared lifecycle management.
|
|
683
639
|
*
|
|
684
|
-
* Handles channel setup, action dispatch via select, flow collectors,
|
|
685
|
-
*
|
|
640
|
+
* Handles channel setup, action dispatch via select, flow collectors, and safety-net cleanup in
|
|
641
|
+
* a single place.
|
|
686
642
|
*
|
|
687
643
|
* @param id Call UUID
|
|
688
644
|
* @param attributes Core-Telecom call attributes
|
|
@@ -704,15 +660,18 @@ class CallManager private constructor() {
|
|
|
704
660
|
callAttributes = attributes,
|
|
705
661
|
onAnswer = onAnswer,
|
|
706
662
|
onDisconnect = { cause ->
|
|
707
|
-
CallKitTelecomLog.d(TAG) {
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
663
|
+
CallKitTelecomLog.d(TAG) {
|
|
664
|
+
"Call onDisconnect - id: $id, cause: ${cause.code}"
|
|
665
|
+
}
|
|
666
|
+
finishCall(
|
|
667
|
+
id,
|
|
668
|
+
emitEnded = true,
|
|
669
|
+
reportedReason = null,
|
|
670
|
+
sendDisconnect = false,
|
|
671
|
+
)
|
|
715
672
|
},
|
|
673
|
+
onSetActive = { setHeld(id, false) },
|
|
674
|
+
onSetInactive = { setHeld(id, true) },
|
|
716
675
|
) {
|
|
717
676
|
val callScope: CallControlScope = this
|
|
718
677
|
|
|
@@ -729,7 +688,12 @@ class CallManager private constructor() {
|
|
|
729
688
|
CallKitTelecomLog.d(TAG) { "Call coroutine cancelled - id: $id" }
|
|
730
689
|
} catch (e: Exception) {
|
|
731
690
|
CallKitTelecomLog.e(TAG) { "Call addCall failed - id: $id, error: ${e.message}" }
|
|
732
|
-
finishCall(
|
|
691
|
+
finishCall(
|
|
692
|
+
id,
|
|
693
|
+
emitEnded = true,
|
|
694
|
+
reportedReason = CallEndedReason.FAILED,
|
|
695
|
+
sendDisconnect = false,
|
|
696
|
+
)
|
|
733
697
|
} finally {
|
|
734
698
|
cleanupCallIfNeeded(id)
|
|
735
699
|
CallKitTelecomLog.d(TAG) { "Call addCall block exited - id: $id" }
|
|
@@ -739,26 +703,17 @@ class CallManager private constructor() {
|
|
|
739
703
|
/**
|
|
740
704
|
* Processes call action channels using a single select loop.
|
|
741
705
|
*
|
|
742
|
-
* Runs until the coroutine is cancelled (when addCall returns).
|
|
743
|
-
*
|
|
744
|
-
* concurrent scope method calls from racing.
|
|
706
|
+
* Runs until the coroutine is cancelled (when addCall returns). Using select ensures actions
|
|
707
|
+
* are processed sequentially, preventing concurrent scope method calls from racing.
|
|
745
708
|
*/
|
|
746
|
-
private suspend fun handleCallActions(
|
|
747
|
-
id: UUID,
|
|
748
|
-
actions: CallActions,
|
|
749
|
-
scope: CallControlScope,
|
|
750
|
-
) {
|
|
709
|
+
private suspend fun handleCallActions(id: UUID, actions: CallActions, scope: CallControlScope) {
|
|
751
710
|
while (currentCoroutineContext().isActive) {
|
|
752
711
|
select<Unit> {
|
|
753
712
|
actions.setActive.onReceive {
|
|
754
713
|
handleControlResult(id, "setActive", scope.setActive())
|
|
755
714
|
}
|
|
756
|
-
actions.setInactive.onReceive {
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
actions.disconnect.onReceive { cause ->
|
|
760
|
-
scope.disconnect(cause)
|
|
761
|
-
}
|
|
715
|
+
actions.setInactive.onReceive { logIfError(id, "setInactive", scope.setInactive()) }
|
|
716
|
+
actions.disconnect.onReceive { cause -> scope.disconnect(cause) }
|
|
762
717
|
actions.endpointChange.onReceive { endpoint ->
|
|
763
718
|
logIfError(id, "requestEndpointChange", scope.requestEndpointChange(endpoint))
|
|
764
719
|
}
|
|
@@ -769,14 +724,10 @@ class CallManager private constructor() {
|
|
|
769
724
|
/**
|
|
770
725
|
* Handles CallControlResult for critical actions like setActive.
|
|
771
726
|
*
|
|
772
|
-
* When a critical action fails, the call cannot continue in a valid state
|
|
773
|
-
*
|
|
727
|
+
* When a critical action fails, the call cannot continue in a valid state and is ended with a
|
|
728
|
+
* FAILED reason.
|
|
774
729
|
*/
|
|
775
|
-
private fun handleControlResult(
|
|
776
|
-
id: UUID,
|
|
777
|
-
action: String,
|
|
778
|
-
result: CallControlResult,
|
|
779
|
-
) {
|
|
730
|
+
private fun handleControlResult(id: UUID, action: String, result: CallControlResult) {
|
|
780
731
|
if (result is CallControlResult.Error) {
|
|
781
732
|
CallKitTelecomLog.e(TAG) { "$action failed - id: $id, error: ${result.errorCode}" }
|
|
782
733
|
reportCallEnded(id, CallEndedReason.FAILED)
|
|
@@ -784,11 +735,7 @@ class CallManager private constructor() {
|
|
|
784
735
|
}
|
|
785
736
|
|
|
786
737
|
/** Logs Core-Telecom control action failures at error level. */
|
|
787
|
-
private fun logIfError(
|
|
788
|
-
id: UUID,
|
|
789
|
-
action: String,
|
|
790
|
-
result: CallControlResult,
|
|
791
|
-
) {
|
|
738
|
+
private fun logIfError(id: UUID, action: String, result: CallControlResult) {
|
|
792
739
|
if (result is CallControlResult.Error) {
|
|
793
740
|
CallKitTelecomLog.e(TAG) { "$action failed - id: $id, error: ${result.errorCode}" }
|
|
794
741
|
}
|
|
@@ -797,17 +744,16 @@ class CallManager private constructor() {
|
|
|
797
744
|
/**
|
|
798
745
|
* Collects Core-Telecom endpoint and mute flows within the CallControlScope.
|
|
799
746
|
*
|
|
800
|
-
* These coroutines keep the addCall block alive and forward audio state
|
|
801
|
-
*
|
|
747
|
+
* These coroutines keep the addCall block alive and forward audio state changes to
|
|
748
|
+
* CallAudioManager and CallStore.
|
|
802
749
|
*/
|
|
803
|
-
private fun CoroutineScope.collectCallFlows(
|
|
804
|
-
id: UUID,
|
|
805
|
-
callScope: CallControlScope,
|
|
806
|
-
) {
|
|
750
|
+
private fun CoroutineScope.collectCallFlows(id: UUID, callScope: CallControlScope) {
|
|
807
751
|
launch {
|
|
808
752
|
callScope.availableEndpoints.collect { endpoints ->
|
|
809
753
|
CallStore.session(id) ?: return@collect
|
|
810
|
-
CallKitTelecomLog.d(TAG) {
|
|
754
|
+
CallKitTelecomLog.d(TAG) {
|
|
755
|
+
"Available endpoints changed - id: $id, count: ${endpoints.size}"
|
|
756
|
+
}
|
|
811
757
|
CallAudioManager.onAvailableEndpointsChanged(endpoints)
|
|
812
758
|
}
|
|
813
759
|
}
|
|
@@ -815,7 +761,9 @@ class CallManager private constructor() {
|
|
|
815
761
|
launch {
|
|
816
762
|
callScope.currentCallEndpoint.collect { endpoint ->
|
|
817
763
|
CallStore.session(id) ?: return@collect
|
|
818
|
-
CallKitTelecomLog.d(TAG) {
|
|
764
|
+
CallKitTelecomLog.d(TAG) {
|
|
765
|
+
"Endpoint changed - id: $id, type: ${endpoint.type}, name: ${endpoint.name}"
|
|
766
|
+
}
|
|
819
767
|
CallAudioManager.onEndpointChanged(endpoint)
|
|
820
768
|
}
|
|
821
769
|
}
|
|
@@ -828,10 +776,7 @@ class CallManager private constructor() {
|
|
|
828
776
|
CallStore.updateMuted(id, muted)
|
|
829
777
|
CallEventEmitter.send(
|
|
830
778
|
CallEvents.SET_MUTED_ACTION,
|
|
831
|
-
mapOf(
|
|
832
|
-
"id" to id.toString(),
|
|
833
|
-
"isMuted" to muted,
|
|
834
|
-
),
|
|
779
|
+
mapOf("id" to id.toString(), "isMuted" to muted),
|
|
835
780
|
)
|
|
836
781
|
}
|
|
837
782
|
}
|
|
@@ -865,10 +810,7 @@ class CallManager private constructor() {
|
|
|
865
810
|
}
|
|
866
811
|
|
|
867
812
|
val request =
|
|
868
|
-
FulfillRequestManager.createRequest(
|
|
869
|
-
callId = id,
|
|
870
|
-
timeoutMs = fulfillAnswerTimeoutMs,
|
|
871
|
-
) {
|
|
813
|
+
FulfillRequestManager.createRequest(callId = id, timeoutMs = fulfillAnswerTimeoutMs) {
|
|
872
814
|
reportCallEnded(it, CallEndedReason.FAILED)
|
|
873
815
|
}
|
|
874
816
|
|
|
@@ -876,10 +818,7 @@ class CallManager private constructor() {
|
|
|
876
818
|
|
|
877
819
|
CallEventEmitter.send(
|
|
878
820
|
CallEvents.CALL_ANSWERED,
|
|
879
|
-
mapOf(
|
|
880
|
-
"id" to id.toString(),
|
|
881
|
-
"requestId" to request.requestId.toString(),
|
|
882
|
-
),
|
|
821
|
+
mapOf("id" to id.toString(), "requestId" to request.requestId.toString()),
|
|
883
822
|
)
|
|
884
823
|
}
|
|
885
824
|
|