@qusaieilouti99/call-manager 0.1.51 → 0.1.53

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,10 @@ 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
66
72
 
67
73
  // Lock Screen Bypass
68
74
  private var lockScreenBypassActive = false
@@ -80,7 +86,8 @@ object CallEngine {
80
86
  val callData: String,
81
87
  var state: CallState,
82
88
  val callType: String = "Audio",
83
- val timestamp: Long = System.currentTimeMillis()
89
+ val timestamp: Long = System.currentTimeMillis(),
90
+ var wasHeldBySystem: Boolean = false
84
91
  )
85
92
 
86
93
  enum class CallState {
@@ -91,8 +98,121 @@ object CallEngine {
91
98
  fun onLockScreenBypassChanged(shouldBypass: Boolean)
92
99
  }
93
100
 
94
- interface ServerCallRejectCallback {
95
- fun onRejectCall(callId: String, reason: String)
101
+ // --- Audio Focus Management ---
102
+ private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
103
+ Log.d(TAG, "Audio focus changed: $focusChange")
104
+ when (focusChange) {
105
+ AudioManager.AUDIOFOCUS_LOSS -> {
106
+ handleAudioFocusLoss()
107
+ }
108
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
109
+ handleAudioFocusLoss()
110
+ }
111
+ AudioManager.AUDIOFOCUS_GAIN -> {
112
+ handleAudioFocusGain()
113
+ }
114
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
115
+ // Can duck audio - don't hold call
116
+ Log.d(TAG, "Audio focus loss - can duck, not holding call")
117
+ }
118
+ }
119
+ }
120
+
121
+ private fun handleAudioFocusLoss() {
122
+ Log.d(TAG, "Audio focus lost - likely system call active")
123
+ hasAudioFocus = false
124
+ isSystemCallActive = true
125
+
126
+ // Hold all active calls instead of ending them
127
+ activeCalls.values.filter { it.state == CallState.ACTIVE }.forEach { call ->
128
+ if (!call.wasHeldBySystem) {
129
+ call.wasHeldBySystem = true
130
+ call.state = CallState.HELD
131
+
132
+ val connection = telecomConnections[call.callId]
133
+ connection?.setOnHold()
134
+
135
+ emitEvent(CallEventType.CALL_HELD, JSONObject().apply {
136
+ put("callId", call.callId)
137
+ })
138
+
139
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.HELD)
140
+ Log.d(TAG, "Call ${call.callId} held by system due to audio focus loss")
141
+ }
142
+ }
143
+
144
+ stopRingback()
145
+ updateForegroundNotification(appContext!!)
146
+ }
147
+
148
+ private fun handleAudioFocusGain() {
149
+ Log.d(TAG, "Audio focus regained - system call likely ended")
150
+ hasAudioFocus = true
151
+ isSystemCallActive = false
152
+
153
+ // Automatically resume calls that were held by system after a short delay
154
+ Handler(Looper.getMainLooper()).postDelayed({
155
+ activeCalls.values.filter { it.state == CallState.HELD && it.wasHeldBySystem }.forEach { call ->
156
+ Log.d(TAG, "Auto-resuming call ${call.callId} after system call ended")
157
+ call.wasHeldBySystem = false
158
+ call.state = CallState.ACTIVE
159
+
160
+ val connection = telecomConnections[call.callId]
161
+ connection?.setActive()
162
+
163
+ emitEvent(CallEventType.CALL_UNHELD, JSONObject().apply {
164
+ put("callId", call.callId)
165
+ })
166
+
167
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.ACTIVE)
168
+ }
169
+ updateForegroundNotification(appContext!!)
170
+ }, 1000) // 1 second delay to ensure system is ready
171
+ }
172
+
173
+ private fun requestAudioFocus(): Boolean {
174
+ audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
175
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
176
+ if (audioFocusRequest == null) {
177
+ audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
178
+ .setAudioAttributes(
179
+ AudioAttributes.Builder()
180
+ .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
181
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
182
+ .build()
183
+ )
184
+ .setOnAudioFocusChangeListener(audioFocusChangeListener)
185
+ .build()
186
+ }
187
+ val result = audioManager?.requestAudioFocus(audioFocusRequest!!)
188
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
189
+ Log.d(TAG, "Audio focus request result: $result")
190
+ hasAudioFocus
191
+ } else {
192
+ @Suppress("DEPRECATION")
193
+ val result = audioManager?.requestAudioFocus(
194
+ audioFocusChangeListener,
195
+ AudioManager.STREAM_VOICE_CALL,
196
+ AudioManager.AUDIOFOCUS_GAIN
197
+ )
198
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
199
+ Log.d(TAG, "Audio focus request result (legacy): $result")
200
+ hasAudioFocus
201
+ }
202
+ }
203
+
204
+ private fun abandonAudioFocus() {
205
+ audioManager = audioManager ?: appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
206
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
207
+ audioFocusRequest?.let { request ->
208
+ audioManager?.abandonAudioFocusRequest(request)
209
+ }
210
+ } else {
211
+ @Suppress("DEPRECATION")
212
+ audioManager?.abandonAudioFocus(audioFocusChangeListener)
213
+ }
214
+ hasAudioFocus = false
215
+ Log.d(TAG, "Audio focus abandoned")
96
216
  }
97
217
 
98
218
  // --- Event System ---
@@ -192,15 +312,13 @@ object CallEngine {
192
312
  val incomingCall = activeCalls.values.find { it.state == CallState.INCOMING }
193
313
  if (incomingCall != null && incomingCall.callId != callId) {
194
314
  Log.d(TAG, "Incoming call collision detected. Auto-rejecting new call: $callId")
195
-
196
- // Auto-reject the new call
197
315
  rejectIncomingCallCollision(callId, "Another call is already incoming")
198
316
  return
199
317
  }
200
318
 
201
319
  // Check if there's an active call when receiving incoming
202
- val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE }
203
- if (activeCall != null) {
320
+ val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE || it.state == CallState.HELD }
321
+ if (activeCall != null && !canMakeMultipleCalls) {
204
322
  Log.d(TAG, "Active call exists when receiving incoming call. Auto-rejecting: $callId")
205
323
  rejectIncomingCallCollision(callId, "Another call is already active")
206
324
  return
@@ -212,7 +330,11 @@ object CallEngine {
212
330
 
213
331
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
214
332
  Log.d(TAG, "Can't make multiple calls, holding existing calls.")
215
- activeCalls.values.forEach { it.state = CallState.HELD }
333
+ activeCalls.values.forEach {
334
+ if (it.state == CallState.ACTIVE) {
335
+ it.state = CallState.HELD
336
+ }
337
+ }
216
338
  }
217
339
 
218
340
  activeCalls[callId] = CallInfo(callId, callData, CallState.INCOMING, parsedCallType)
@@ -266,7 +388,11 @@ object CallEngine {
266
388
 
267
389
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
268
390
  Log.d(TAG, "Can't make multiple calls, holding existing calls before outgoing.")
269
- activeCalls.values.forEach { it.state = CallState.HELD }
391
+ activeCalls.values.forEach {
392
+ if (it.state == CallState.ACTIVE) {
393
+ it.state = CallState.HELD
394
+ }
395
+ }
270
396
  }
271
397
 
272
398
  activeCalls[callId] = CallInfo(callId, callData, CallState.DIALING, parsedCallType)
@@ -290,7 +416,10 @@ object CallEngine {
290
416
  startForegroundService(context)
291
417
  Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
292
418
 
419
+ // Request audio focus and start ringback
420
+ requestAudioFocus()
293
421
  startRingback()
422
+
294
423
  bringAppToForeground(context)
295
424
  keepScreenAwake(context, true)
296
425
  setInitialAudioRoute(context, parsedCallType)
@@ -331,12 +460,19 @@ object CallEngine {
331
460
  stopRingback()
332
461
  cancelIncomingCallUI(context)
333
462
 
463
+ // Request audio focus when answering
464
+ requestAudioFocus()
465
+
334
466
  // Update call state
335
467
  activeCalls[callId]?.state = CallState.ACTIVE
336
468
  currentCallId = callId
337
469
 
338
470
  if (!canMakeMultipleCalls) {
339
- activeCalls.filter { it.key != callId }.values.forEach { it.state = CallState.HELD }
471
+ activeCalls.filter { it.key != callId }.values.forEach {
472
+ if (it.state == CallState.ACTIVE) {
473
+ it.state = CallState.HELD
474
+ }
475
+ }
340
476
  }
341
477
 
342
478
  // Bring app to foreground when call is answered
@@ -366,7 +502,7 @@ object CallEngine {
366
502
  Log.d(TAG, "holdCall: $callId")
367
503
  val callInfo = activeCalls[callId]
368
504
  if (callInfo?.state != CallState.ACTIVE) {
369
- Log.w(TAG, "Cannot hold call $callId - not in active state")
505
+ Log.w(TAG, "Cannot hold call $callId - not in active state (current: ${callInfo?.state})")
370
506
  return
371
507
  }
372
508
 
@@ -384,11 +520,19 @@ object CallEngine {
384
520
  Log.d(TAG, "unholdCall: $callId")
385
521
  val callInfo = activeCalls[callId]
386
522
  if (callInfo?.state != CallState.HELD) {
387
- Log.w(TAG, "Cannot unhold call $callId - not in held state")
523
+ Log.w(TAG, "Cannot unhold call $callId - not in held state (current: ${callInfo?.state})")
388
524
  return
389
525
  }
390
526
 
527
+ // Try to request audio focus, but don't fail if we can't get it immediately
528
+ // The system will handle audio routing properly
529
+ if (!hasAudioFocus) {
530
+ Log.d(TAG, "Attempting to request audio focus for unhold")
531
+ requestAudioFocus()
532
+ }
533
+
391
534
  activeCalls[callId]?.state = CallState.ACTIVE
535
+ activeCalls[callId]?.wasHeldBySystem = false
392
536
  val connection = telecomConnections[callId]
393
537
  connection?.setActive()
394
538
 
@@ -396,6 +540,8 @@ object CallEngine {
396
540
  emitEvent(CallEventType.CALL_UNHELD, JSONObject().put("callId", callId))
397
541
  updateLockScreenBypass()
398
542
  notifySpecificCallStateChanged(context, callId, CallState.ACTIVE)
543
+
544
+ Log.d(TAG, "Call $callId successfully unheld")
399
545
  }
400
546
 
401
547
  fun muteCall(context: Context, callId: String) {
@@ -622,6 +768,7 @@ object CallEngine {
622
768
  audioManager?.stopBluetoothSco()
623
769
  audioManager?.isBluetoothScoOn = false
624
770
  audioManager?.isSpeakerphoneOn = false
771
+ abandonAudioFocus()
625
772
  } else {
626
773
  Log.d(TAG, "Audio mode not reset; ${activeCalls.size} calls still active.")
627
774
  }
@@ -700,7 +847,8 @@ object CallEngine {
700
847
  fun isCallActive(): Boolean = activeCalls.any {
701
848
  it.value.state == CallState.ACTIVE ||
702
849
  it.value.state == CallState.INCOMING ||
703
- it.value.state == CallState.DIALING
850
+ it.value.state == CallState.DIALING ||
851
+ it.value.state == CallState.HELD
704
852
  }
705
853
 
706
854
  private fun validateOutgoingCallRequest(): Boolean {
@@ -730,8 +878,6 @@ object CallEngine {
730
878
  CoroutineScope(Dispatchers.IO).launch {
731
879
  try {
732
880
  // TODO: Add your server HTTP request here
733
- // Example:
734
- // ApiService.rejectCall(callId, reason)
735
881
  Log.d(TAG, "Server rejection request would be made here for callId: $callId, reason: $reason")
736
882
  } catch (e: Exception) {
737
883
  Log.e(TAG, "Failed to send rejection to server", e)
@@ -857,7 +1003,8 @@ object CallEngine {
857
1003
 
858
1004
  // Find the current active call to pass its info
859
1005
  val currentCall = activeCalls.values.find {
860
- it.state == CallState.ACTIVE || it.state == CallState.INCOMING || it.state == CallState.DIALING
1006
+ it.state == CallState.ACTIVE || it.state == CallState.INCOMING ||
1007
+ it.state == CallState.DIALING || it.state == CallState.HELD
861
1008
  }
862
1009
 
863
1010
  val intent = Intent(context, CallForegroundService::class.java)
@@ -1053,5 +1200,6 @@ object CallEngine {
1053
1200
  stopForegroundService(context)
1054
1201
  keepScreenAwake(context, false)
1055
1202
  resetAudioMode(context)
1203
+ isSystemCallActive = false
1056
1204
  }
1057
1205
  }
@@ -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.53",
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'