@qusaieilouti99/call-manager 0.1.51 → 0.1.52

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.
@@ -13,6 +13,7 @@ import android.graphics.Color
13
13
  import android.media.AudioAttributes
14
14
  import android.media.AudioDeviceCallback
15
15
  import android.media.AudioDeviceInfo
16
+ import android.media.AudioFocusRequest
16
17
  import android.media.AudioManager
17
18
  import android.media.MediaPlayer
18
19
  import android.media.RingtoneManager
@@ -53,6 +54,7 @@ object CallEngine {
53
54
  private var audioManager: AudioManager? = null
54
55
  private var wakeLock: PowerManager.WakeLock? = null
55
56
  private var appContext: Context? = null
57
+ private var audioFocusRequest: AudioFocusRequest? = null
56
58
 
57
59
  // Call State Management
58
60
  private val activeCalls = ConcurrentHashMap<String, CallInfo>()
@@ -63,6 +65,11 @@ object CallEngine {
63
65
  // Audio State Tracking
64
66
  private var lastAudioRoutesInfo: AudioRoutesInfo? = null
65
67
  private var lastMuteState: Boolean = false
68
+ private var hasAudioFocus: Boolean = false
69
+
70
+ // System Call State Tracking
71
+ private var isSystemCallActive: Boolean = false
72
+ private var wasHeldBySystem: Boolean = false
66
73
 
67
74
  // Lock Screen Bypass
68
75
  private var lockScreenBypassActive = false
@@ -80,19 +87,147 @@ object CallEngine {
80
87
  val callData: String,
81
88
  var state: CallState,
82
89
  val callType: String = "Audio",
83
- val timestamp: Long = System.currentTimeMillis()
90
+ val timestamp: Long = System.currentTimeMillis(),
91
+ var wasHeldBySystem: Boolean = false
84
92
  )
85
93
 
86
94
  enum class CallState {
87
- INCOMING, DIALING, ACTIVE, HELD, ENDED
95
+ INCOMING, DIALING, ACTIVE, HELD, HELD_BY_SYSTEM, ENDED
88
96
  }
89
97
 
90
98
  interface LockScreenBypassCallback {
91
99
  fun onLockScreenBypassChanged(shouldBypass: Boolean)
92
100
  }
93
101
 
94
- interface ServerCallRejectCallback {
95
- fun onRejectCall(callId: String, reason: String)
102
+ // --- Audio Focus Management ---
103
+ private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
104
+ Log.d(TAG, "Audio focus changed: $focusChange")
105
+ when (focusChange) {
106
+ AudioManager.AUDIOFOCUS_LOSS -> {
107
+ // Lost focus permanently - likely due to phone call
108
+ handleAudioFocusLoss()
109
+ }
110
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
111
+ // Lost focus temporarily
112
+ handleAudioFocusLossTransient()
113
+ }
114
+ AudioManager.AUDIOFOCUS_GAIN -> {
115
+ // Regained focus
116
+ handleAudioFocusGain()
117
+ }
118
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
119
+ // Can duck audio
120
+ handleAudioFocusLossCanDuck()
121
+ }
122
+ }
123
+ }
124
+
125
+ private fun handleAudioFocusLoss() {
126
+ Log.d(TAG, "Audio focus lost permanently - likely system call active")
127
+ hasAudioFocus = false
128
+ isSystemCallActive = true
129
+
130
+ // Hold all active calls instead of ending them
131
+ activeCalls.values.filter { it.state == CallState.ACTIVE }.forEach { call ->
132
+ if (!call.wasHeldBySystem) {
133
+ call.wasHeldBySystem = true
134
+ call.state = CallState.HELD_BY_SYSTEM
135
+
136
+ val connection = telecomConnections[call.callId]
137
+ connection?.setOnHold()
138
+
139
+ emitEvent(CallEventType.CALL_HELD, JSONObject().apply {
140
+ put("callId", call.callId)
141
+ put("reason", "system_call_active")
142
+ })
143
+
144
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.HELD_BY_SYSTEM)
145
+ Log.d(TAG, "Call ${call.callId} held by system due to audio focus loss")
146
+ }
147
+ }
148
+
149
+ stopRingback()
150
+ updateForegroundNotification(appContext!!)
151
+ }
152
+
153
+ private fun handleAudioFocusLossTransient() {
154
+ // Similar to permanent loss but may be temporary
155
+ handleAudioFocusLoss()
156
+ }
157
+
158
+ private fun handleAudioFocusGain() {
159
+ Log.d(TAG, "Audio focus regained - system call likely ended")
160
+ hasAudioFocus = true
161
+ isSystemCallActive = false
162
+
163
+ // Resume calls that were held by system
164
+ activeCalls.values.filter { it.state == CallState.HELD_BY_SYSTEM && it.wasHeldBySystem }.forEach { call ->
165
+ call.wasHeldBySystem = false
166
+ call.state = CallState.ACTIVE
167
+
168
+ val connection = telecomConnections[call.callId]
169
+ connection?.setActive()
170
+
171
+ emitEvent(CallEventType.CALL_UNHELD, JSONObject().apply {
172
+ put("callId", call.callId)
173
+ put("reason", "system_call_ended")
174
+ })
175
+
176
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.ACTIVE)
177
+ Log.d(TAG, "Call ${call.callId} resumed after system call ended")
178
+ }
179
+
180
+ updateForegroundNotification(appContext!!)
181
+ }
182
+
183
+ private fun handleAudioFocusLossCanDuck() {
184
+ // Lower volume but continue
185
+ Log.d(TAG, "Audio focus loss - can duck")
186
+ }
187
+
188
+ private fun requestAudioFocus(): Boolean {
189
+ audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
190
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
191
+ if (audioFocusRequest == null) {
192
+ audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
193
+ .setAudioAttributes(
194
+ AudioAttributes.Builder()
195
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
196
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
197
+ .build()
198
+ )
199
+ .setOnAudioFocusChangeListener(audioFocusChangeListener)
200
+ .build()
201
+ }
202
+ val result = audioManager?.requestAudioFocus(audioFocusRequest!!)
203
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
204
+ Log.d(TAG, "Audio focus request result: $result")
205
+ hasAudioFocus
206
+ } else {
207
+ @Suppress("DEPRECATION")
208
+ val result = audioManager?.requestAudioFocus(
209
+ audioFocusChangeListener,
210
+ AudioManager.STREAM_VOICE_CALL,
211
+ AudioManager.AUDIOFOCUS_GAIN
212
+ )
213
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
214
+ Log.d(TAG, "Audio focus request result (legacy): $result")
215
+ hasAudioFocus
216
+ }
217
+ }
218
+
219
+ private fun abandonAudioFocus() {
220
+ audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
221
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
222
+ audioFocusRequest?.let { request ->
223
+ audioManager?.abandonAudioFocusRequest(request)
224
+ }
225
+ } else {
226
+ @Suppress("DEPRECATION")
227
+ audioManager?.abandonAudioFocus(audioFocusChangeListener)
228
+ }
229
+ hasAudioFocus = false
230
+ Log.d(TAG, "Audio focus abandoned")
96
231
  }
97
232
 
98
233
  // --- Event System ---
@@ -176,6 +311,7 @@ object CallEngine {
176
311
  obj.put("callData", it.callData)
177
312
  obj.put("state", it.state.name)
178
313
  obj.put("callType", it.callType)
314
+ obj.put("wasHeldBySystem", it.wasHeldBySystem)
179
315
  jsonArray.put(obj)
180
316
  }
181
317
  val result = jsonArray.toString()
@@ -192,15 +328,13 @@ object CallEngine {
192
328
  val incomingCall = activeCalls.values.find { it.state == CallState.INCOMING }
193
329
  if (incomingCall != null && incomingCall.callId != callId) {
194
330
  Log.d(TAG, "Incoming call collision detected. Auto-rejecting new call: $callId")
195
-
196
- // Auto-reject the new call
197
331
  rejectIncomingCallCollision(callId, "Another call is already incoming")
198
332
  return
199
333
  }
200
334
 
201
335
  // Check if there's an active call when receiving incoming
202
- val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE }
203
- if (activeCall != null) {
336
+ val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE || it.state == CallState.HELD_BY_SYSTEM }
337
+ if (activeCall != null && !canMakeMultipleCalls) {
204
338
  Log.d(TAG, "Active call exists when receiving incoming call. Auto-rejecting: $callId")
205
339
  rejectIncomingCallCollision(callId, "Another call is already active")
206
340
  return
@@ -212,7 +346,11 @@ object CallEngine {
212
346
 
213
347
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
214
348
  Log.d(TAG, "Can't make multiple calls, holding existing calls.")
215
- activeCalls.values.forEach { it.state = CallState.HELD }
349
+ activeCalls.values.forEach {
350
+ if (it.state == CallState.ACTIVE) {
351
+ it.state = CallState.HELD
352
+ }
353
+ }
216
354
  }
217
355
 
218
356
  activeCalls[callId] = CallInfo(callId, callData, CallState.INCOMING, parsedCallType)
@@ -266,7 +404,11 @@ object CallEngine {
266
404
 
267
405
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
268
406
  Log.d(TAG, "Can't make multiple calls, holding existing calls before outgoing.")
269
- activeCalls.values.forEach { it.state = CallState.HELD }
407
+ activeCalls.values.forEach {
408
+ if (it.state == CallState.ACTIVE) {
409
+ it.state = CallState.HELD
410
+ }
411
+ }
270
412
  }
271
413
 
272
414
  activeCalls[callId] = CallInfo(callId, callData, CallState.DIALING, parsedCallType)
@@ -290,10 +432,14 @@ object CallEngine {
290
432
  startForegroundService(context)
291
433
  Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
292
434
 
293
- startRingback()
435
+ // Request audio focus for outgoing call
436
+ if (requestAudioFocus()) {
437
+ startRingback()
438
+ setInitialAudioRoute(context, parsedCallType)
439
+ }
440
+
294
441
  bringAppToForeground(context)
295
442
  keepScreenAwake(context, true)
296
- setInitialAudioRoute(context, parsedCallType)
297
443
  } catch (e: SecurityException) {
298
444
  Log.e(TAG, "SecurityException: Failed to start outgoing call via placeCall. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
299
445
  endCall(context, callId)
@@ -331,12 +477,21 @@ object CallEngine {
331
477
  stopRingback()
332
478
  cancelIncomingCallUI(context)
333
479
 
480
+ // Request audio focus when answering
481
+ if (!hasAudioFocus) {
482
+ requestAudioFocus()
483
+ }
484
+
334
485
  // Update call state
335
486
  activeCalls[callId]?.state = CallState.ACTIVE
336
487
  currentCallId = callId
337
488
 
338
489
  if (!canMakeMultipleCalls) {
339
- activeCalls.filter { it.key != callId }.values.forEach { it.state = CallState.HELD }
490
+ activeCalls.filter { it.key != callId }.values.forEach {
491
+ if (it.state == CallState.ACTIVE) {
492
+ it.state = CallState.HELD
493
+ }
494
+ }
340
495
  }
341
496
 
342
497
  // Bring app to foreground when call is answered
@@ -366,7 +521,7 @@ object CallEngine {
366
521
  Log.d(TAG, "holdCall: $callId")
367
522
  val callInfo = activeCalls[callId]
368
523
  if (callInfo?.state != CallState.ACTIVE) {
369
- Log.w(TAG, "Cannot hold call $callId - not in active state")
524
+ Log.w(TAG, "Cannot hold call $callId - not in active state (current: ${callInfo?.state})")
370
525
  return
371
526
  }
372
527
 
@@ -383,12 +538,35 @@ object CallEngine {
383
538
  fun unholdCall(context: Context, callId: String) {
384
539
  Log.d(TAG, "unholdCall: $callId")
385
540
  val callInfo = activeCalls[callId]
386
- if (callInfo?.state != CallState.HELD) {
387
- Log.w(TAG, "Cannot unhold call $callId - not in held state")
541
+ if (callInfo?.state != CallState.HELD && callInfo?.state != CallState.HELD_BY_SYSTEM) {
542
+ Log.w(TAG, "Cannot unhold call $callId - not in held state (current: ${callInfo?.state})")
388
543
  return
389
544
  }
390
545
 
546
+ // If call was held by system, don't allow manual unhold until system allows it
547
+ if (callInfo.state == CallState.HELD_BY_SYSTEM && isSystemCallActive) {
548
+ Log.w(TAG, "Cannot unhold call $callId - held by system and system call still active")
549
+ emitEvent(CallEventType.CALL_UNHOLD_FAILED, JSONObject().apply {
550
+ put("callId", callId)
551
+ put("reason", "system_call_active")
552
+ })
553
+ return
554
+ }
555
+
556
+ // Request audio focus before unholding
557
+ if (!hasAudioFocus) {
558
+ if (!requestAudioFocus()) {
559
+ Log.w(TAG, "Cannot unhold call $callId - failed to gain audio focus")
560
+ emitEvent(CallEventType.CALL_UNHOLD_FAILED, JSONObject().apply {
561
+ put("callId", callId)
562
+ put("reason", "audio_focus_failed")
563
+ })
564
+ return
565
+ }
566
+ }
567
+
391
568
  activeCalls[callId]?.state = CallState.ACTIVE
569
+ activeCalls[callId]?.wasHeldBySystem = false
392
570
  val connection = telecomConnections[callId]
393
571
  connection?.setActive()
394
572
 
@@ -622,6 +800,7 @@ object CallEngine {
622
800
  audioManager?.stopBluetoothSco()
623
801
  audioManager?.isBluetoothScoOn = false
624
802
  audioManager?.isSpeakerphoneOn = false
803
+ abandonAudioFocus()
625
804
  } else {
626
805
  Log.d(TAG, "Audio mode not reset; ${activeCalls.size} calls still active.")
627
806
  }
@@ -700,7 +879,9 @@ object CallEngine {
700
879
  fun isCallActive(): Boolean = activeCalls.any {
701
880
  it.value.state == CallState.ACTIVE ||
702
881
  it.value.state == CallState.INCOMING ||
703
- it.value.state == CallState.DIALING
882
+ it.value.state == CallState.DIALING ||
883
+ it.value.state == CallState.HELD ||
884
+ it.value.state == CallState.HELD_BY_SYSTEM
704
885
  }
705
886
 
706
887
  private fun validateOutgoingCallRequest(): Boolean {
@@ -730,8 +911,6 @@ object CallEngine {
730
911
  CoroutineScope(Dispatchers.IO).launch {
731
912
  try {
732
913
  // TODO: Add your server HTTP request here
733
- // Example:
734
- // ApiService.rejectCall(callId, reason)
735
914
  Log.d(TAG, "Server rejection request would be made here for callId: $callId, reason: $reason")
736
915
  } catch (e: Exception) {
737
916
  Log.e(TAG, "Failed to send rejection to server", e)
@@ -857,7 +1036,9 @@ object CallEngine {
857
1036
 
858
1037
  // Find the current active call to pass its info
859
1038
  val currentCall = activeCalls.values.find {
860
- it.state == CallState.ACTIVE || it.state == CallState.INCOMING || it.state == CallState.DIALING
1039
+ it.state == CallState.ACTIVE || it.state == CallState.INCOMING ||
1040
+ it.state == CallState.DIALING || it.state == CallState.HELD ||
1041
+ it.state == CallState.HELD_BY_SYSTEM
861
1042
  }
862
1043
 
863
1044
  val intent = Intent(context, CallForegroundService::class.java)
@@ -1022,6 +1203,7 @@ object CallEngine {
1022
1203
  put("callData", callInfo.callData)
1023
1204
  put("state", newState.name)
1024
1205
  put("callType", callInfo.callType)
1206
+ put("wasHeldBySystem", callInfo.wasHeldBySystem)
1025
1207
  }
1026
1208
 
1027
1209
  Log.d(TAG, "Specific call state changed. Emitting CALL_STATE_CHANGED for $callId: $newState")
@@ -1031,8 +1213,9 @@ object CallEngine {
1031
1213
  private fun updateForegroundNotification(context: Context) {
1032
1214
  val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE }
1033
1215
  val heldCall = activeCalls.values.find { it.state == CallState.HELD }
1216
+ val heldBySystemCall = activeCalls.values.find { it.state == CallState.HELD_BY_SYSTEM }
1034
1217
 
1035
- val callToShow = activeCall ?: heldCall
1218
+ val callToShow = activeCall ?: heldCall ?: heldBySystemCall
1036
1219
  callToShow?.let {
1037
1220
  val intent = Intent(context, CallForegroundService::class.java)
1038
1221
  intent.putExtra("UPDATE_NOTIFICATION", true)
@@ -1053,5 +1236,8 @@ object CallEngine {
1053
1236
  stopForegroundService(context)
1054
1237
  keepScreenAwake(context, false)
1055
1238
  resetAudioMode(context)
1239
+ abandonAudioFocus()
1240
+ isSystemCallActive = false
1241
+ wasHeldBySystem = false
1056
1242
  }
1057
1243
  }
@@ -33,7 +33,9 @@ class MyConnection(
33
33
  } catch (e: Exception) { "Audio" }
34
34
 
35
35
  connectionProperties = Connection.PROPERTY_SELF_MANAGED
36
- connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or Connection.CAPABILITY_MUTE
36
+ connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or
37
+ Connection.CAPABILITY_MUTE or
38
+ Connection.CAPABILITY_HOLD
37
39
 
38
40
  if (currentCallType == "Video") {
39
41
  Log.d(TAG, "MyConnection for callId $callId initialized as VIDEO call.")
@@ -70,12 +72,18 @@ class MyConnection(
70
72
  override fun onHold() {
71
73
  super.onHold()
72
74
  Log.d(TAG, "Call held via Telecom for callId: $callId")
75
+
76
+ // This is called by the system when it wants to hold our call
77
+ // Usually happens when a phone call comes in
73
78
  CallEngine.holdCall(context, callId)
74
79
  }
75
80
 
76
81
  override fun onUnhold() {
77
82
  super.onUnhold()
78
83
  Log.d(TAG, "Call unheld via Telecom for callId: $callId")
84
+
85
+ // This is called by the system when it's safe to resume our call
86
+ // Usually happens when a phone call ends
79
87
  CallEngine.unholdCall(context, callId)
80
88
  }
81
89
 
@@ -130,4 +138,22 @@ class MyConnection(
130
138
  Log.d(TAG, "onShowIncomingCallUi for callId: $callId")
131
139
  // Don't bring app to foreground for incoming calls automatically
132
140
  }
141
+
142
+ override fun onStateChanged(state: Int) {
143
+ super.onStateChanged(state)
144
+ Log.d(TAG, "Connection state changed for callId: $callId. New state: $state")
145
+
146
+ when (state) {
147
+ STATE_HOLDING -> {
148
+ Log.d(TAG, "Connection is now holding for callId: $callId")
149
+ }
150
+ STATE_ACTIVE -> {
151
+ Log.d(TAG, "Connection is now active for callId: $callId")
152
+ }
153
+ STATE_DISCONNECTED -> {
154
+ Log.d(TAG, "Connection is now disconnected for callId: $callId")
155
+ CallEngine.removeTelecomConnection(callId)
156
+ }
157
+ }
158
+ }
133
159
  }
@@ -1,2 +1,2 @@
1
- export type CallEventType = 'INITIAL_CALL_STATE' | 'CALL_STATE_CHANGED' | 'AUDIO_DEVICES_CHANGED' | 'AUDIO_ROUTE_CHANGED' | 'CALL_HELD' | 'CALL_UNHELD' | 'CALL_MUTED' | 'CALL_UNMUTED' | 'CALL_ANSWERED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'DTMF_TONE';
1
+ export type CallEventType = 'INITIAL_CALL_STATE' | 'CALL_STATE_CHANGED' | 'AUDIO_DEVICES_CHANGED' | 'AUDIO_ROUTE_CHANGED' | 'CALL_HELD' | 'CALL_UNHELD' | 'CALL_UNHOLD_FAILED' | 'CALL_MUTED' | 'CALL_UNMUTED' | 'CALL_ANSWERED' | 'CALL_REJECTED' | 'CALL_ENDED' | 'DTMF_TONE';
2
2
  //# sourceMappingURL=CallEventType.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../../src/CallEventType.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,WAAW,GACX,aAAa,GACb,YAAY,GACZ,cAAc,GACd,eAAe,GACf,eAAe,GACf,YAAY,GACZ,WAAW,CAAC"}
1
+ {"version":3,"file":"CallEventType.d.ts","sourceRoot":"","sources":["../../../src/CallEventType.ts"],"names":[],"mappings":"AACA,MAAM,MAAM,aAAa,GACrB,oBAAoB,GACpB,oBAAoB,GACpB,uBAAuB,GACvB,qBAAqB,GACrB,WAAW,GACX,aAAa,GACb,oBAAoB,GACpB,YAAY,GACZ,cAAc,GACd,eAAe,GACf,eAAe,GACf,YAAY,GACZ,WAAW,CAAC"}
@@ -47,6 +47,7 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
47
47
  static const auto fieldAUDIO_ROUTE_CHANGED = clazz->getStaticField<JCallEventType>("AUDIO_ROUTE_CHANGED");
48
48
  static const auto fieldCALL_HELD = clazz->getStaticField<JCallEventType>("CALL_HELD");
49
49
  static const auto fieldCALL_UNHELD = clazz->getStaticField<JCallEventType>("CALL_UNHELD");
50
+ static const auto fieldCALL_UNHOLD_FAILED = clazz->getStaticField<JCallEventType>("CALL_UNHOLD_FAILED");
50
51
  static const auto fieldCALL_MUTED = clazz->getStaticField<JCallEventType>("CALL_MUTED");
51
52
  static const auto fieldCALL_UNMUTED = clazz->getStaticField<JCallEventType>("CALL_UNMUTED");
52
53
  static const auto fieldCALL_ANSWERED = clazz->getStaticField<JCallEventType>("CALL_ANSWERED");
@@ -67,6 +68,8 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
67
68
  return clazz->getStaticFieldValue(fieldCALL_HELD);
68
69
  case CallEventType::CALL_UNHELD:
69
70
  return clazz->getStaticFieldValue(fieldCALL_UNHELD);
71
+ case CallEventType::CALL_UNHOLD_FAILED:
72
+ return clazz->getStaticFieldValue(fieldCALL_UNHOLD_FAILED);
70
73
  case CallEventType::CALL_MUTED:
71
74
  return clazz->getStaticFieldValue(fieldCALL_MUTED);
72
75
  case CallEventType::CALL_UNMUTED:
@@ -22,6 +22,7 @@ enum class CallEventType {
22
22
  AUDIO_ROUTE_CHANGED,
23
23
  CALL_HELD,
24
24
  CALL_UNHELD,
25
+ CALL_UNHOLD_FAILED,
25
26
  CALL_MUTED,
26
27
  CALL_UNMUTED,
27
28
  CALL_ANSWERED,
@@ -29,6 +29,8 @@ public extension CallEventType {
29
29
  self = .callHeld
30
30
  case "CALL_UNHELD":
31
31
  self = .callUnheld
32
+ case "CALL_UNHOLD_FAILED":
33
+ self = .callUnholdFailed
32
34
  case "CALL_MUTED":
33
35
  self = .callMuted
34
36
  case "CALL_UNMUTED":
@@ -63,6 +65,8 @@ public extension CallEventType {
63
65
  return "CALL_HELD"
64
66
  case .callUnheld:
65
67
  return "CALL_UNHELD"
68
+ case .callUnholdFailed:
69
+ return "CALL_UNHOLD_FAILED"
66
70
  case .callMuted:
67
71
  return "CALL_MUTED"
68
72
  case .callUnmuted:
@@ -35,12 +35,13 @@ namespace margelo::nitro::qusaieilouti99_callmanager {
35
35
  AUDIO_ROUTE_CHANGED SWIFT_NAME(audioRouteChanged) = 3,
36
36
  CALL_HELD SWIFT_NAME(callHeld) = 4,
37
37
  CALL_UNHELD SWIFT_NAME(callUnheld) = 5,
38
- CALL_MUTED SWIFT_NAME(callMuted) = 6,
39
- CALL_UNMUTED SWIFT_NAME(callUnmuted) = 7,
40
- CALL_ANSWERED SWIFT_NAME(callAnswered) = 8,
41
- CALL_REJECTED SWIFT_NAME(callRejected) = 9,
42
- CALL_ENDED SWIFT_NAME(callEnded) = 10,
43
- DTMF_TONE SWIFT_NAME(dtmfTone) = 11,
38
+ CALL_UNHOLD_FAILED SWIFT_NAME(callUnholdFailed) = 6,
39
+ CALL_MUTED SWIFT_NAME(callMuted) = 7,
40
+ CALL_UNMUTED SWIFT_NAME(callUnmuted) = 8,
41
+ CALL_ANSWERED SWIFT_NAME(callAnswered) = 9,
42
+ CALL_REJECTED SWIFT_NAME(callRejected) = 10,
43
+ CALL_ENDED SWIFT_NAME(callEnded) = 11,
44
+ DTMF_TONE SWIFT_NAME(dtmfTone) = 12,
44
45
  } CLOSED_ENUM;
45
46
 
46
47
  } // namespace margelo::nitro::qusaieilouti99_callmanager
@@ -61,6 +62,7 @@ namespace margelo::nitro {
61
62
  case hashString("AUDIO_ROUTE_CHANGED"): return CallEventType::AUDIO_ROUTE_CHANGED;
62
63
  case hashString("CALL_HELD"): return CallEventType::CALL_HELD;
63
64
  case hashString("CALL_UNHELD"): return CallEventType::CALL_UNHELD;
65
+ case hashString("CALL_UNHOLD_FAILED"): return CallEventType::CALL_UNHOLD_FAILED;
64
66
  case hashString("CALL_MUTED"): return CallEventType::CALL_MUTED;
65
67
  case hashString("CALL_UNMUTED"): return CallEventType::CALL_UNMUTED;
66
68
  case hashString("CALL_ANSWERED"): return CallEventType::CALL_ANSWERED;
@@ -79,6 +81,7 @@ namespace margelo::nitro {
79
81
  case CallEventType::AUDIO_ROUTE_CHANGED: return JSIConverter<std::string>::toJSI(runtime, "AUDIO_ROUTE_CHANGED");
80
82
  case CallEventType::CALL_HELD: return JSIConverter<std::string>::toJSI(runtime, "CALL_HELD");
81
83
  case CallEventType::CALL_UNHELD: return JSIConverter<std::string>::toJSI(runtime, "CALL_UNHELD");
84
+ case CallEventType::CALL_UNHOLD_FAILED: return JSIConverter<std::string>::toJSI(runtime, "CALL_UNHOLD_FAILED");
82
85
  case CallEventType::CALL_MUTED: return JSIConverter<std::string>::toJSI(runtime, "CALL_MUTED");
83
86
  case CallEventType::CALL_UNMUTED: return JSIConverter<std::string>::toJSI(runtime, "CALL_UNMUTED");
84
87
  case CallEventType::CALL_ANSWERED: return JSIConverter<std::string>::toJSI(runtime, "CALL_ANSWERED");
@@ -102,6 +105,7 @@ namespace margelo::nitro {
102
105
  case hashString("AUDIO_ROUTE_CHANGED"):
103
106
  case hashString("CALL_HELD"):
104
107
  case hashString("CALL_UNHELD"):
108
+ case hashString("CALL_UNHOLD_FAILED"):
105
109
  case hashString("CALL_MUTED"):
106
110
  case hashString("CALL_UNMUTED"):
107
111
  case hashString("CALL_ANSWERED"):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.51",
3
+ "version": "0.1.52",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -6,6 +6,7 @@ export type CallEventType =
6
6
  | 'AUDIO_ROUTE_CHANGED'
7
7
  | 'CALL_HELD'
8
8
  | 'CALL_UNHELD'
9
+ | 'CALL_UNHOLD_FAILED'
9
10
  | 'CALL_MUTED'
10
11
  | 'CALL_UNMUTED'
11
12
  | 'CALL_ANSWERED'