expo-callkit-telecom 0.1.0

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.
Files changed (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/android/build.gradle +32 -0
  4. package/android/src/main/AndroidManifest.xml +33 -0
  5. package/android/src/main/java/expo/modules/callkittelecom/ExpoCallKitTelecomModule.kt +384 -0
  6. package/android/src/main/java/expo/modules/callkittelecom/IncomingCallActivity.kt +275 -0
  7. package/android/src/main/java/expo/modules/callkittelecom/events/CallEventEmitter.kt +151 -0
  8. package/android/src/main/java/expo/modules/callkittelecom/events/CallEvents.kt +59 -0
  9. package/android/src/main/java/expo/modules/callkittelecom/managers/CallAudioManager.kt +361 -0
  10. package/android/src/main/java/expo/modules/callkittelecom/managers/CallManager.kt +891 -0
  11. package/android/src/main/java/expo/modules/callkittelecom/managers/CallNotificationManager.kt +445 -0
  12. package/android/src/main/java/expo/modules/callkittelecom/managers/CaptureSessionManager.kt +27 -0
  13. package/android/src/main/java/expo/modules/callkittelecom/managers/DialtonePlayer.kt +171 -0
  14. package/android/src/main/java/expo/modules/callkittelecom/managers/FulfillRequestManager.kt +150 -0
  15. package/android/src/main/java/expo/modules/callkittelecom/managers/VoIPPushManager.kt +54 -0
  16. package/android/src/main/java/expo/modules/callkittelecom/models/CallModels.kt +269 -0
  17. package/android/src/main/java/expo/modules/callkittelecom/services/CallNotificationReceiver.kt +54 -0
  18. package/android/src/main/java/expo/modules/callkittelecom/services/ExpoCallKitTelecomMessagingService.kt +161 -0
  19. package/android/src/main/java/expo/modules/callkittelecom/store/CallStore.kt +181 -0
  20. package/android/src/main/java/expo/modules/callkittelecom/utils/CallKitTelecomLog.kt +52 -0
  21. package/android/src/main/java/expo/modules/callkittelecom/utils/PermissionUtils.kt +28 -0
  22. package/android/src/main/res/drawable/expo_callkit_telecom_bg_answer.xml +9 -0
  23. package/android/src/main/res/drawable/expo_callkit_telecom_bg_avatar.xml +5 -0
  24. package/android/src/main/res/drawable/expo_callkit_telecom_bg_decline.xml +9 -0
  25. package/android/src/main/res/drawable/expo_callkit_telecom_ic_answer.xml +9 -0
  26. package/android/src/main/res/drawable/expo_callkit_telecom_ic_decline.xml +9 -0
  27. package/android/src/main/res/drawable/expo_callkit_telecom_ic_videocam.xml +9 -0
  28. package/android/src/main/res/layout/activity_incoming_call.xml +169 -0
  29. package/app.json +8 -0
  30. package/app.plugin.js +1 -0
  31. package/build/Calls.d.ts +577 -0
  32. package/build/Calls.d.ts.map +1 -0
  33. package/build/Calls.js +715 -0
  34. package/build/Calls.js.map +1 -0
  35. package/build/Calls.types.d.ts +203 -0
  36. package/build/Calls.types.d.ts.map +1 -0
  37. package/build/Calls.types.js +2 -0
  38. package/build/Calls.types.js.map +1 -0
  39. package/build/ExpoCallKitTelecomModule.d.ts +3 -0
  40. package/build/ExpoCallKitTelecomModule.d.ts.map +1 -0
  41. package/build/ExpoCallKitTelecomModule.js +4 -0
  42. package/build/ExpoCallKitTelecomModule.js.map +1 -0
  43. package/build/hooks/index.d.ts +2 -0
  44. package/build/hooks/index.d.ts.map +1 -0
  45. package/build/hooks/index.js +2 -0
  46. package/build/hooks/index.js.map +1 -0
  47. package/build/hooks/useVoIPPushToken.d.ts +14 -0
  48. package/build/hooks/useVoIPPushToken.d.ts.map +1 -0
  49. package/build/hooks/useVoIPPushToken.js +26 -0
  50. package/build/hooks/useVoIPPushToken.js.map +1 -0
  51. package/build/index.d.ts +4 -0
  52. package/build/index.d.ts.map +1 -0
  53. package/build/index.js +4 -0
  54. package/build/index.js.map +1 -0
  55. package/expo-module.config.json +10 -0
  56. package/ios/AppDelegateSubscriber.swift +93 -0
  57. package/ios/ExpoCallKitTelecom.podspec +31 -0
  58. package/ios/ExpoCallKitTelecomLogger.swift +55 -0
  59. package/ios/ExpoCallKitTelecomModule.swift +503 -0
  60. package/ios/Managers/AudioManager.swift +363 -0
  61. package/ios/Managers/CallEventEmitter.swift +199 -0
  62. package/ios/Managers/CallManager+CXProviderDelegate.swift +195 -0
  63. package/ios/Managers/CallManager.swift +714 -0
  64. package/ios/Managers/CaptureSessionManager.swift +54 -0
  65. package/ios/Managers/DialtonePlayer.swift +126 -0
  66. package/ios/Managers/FulfillRequestManager.swift +154 -0
  67. package/ios/Managers/VoIPPushManager+PKPushRegistryDelegate.swift +123 -0
  68. package/ios/Managers/VoIPPushManager.swift +58 -0
  69. package/ios/Models/CallEvents.swift +263 -0
  70. package/ios/Models/CallOptions.swift +15 -0
  71. package/ios/Models/CallParticipant.swift +37 -0
  72. package/ios/Models/CallSession.swift +80 -0
  73. package/ios/Models/IncomingCallEvent.swift +196 -0
  74. package/ios/Stores/CallStore.swift +149 -0
  75. package/package.json +56 -0
  76. package/plugin/build/constants.d.ts +3 -0
  77. package/plugin/build/constants.js +7 -0
  78. package/plugin/build/withExpoCallKitTelecom.d.ts +67 -0
  79. package/plugin/build/withExpoCallKitTelecom.js +16 -0
  80. package/plugin/build/withExpoCallKitTelecomAndroid.d.ts +3 -0
  81. package/plugin/build/withExpoCallKitTelecomAndroid.js +177 -0
  82. package/plugin/build/withExpoCallKitTelecomIos.d.ts +3 -0
  83. package/plugin/build/withExpoCallKitTelecomIos.js +195 -0
  84. package/plugin/src/constants.ts +4 -0
  85. package/plugin/src/withExpoCallKitTelecom.ts +83 -0
  86. package/plugin/src/withExpoCallKitTelecomAndroid.ts +293 -0
  87. package/plugin/src/withExpoCallKitTelecomIos.ts +276 -0
  88. package/src/Calls.ts +848 -0
  89. package/src/Calls.types.ts +275 -0
  90. package/src/ExpoCallKitTelecomModule.ts +4 -0
  91. package/src/hooks/index.ts +1 -0
  92. package/src/hooks/useVoIPPushToken.ts +34 -0
  93. package/src/index.ts +3 -0
@@ -0,0 +1,275 @@
1
+ package expo.modules.callkittelecom
2
+
3
+ import android.app.Activity
4
+ import android.app.KeyguardManager
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.graphics.BitmapFactory
8
+ import android.os.Bundle
9
+ import android.view.View
10
+ import android.view.WindowManager
11
+ import android.widget.ImageButton
12
+ import android.widget.ImageView
13
+ import android.widget.TextView
14
+ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
15
+ import expo.modules.callkittelecom.managers.CallManager
16
+ import expo.modules.callkittelecom.models.CallSessionStatus
17
+ import expo.modules.callkittelecom.store.CallStore
18
+ import expo.modules.callkittelecom.utils.CallKitTelecomLog
19
+ import kotlinx.coroutines.CoroutineScope
20
+ import kotlinx.coroutines.Dispatchers
21
+ import kotlinx.coroutines.SupervisorJob
22
+ import kotlinx.coroutines.cancel
23
+ import kotlinx.coroutines.launch
24
+ import kotlinx.coroutines.withContext
25
+ import java.net.HttpURLConnection
26
+ import java.net.URL
27
+ import java.util.UUID
28
+
29
+ /**
30
+ * Native full-screen incoming call Activity displayed over the lock screen.
31
+ *
32
+ * Shows caller information with answer/decline buttons. Automatically
33
+ * dismisses when the call leaves the RINGING state (answered, declined,
34
+ * timed out, or ended elsewhere).
35
+ *
36
+ * Answer flow: answers the call directly, dismisses the keyguard via
37
+ * [KeyguardManager.requestDismissKeyguard], then launches the main Activity
38
+ * so the user sees the in-call UI after unlocking.
39
+ */
40
+ class IncomingCallActivity : Activity() {
41
+ companion object {
42
+ private const val TAG = "ExpoCallKitTelecom.IncomingCallActivity"
43
+ const val EXTRA_CALL_ID = "expo.modules.callkittelecom.EXTRA_CALL_ID"
44
+ }
45
+
46
+ private var callId: UUID? = null
47
+ private var isAnswering = false
48
+ private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
49
+
50
+ override fun onCreate(savedInstanceState: Bundle?) {
51
+ super.onCreate(savedInstanceState)
52
+
53
+ configureWindowForLockScreen()
54
+ setContentView(R.layout.activity_incoming_call)
55
+
56
+ val id = parseCallId() ?: return
57
+ callId = id
58
+
59
+ val session = CallStore.session(id)
60
+ if (session == null || session.status != CallSessionStatus.RINGING) {
61
+ CallKitTelecomLog.d(TAG) { "No ringing session for $id, finishing" }
62
+ finish()
63
+ return
64
+ }
65
+
66
+ val caller = session.remoteParticipants.firstOrNull()
67
+
68
+ bindAppBranding()
69
+ bindCallerInfo(caller?.displayName, session.options.hasVideo)
70
+ loadAvatar(caller?.avatarUrl)
71
+ bindButtons(id, session.options.hasVideo)
72
+ observeSessionChanges(id)
73
+
74
+ CallKitTelecomLog.d(TAG) { "Showing incoming call UI - callId: $id" }
75
+ }
76
+
77
+ @Suppress("DEPRECATION")
78
+ private fun configureWindowForLockScreen() {
79
+ setShowWhenLocked(true)
80
+ setTurnScreenOn(true)
81
+ window.addFlags(
82
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
83
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
84
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
85
+ )
86
+ }
87
+
88
+ private fun parseCallId(): UUID? {
89
+ val callIdStr = intent.getStringExtra(EXTRA_CALL_ID)
90
+ return try {
91
+ UUID.fromString(callIdStr)
92
+ } catch (_: Exception) {
93
+ CallKitTelecomLog.e(TAG) { "Invalid or missing call ID: $callIdStr" }
94
+ finish()
95
+ null
96
+ }
97
+ }
98
+
99
+ private fun bindAppBranding() {
100
+ try {
101
+ val appInfo = packageManager.getApplicationInfo(packageName, 0)
102
+ findViewById<ImageView>(R.id.expo_callkit_telecom_app_icon).setImageDrawable(
103
+ packageManager.getApplicationIcon(appInfo),
104
+ )
105
+ findViewById<TextView>(R.id.expo_callkit_telecom_app_name).text =
106
+ packageManager.getApplicationLabel(appInfo)
107
+ } catch (_: Exception) {
108
+ // Non-critical — branding just won't show
109
+ }
110
+ }
111
+
112
+ private fun bindCallerInfo(
113
+ displayName: String?,
114
+ hasVideo: Boolean,
115
+ ) {
116
+ val name = displayName ?: "Unknown"
117
+
118
+ findViewById<TextView>(R.id.expo_callkit_telecom_avatar_text).text =
119
+ name.firstOrNull()?.uppercase() ?: "?"
120
+
121
+ findViewById<TextView>(R.id.expo_callkit_telecom_caller_name).text = name
122
+
123
+ findViewById<TextView>(R.id.expo_callkit_telecom_subtitle).text =
124
+ if (hasVideo) "Incoming video call" else "Incoming call"
125
+ }
126
+
127
+ /**
128
+ * Loads the caller's avatar from [avatarUrl] on a background thread.
129
+ * On success, displays a circular-cropped image and hides the initial letter.
130
+ * On failure, silently keeps the initial letter fallback.
131
+ */
132
+ private fun loadAvatar(avatarUrl: String?) {
133
+ if (avatarUrl.isNullOrBlank()) return
134
+
135
+ val avatarImage = findViewById<ImageView>(R.id.expo_callkit_telecom_avatar_image)
136
+ val avatarText = findViewById<TextView>(R.id.expo_callkit_telecom_avatar_text)
137
+
138
+ scope.launch {
139
+ val drawable =
140
+ withContext(Dispatchers.IO) {
141
+ try {
142
+ val connection = URL(avatarUrl).openConnection() as HttpURLConnection
143
+ connection.connectTimeout = 5_000
144
+ connection.readTimeout = 5_000
145
+ val bitmap =
146
+ connection.inputStream.use { stream ->
147
+ BitmapFactory.decodeStream(stream)
148
+ }
149
+ bitmap?.let {
150
+ RoundedBitmapDrawableFactory.create(resources, it).apply {
151
+ isCircular = true
152
+ }
153
+ }
154
+ } catch (e: Exception) {
155
+ CallKitTelecomLog.d(TAG) { "Avatar load failed: ${e.message}" }
156
+ null
157
+ }
158
+ }
159
+
160
+ if (drawable != null) {
161
+ avatarImage.setImageDrawable(drawable)
162
+ avatarImage.visibility = View.VISIBLE
163
+ avatarText.visibility = View.GONE
164
+ }
165
+ }
166
+ }
167
+
168
+ private fun bindButtons(
169
+ id: UUID,
170
+ hasVideo: Boolean,
171
+ ) {
172
+ val answerButton = findViewById<ImageButton>(R.id.expo_callkit_telecom_answer_button)
173
+ if (hasVideo) {
174
+ answerButton.setImageResource(R.drawable.expo_callkit_telecom_ic_videocam)
175
+ }
176
+ answerButton.setOnClickListener { onAnswerTapped(id) }
177
+
178
+ findViewById<ImageButton>(R.id.expo_callkit_telecom_decline_button).setOnClickListener {
179
+ onDeclineTapped(id)
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Answers the call directly, then dismisses the keyguard and launches
185
+ * the main Activity so the user transitions into the in-call UI.
186
+ *
187
+ * The call is answered immediately (media connection starts) regardless
188
+ * of whether the keyguard dismissal succeeds. This matches the behavior
189
+ * of iOS CallKit where audio connects before the device is unlocked.
190
+ */
191
+ private fun onAnswerTapped(id: UUID) {
192
+ if (isAnswering) return
193
+ isAnswering = true
194
+ CallKitTelecomLog.d(TAG) { "Answer tapped - callId: $id" }
195
+
196
+ // Answer immediately — don't wait for keyguard dismissal
197
+ CallManager.shared.answerCall(id)
198
+
199
+ // Dismiss keyguard, then bring the main app to the foreground
200
+ val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
201
+ keyguardManager.requestDismissKeyguard(
202
+ this,
203
+ object : KeyguardManager.KeyguardDismissCallback() {
204
+ override fun onDismissSucceeded() {
205
+ launchMainActivity()
206
+ finish()
207
+ }
208
+
209
+ override fun onDismissCancelled() {
210
+ // User cancelled unlock — call is still answered, launch anyway
211
+ launchMainActivity()
212
+ finish()
213
+ }
214
+
215
+ override fun onDismissError() {
216
+ launchMainActivity()
217
+ finish()
218
+ }
219
+ },
220
+ )
221
+ }
222
+
223
+ private fun launchMainActivity() {
224
+ val intent =
225
+ packageManager.getLaunchIntentForPackage(packageName)?.apply {
226
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
227
+ }
228
+ if (intent != null) {
229
+ startActivity(intent)
230
+ }
231
+ }
232
+
233
+ private fun onDeclineTapped(id: UUID) {
234
+ CallKitTelecomLog.d(TAG) { "Decline tapped - callId: $id" }
235
+ CallManager.shared.endCall(id)
236
+ finish()
237
+ }
238
+
239
+ /**
240
+ * Observes session updates to auto-dismiss when the call is no longer ringing
241
+ * (answered elsewhere, timed out, or ended by the remote side).
242
+ *
243
+ * When [isAnswering] is true (user tapped answer locally), only auto-dismiss
244
+ * for ENDED status — the CONNECTING transition is expected and handled by
245
+ * the keyguard dismissal flow.
246
+ */
247
+ private fun observeSessionChanges(id: UUID) {
248
+ scope.launch {
249
+ CallStore.sessionUpdates(id).collect { session ->
250
+ if (session.status == CallSessionStatus.ENDED) {
251
+ finish()
252
+ } else if (!isAnswering && session.status != CallSessionStatus.RINGING) {
253
+ CallKitTelecomLog.d(TAG) { "Call no longer ringing (${session.status.value}), finishing" }
254
+ finish()
255
+ }
256
+ }
257
+ }
258
+ }
259
+
260
+ override fun onResume() {
261
+ super.onResume()
262
+ val id = callId ?: return
263
+ val session = CallStore.session(id)
264
+ if (session == null || session.status == CallSessionStatus.ENDED) {
265
+ finish()
266
+ } else if (!isAnswering && session.status != CallSessionStatus.RINGING) {
267
+ finish()
268
+ }
269
+ }
270
+
271
+ override fun onDestroy() {
272
+ super.onDestroy()
273
+ scope.cancel()
274
+ }
275
+ }
@@ -0,0 +1,151 @@
1
+ package expo.modules.callkittelecom.events
2
+
3
+ import expo.modules.callkittelecom.utils.CallKitTelecomLog
4
+ import java.time.Instant
5
+ import java.time.format.DateTimeFormatter
6
+
7
+ private data class QueuedEvent(
8
+ val body: Map<String, Any?>,
9
+ val timestamp: Instant,
10
+ )
11
+
12
+ /**
13
+ * Event bridge between Android native call state and the Expo JS module.
14
+ *
15
+ * Behavior:
16
+ * - Tracks which individual events are being observed by JS
17
+ * - Queues events that arrive before observers mount
18
+ * - Flushes queued events with `{ meta: { flushed: true } }`
19
+ *
20
+ * All mutable state is guarded by [lock] for thread safety.
21
+ */
22
+ object CallEventEmitter {
23
+ private const val TAG = "ExpoCallKitTelecom.Emitter"
24
+
25
+ private val lock = Any()
26
+ private val observingEvents = mutableSetOf<String>()
27
+ private val eventQueues = mutableMapOf<String, MutableList<QueuedEvent>>()
28
+ private val queueLimits = mutableMapOf<String, Int?>()
29
+
30
+ var defaultQueueLimit: Int? = 0
31
+
32
+ @Volatile
33
+ private var sender: ((String, Map<String, Any?>) -> Unit)? = null
34
+
35
+ /** Sets or clears the active event sender provided by the Expo module. */
36
+ fun setSender(eventSender: ((String, Map<String, Any?>) -> Unit)?) {
37
+ sender = eventSender
38
+ }
39
+
40
+ /**
41
+ * Configures queue size for a specific event.
42
+ *
43
+ * `null` means unlimited queueing, `0` disables queueing.
44
+ */
45
+ fun setQueueLimit(
46
+ eventName: String,
47
+ limit: Int?,
48
+ ) {
49
+ synchronized(lock) {
50
+ queueLimits[eventName] = limit
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Sends an event to JS if it is currently observed, or queues it otherwise.
56
+ *
57
+ * All delivered events are augmented with a `meta` object containing timestamp and flush status.
58
+ */
59
+ fun send(
60
+ eventName: String,
61
+ body: Map<String, Any?>,
62
+ ) {
63
+ val timestamp = Instant.now()
64
+ val senderRef = sender
65
+ val isObserving = synchronized(lock) { observingEvents.contains(eventName) }
66
+
67
+ if (senderRef != null && isObserving) {
68
+ CallKitTelecomLog.d(TAG) { "Sending event to JS - name: $eventName" }
69
+ senderRef(eventName, buildEventBody(body, flushed = false, timestamp = timestamp))
70
+ return
71
+ }
72
+ queueEvent(eventName, body, timestamp)
73
+ }
74
+
75
+ /** Marks an event as observed and flushes any pending queue for that event. */
76
+ fun startObserving(eventName: String) {
77
+ val queueCount: Int
78
+ synchronized(lock) {
79
+ queueCount = eventQueues[eventName]?.size ?: 0
80
+ observingEvents.add(eventName)
81
+ }
82
+ CallKitTelecomLog.d(TAG) { "Start observing - event: $eventName, queuedEvents: $queueCount" }
83
+ flushQueue(eventName)
84
+ }
85
+
86
+ /** Marks an event as no longer observed by JS. */
87
+ fun stopObserving(eventName: String) {
88
+ CallKitTelecomLog.d(TAG) { "Stop observing - event: $eventName" }
89
+ synchronized(lock) {
90
+ observingEvents.remove(eventName)
91
+ }
92
+ }
93
+
94
+ /** Adds native event metadata used by TypeScript event types. */
95
+ private fun buildEventBody(
96
+ body: Map<String, Any?>,
97
+ flushed: Boolean,
98
+ timestamp: Instant,
99
+ ): Map<String, Any?> {
100
+ val result = body.toMutableMap()
101
+ result["meta"] =
102
+ mapOf(
103
+ "flushed" to flushed,
104
+ "timestamp" to DateTimeFormatter.ISO_INSTANT.format(timestamp),
105
+ )
106
+ return result
107
+ }
108
+
109
+ /** Queues an event and enforces per-event queue limits (drop oldest first). */
110
+ private fun queueEvent(
111
+ name: String,
112
+ body: Map<String, Any?>,
113
+ timestamp: Instant,
114
+ ) {
115
+ synchronized(lock) {
116
+ val limit = queueLimits[name] ?: defaultQueueLimit
117
+ if (limit == 0) {
118
+ CallKitTelecomLog.d(TAG) { "Dropping event (queueing disabled) - name: $name" }
119
+ return
120
+ }
121
+
122
+ val queue = eventQueues.getOrPut(name) { mutableListOf() }
123
+ queue += QueuedEvent(body = body, timestamp = timestamp)
124
+
125
+ if (limit != null && queue.size > limit) {
126
+ val dropCount = queue.size - limit
127
+ repeat(dropCount) {
128
+ queue.removeAt(0)
129
+ }
130
+ CallKitTelecomLog.d(TAG) { "Queueing event (dropped $dropCount old) - name: $name, queueSize: ${queue.size}" }
131
+ } else {
132
+ CallKitTelecomLog.d(TAG) { "Queueing event (JS not listening) - name: $name, queueSize: ${queue.size}" }
133
+ }
134
+ }
135
+ }
136
+
137
+ /** Flushes all queued events for a single event name. */
138
+ private fun flushQueue(eventName: String) {
139
+ val senderRef = sender ?: return
140
+ val queue = synchronized(lock) { eventQueues.remove(eventName) } ?: return
141
+ if (queue.isEmpty()) return
142
+
143
+ CallKitTelecomLog.d(TAG) { "Flushing event queue - event: $eventName, count: ${queue.size}" }
144
+ queue.forEach { event ->
145
+ senderRef(
146
+ eventName,
147
+ buildEventBody(event.body, flushed = true, timestamp = event.timestamp),
148
+ )
149
+ }
150
+ }
151
+ }
@@ -0,0 +1,59 @@
1
+ package expo.modules.callkittelecom.events
2
+
3
+ /**
4
+ * Canonical event names emitted by the Android native call layer.
5
+ *
6
+ * These values must stay aligned with the TypeScript listeners in `Calls.ts`.
7
+ */
8
+ object CallEvents {
9
+ /** Emitted when a new native call session is created. */
10
+ const val CALL_SESSION_ADDED = "onCallSessionAdded"
11
+
12
+ /** Emitted when an existing native call session changes state. */
13
+ const val CALL_SESSION_UPDATED = "onCallSessionUpdated"
14
+
15
+ /** Emitted when a native call session is removed. */
16
+ const val CALL_SESSION_REMOVED = "onCallSessionRemoved"
17
+
18
+ /** Emitted when call audio has been activated for one or more calls. */
19
+ const val AUDIO_SESSION_ACTIVATED = "onAudioSessionActivated"
20
+
21
+ /** Emitted when call audio has been deactivated after call teardown. */
22
+ const val AUDIO_SESSION_DEACTIVATED = "onAudioSessionDeactivated"
23
+
24
+ /** Emitted when the current input/output route changes. */
25
+ const val AUDIO_ROUTE_CHANGED = "onAudioRouteChanged"
26
+
27
+ /** Emitted after an incoming call is successfully handed to Telecom. */
28
+ const val INCOMING_CALL_REPORTED = "onIncomingCallReported"
29
+
30
+ /** Emitted when an outgoing call has started and JS should connect media. */
31
+ const val OUTGOING_CALL_STARTED = "onOutgoingCallStarted"
32
+
33
+ /** Emitted when an incoming call is answered and media setup should begin. */
34
+ const val CALL_ANSWERED = "onCallAnswered"
35
+
36
+ /** Emitted when the local user/system ends a call. */
37
+ const val CALL_ENDED = "onCallEnded"
38
+
39
+ /** Emitted when the app reports an externally-ended call reason. */
40
+ const val CALL_REPORTED_ENDED = "onCallReportedEnded"
41
+
42
+ /** Emitted when mute state is requested or changed. */
43
+ const val SET_MUTED_ACTION = "onSetMutedAction"
44
+
45
+ /** Emitted when video enabled state changes. */
46
+ const val VIDEO_CHANGED = "onVideoChanged"
47
+
48
+ /** Emitted when hold state is requested or changed. */
49
+ const val SET_HELD_ACTION = "onSetHeldAction"
50
+
51
+ /** Emitted when DTMF digits are requested. */
52
+ const val DTMF = "onDTMF"
53
+
54
+ /** Reserved for call intent integration. */
55
+ const val CALL_INTENT_RECEIVED = "onCallIntentReceived"
56
+
57
+ /** Emitted when the FCM push token is refreshed. */
58
+ const val VOIP_PUSH_TOKEN_UPDATED = "onVoIPPushTokenUpdated"
59
+ }