@stream-io/react-native-callingx 0.1.1-beta.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +45 -357
  3. package/android/src/main/java/io/getstream/rn/callingx/CallRegistrationStore.kt +34 -65
  4. package/android/src/main/java/io/getstream/rn/callingx/CallService.kt +45 -43
  5. package/android/src/main/java/io/getstream/rn/callingx/CallingxModuleImpl.kt +8 -235
  6. package/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt +9 -25
  7. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt +18 -6
  8. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt +41 -18
  9. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverService.kt +24 -0
  10. package/dist/module/CallingxModule.js +7 -14
  11. package/dist/module/CallingxModule.js.map +1 -1
  12. package/dist/module/spec/NativeCallingx.js.map +1 -1
  13. package/dist/module/utils/constants.js +0 -1
  14. package/dist/module/utils/constants.js.map +1 -1
  15. package/dist/typescript/src/CallingxModule.d.ts +1 -2
  16. package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
  17. package/dist/typescript/src/spec/NativeCallingx.d.ts +0 -3
  18. package/dist/typescript/src/spec/NativeCallingx.d.ts.map +1 -1
  19. package/dist/typescript/src/types.d.ts +2 -5
  20. package/dist/typescript/src/types.d.ts.map +1 -1
  21. package/dist/typescript/src/utils/constants.d.ts +1 -2
  22. package/dist/typescript/src/utils/constants.d.ts.map +1 -1
  23. package/package.json +2 -2
  24. package/src/CallingxModule.ts +6 -11
  25. package/src/spec/NativeCallingx.ts +0 -3
  26. package/src/types.ts +2 -4
  27. package/src/utils/constants.ts +0 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
+
5
+ ## [0.1.1](https://github.com/GetStream/stream-video-js/compare/@stream-io/react-native-callingx-0.1.0...@stream-io/react-native-callingx-0.1.1) (2026-04-09)
6
+
7
+ ### Bug Fixes
8
+
9
+ - callingx docs update ([#2195](https://github.com/GetStream/stream-video-js/issues/2195)) ([7a6b632](https://github.com/GetStream/stream-video-js/commit/7a6b632270ec1187236a0e4e5c5396a98a20fd16))
10
+
11
+ ## 0.1.0 (2026-04-09)
12
+
13
+ ### Features
14
+
15
+ - callkit/telecom integration ([#2028](https://github.com/GetStream/stream-video-js/issues/2028)) ([d579acd](https://github.com/GetStream/stream-video-js/commit/d579acd1975fb4945e40452b27e372694c737628))
package/README.md CHANGED
@@ -1,395 +1,83 @@
1
- # react-native-callingx
1
+ # @stream-io/react-native-callingx
2
2
 
3
- A React Native Turbo Module for seamless native calling integration. This library provides a unified API to integrate with **CallKit** on iOS and the **Telecom/ConnectionService** API on Android, enabling your app to display system-level calling UI and interact with native call controls.
3
+ React Native native-calling bridge for:
4
4
 
5
- ## Features
5
+ - iOS CallKit
6
+ - Android Telecom/ConnectionService
6
7
 
7
- - 📞 **Incoming call UI** — Display native incoming call screens (even when the app is killed)
8
- - 📲 **Outgoing call registration** — Register outgoing calls with the system
9
- - 🎛️ **Call controls** — Mute, hold, end calls with native system integration
10
- - 🔔 **Custom notifications** — Configurable Android notification channels
11
- - ⚡ **Turbo Module** — Built with the New Architecture for optimal performance
12
- - 📱 **Background support** — Handle calls when the app is backgrounded or killed
13
-
14
- ## Requirements
15
-
16
- - React Native 0.73+ (New Architecture / Turbo Modules)
17
- - iOS 13.0+
18
- - Android API 26+ (Android 8.0 Oreo)
19
-
20
- ## Installation
8
+ ## Install
21
9
 
22
10
  ```sh
23
- npm install @stream-io/react-native-callingx
24
- # or
25
11
  yarn add @stream-io/react-native-callingx
26
12
  ```
27
13
 
28
- ### iOS Setup
29
-
30
- 1. Add the required background modes to your `Info.plist`:
31
-
32
- ```xml
33
- <key>UIBackgroundModes</key>
34
- <array>
35
- <string>voip</string>
36
- <string>audio</string>
37
- </array>
38
- ```
39
-
40
- 2. Run pod install:
14
+ Then run iOS pods:
41
15
 
42
16
  ```sh
43
17
  cd ios && pod install
44
18
  ```
45
19
 
46
- 3. For VoIP push notifications, configure your `AppDelegate` to report incoming calls:
47
-
48
- ```objc
49
- #import <Callingx/CallingxPublic.h>
50
-
51
- - (void)pushRegistry:(PKPushRegistry *)registry
52
- didReceiveIncomingPushWithPayload:(PKPushPayload *)payload
53
- forType:(PKPushType)type
54
- withCompletionHandler:(void (^)(void))completion {
55
-
56
- // Extract call information from payload
57
- NSString *callId = payload.dictionaryPayload[@"call_id"];
58
- NSString *callerName = payload.dictionaryPayload[@"caller_name"];
59
- NSString *handle = payload.dictionaryPayload[@"handle"];
60
- BOOL hasVideo = [payload.dictionaryPayload[@"has_video"] boolValue];
61
-
62
- [Callingx reportNewIncomingCall:callId
63
- handle:handle
64
- handleType:@"generic"
65
- hasVideo:hasVideo
66
- localizedCallerName:callerName
67
- supportsHolding:YES
68
- supportsDTMF:NO
69
- supportsGrouping:NO
70
- supportsUngrouping:NO
71
- payload:payload.dictionaryPayload
72
- withCompletionHandler:completion];
73
- }
74
- ```
75
-
76
- ### Android Setup
20
+ ## Quick start
77
21
 
78
- ## Usage
22
+ ```ts
23
+ import { CallingxModule } from '@stream-io/react-native-callingx';
79
24
 
80
- ### Setup
81
-
82
- Initialize the module with platform-specific configuration:
83
-
84
- ```typescript
85
- import { CallingxModule } from 'react-native-callingx';
86
-
87
- // Setup must be called before any other method
88
25
  CallingxModule.setup({
89
26
  ios: {
90
- appName: 'My App',
91
27
  supportsVideo: true,
92
- maximumCallsPerCallGroup: 1,
93
- maximumCallGroups: 1,
94
- handleType: 'generic', // 'generic' | 'number' | 'phone' | 'email'
28
+ callsHistory: false,
95
29
  },
96
30
  android: {
97
31
  incomingChannel: {
98
- id: 'incoming_calls',
99
- name: 'Incoming Calls',
100
- sound: 'ringtone', // optional custom sound
101
- vibration: true,
32
+ id: 'incoming_calls_channel',
33
+ name: 'Incoming calls',
102
34
  },
103
- outgoingChannel: {
104
- id: 'ongoing_calls',
105
- name: 'Ongoing Calls',
106
- },
107
- // Optional: transform display text
108
- titleTransformer: (name) => `Call from ${name}`,
109
- subtitleTransformer: (phoneNumber) => phoneNumber,
110
35
  },
111
36
  });
112
- ```
113
-
114
- ### Request Permissions
115
37
 
116
- Before displaying calls, request the required permissions:
117
-
118
- ```typescript
119
- const permissions = await CallingxModule.requestPermissions();
120
- console.log('Audio permission:', permissions.recordAudio);
121
- console.log('Notification permission:', permissions.postNotifications);
122
- ```
123
-
124
- ### Display Incoming Call
125
-
126
- Show the native incoming call UI:
127
-
128
- ```typescript
129
38
  await CallingxModule.displayIncomingCall(
130
- 'unique-call-id',
131
- '+1234567890', // phone number / handle
132
- 'John Doe', // caller name
133
- true, // has video
134
- );
135
- ```
136
-
137
- ### Start Outgoing Call
138
-
139
- Register an outgoing call with the system:
140
-
141
- ```typescript
142
- await CallingxModule.startCall(
143
- 'unique-call-id',
144
- '+1234567890',
39
+ 'call-id',
40
+ '+123456789',
145
41
  'John Doe',
146
- false, // audio only
42
+ true,
147
43
  );
148
44
  ```
149
45
 
150
- ### Answer Call
151
-
152
- Answer an incoming call programmatically:
153
-
154
- ```typescript
155
- await CallingxModule.answerIncomingCall('unique-call-id');
156
- ```
157
-
158
- ### Activate Call
159
-
160
- Mark a call as active (connected):
161
-
162
- ```typescript
163
- await CallingxModule.setCurrentCallActive('unique-call-id');
164
- ```
165
-
166
- ### End Call
167
-
168
- End a call with a specific reason:
169
-
170
- ```typescript
171
- import type { EndCallReason } from 'react-native-callingx';
172
-
173
- // Available reasons:
174
- // 'local' | 'remote' | 'rejected' | 'busy' | 'answeredElsewhere' |
175
- // 'missed' | 'error' | 'canceled' | 'restricted' | 'unknown'
176
- await CallingxModule.endCallWithReason('unique-call-id', 'remote');
177
- ```
178
-
179
- ### Mute/Unmute
180
-
181
- Toggle call mute state:
182
-
183
- ```typescript
184
- await CallingxModule.setMutedCall('unique-call-id', true); // mute
185
- await CallingxModule.setMutedCall('unique-call-id', false); // unmute
186
- ```
187
-
188
- ### Hold/Unhold
189
-
190
- Toggle call hold state:
191
-
192
- ```typescript
193
- await CallingxModule.setOnHoldCall('unique-call-id', true); // hold
194
- await CallingxModule.setOnHoldCall('unique-call-id', false); // unhold
195
- ```
196
-
197
- ### Update Display
198
-
199
- Update the caller information during a call:
200
-
201
- ```typescript
202
- await CallingxModule.updateDisplay(
203
- 'unique-call-id',
204
- '+1234567890',
205
- 'Updated Name',
206
- );
207
- ```
208
-
209
- ### Event Listeners
210
-
211
- Subscribe to call events:
212
-
213
- ```typescript
214
- import { CallingxModule } from 'react-native-callingx';
215
- import type { EventName } from 'react-native-callingx';
216
-
217
- // Answer event - user answered from system UI
218
- const answerSubscription = CallingxModule.addEventListener(
219
- 'answerCall',
220
- (params) => {
221
- console.log('Call answered:', params.callId);
222
- },
223
- );
224
-
225
- // End event - call ended
226
- const endSubscription = CallingxModule.addEventListener('endCall', (params) => {
227
- console.log('Call ended:', params.callId, 'Cause:', params.cause);
228
- });
229
-
230
- // Hold toggle event
231
- const holdSubscription = CallingxModule.addEventListener(
232
- 'didToggleHoldCallAction',
233
- (params) => {
234
- console.log('Hold toggled:', params.callId, 'On hold:', params.hold);
235
- },
236
- );
237
-
238
- // Mute toggle event
239
- const muteSubscription = CallingxModule.addEventListener(
240
- 'didPerformSetMutedCallAction',
241
- (params) => {
242
- console.log('Mute toggled:', params.callId, 'Muted:', params.muted);
243
- },
244
- );
245
-
246
- // Start call action (outgoing call initiated from system)
247
- const startSubscription = CallingxModule.addEventListener(
248
- 'didReceiveStartCallAction',
249
- (params) => {
250
- console.log('Start call action:', params.callId);
251
- },
252
- );
253
-
254
- // Clean up when done
255
- answerSubscription.remove();
256
- endSubscription.remove();
257
- // ... remove other subscriptions
258
- ```
259
-
260
- ### Handle Initial Events
261
-
262
- When the app is launched from a killed state by a call action, retrieve queued events:
263
-
264
- ```typescript
265
- // Get events that occurred before the module was initialized
266
- const initialEvents = CallingxModule.getInitialEvents();
267
- initialEvents.forEach((event) => {
268
- console.log('Initial event:', event.eventName, event.params);
269
- });
270
-
271
- // Clear initial events after processing
272
- await CallingxModule.clearInitialEvents();
273
- ```
274
-
275
- ### Background Tasks (Android)
276
-
277
- Run background tasks for call-related operations:
278
-
279
- ```typescript
280
- // Start a managed background task
281
- await CallingxModule.startBackgroundTask(async (taskData, stopTask) => {
282
- try {
283
- // Perform background work (e.g., connect to call server)
284
- await connectToCallServer();
285
- } finally {
286
- stopTask(); // Always call when done
287
- }
288
- });
289
-
290
- // Or stop manually
291
- await CallingxModule.stopBackgroundTask();
292
- ```
293
-
294
- ## API Reference
295
-
296
- ### CallingxModule
297
-
298
- | Method | Description |
299
- | ---------------------------------------------------------------- | ---------------------------------------------------- |
300
- | `setup(options)` | Initialize the module with platform-specific options |
301
- | `requestPermissions()` | Request required permissions (audio, notifications) |
302
- | `checkPermissions()` | Check current permission status |
303
- | `displayIncomingCall(callId, phoneNumber, callerName, hasVideo)` | Display incoming call UI |
304
- | `answerIncomingCall(callId)` | Answer an incoming call |
305
- | `startCall(callId, phoneNumber, callerName, hasVideo)` | Register an outgoing call |
306
- | `setCurrentCallActive(callId)` | Mark call as active/connected |
307
- | `updateDisplay(callId, phoneNumber, callerName)` | Update caller display info |
308
- | `endCallWithReason(callId, reason)` | End call with specified reason |
309
- | `setMutedCall(callId, isMuted)` | Toggle call mute state |
310
- | `setOnHoldCall(callId, isOnHold)` | Toggle call hold state |
311
- | `addEventListener(eventName, callback)` | Subscribe to call events |
312
- | `getInitialEvents()` | Get queued events from app launch |
313
- | `clearInitialEvents()` | Clear queued initial events |
314
- | `startBackgroundTask(taskProvider)` | Start Android background task |
315
- | `stopBackgroundTask()` | Stop Android background task |
316
- | `log(message, level)` | Log message to native console |
317
-
318
- ### Events
319
-
320
- | Event | Parameters | Description |
321
- | ------------------------------ | ------------------- | --------------------------------- |
322
- | `answerCall` | `{ callId }` | User answered call from system UI |
323
- | `endCall` | `{ callId, cause }` | Call ended |
324
- | `didToggleHoldCallAction` | `{ callId, hold }` | Hold state changed |
325
- | `didPerformSetMutedCallAction` | `{ callId, muted }` | Mute state changed |
326
- | `didReceiveStartCallAction` | `{ callId }` | Outgoing call action received |
327
-
328
- ### Types
329
-
330
- ```typescript
331
- type EndCallReason =
332
- | 'local' // Call ended by the local user (e.g., hanging up)
333
- | 'remote' // Call ended by the remote party, or outgoing not answered
334
- | 'rejected' // Call was rejected/declined
335
- | 'busy' // Remote party was busy
336
- | 'answeredElsewhere' // Answered on another device
337
- | 'missed' // No response to an incoming call
338
- | 'error' // Call failed due to an error (e.g., network issue)
339
- | 'canceled' // Call canceled before the remote party could answer
340
- | 'restricted' // Call restricted (e.g., airplane mode)
341
- | 'unknown'; // Unknown or unspecified disconnect reason
342
-
343
- type CallingExpiOSOptions = {
344
- appName: string;
345
- supportsVideo?: boolean;
346
- maximumCallsPerCallGroup?: number;
347
- maximumCallGroups?: number;
348
- handleType?: 'generic' | 'number' | 'phone' | 'email';
349
- };
350
-
351
- type CallingExpAndroidOptions = {
352
- incomingChannel?: {
353
- id: string;
354
- name: string;
355
- sound?: string;
356
- vibration?: boolean;
357
- };
358
- outgoingChannel?: {
359
- id: string;
360
- name: string;
361
- sound?: string;
362
- vibration?: boolean;
363
- };
364
- };
365
-
366
- type PermissionsResult = {
367
- recordAudio: boolean;
368
- postNotifications: boolean;
369
- };
370
- ```
371
-
372
- ## Troubleshooting
373
-
374
- ### iOS
375
-
376
- - **Incoming call not showing**: Ensure `voip` background mode is enabled and VoIP push certificate is configured
377
- - **CallKit errors**: Check that `appName` is set in setup options
378
- - **Audio issues**: The module automatically configures the audio session, but ensure no conflicts with other audio libraries
46
+ ## Main APIs
379
47
 
380
- ### Android
48
+ - `setup(options)` - required before any call action.
49
+ - `displayIncomingCall(callId, phoneNumber, callerName, hasVideo)`.
50
+ - `startCall(callId, phoneNumber, callerName, hasVideo)`.
51
+ - `setCurrentCallActive(callId)`.
52
+ - `updateDisplay(callId, phoneNumber, callerName, incoming)`.
53
+ - `endCallWithReason(callId, reason)`.
54
+ - `setMutedCall(callId, isMuted)`.
55
+ - `setOnHoldCall(callId, isOnHold)`.
56
+ - `addEventListener(eventName, callback)`.
57
+ - `getInitialEvents()` and `getInitialVoipEvents()`.
58
+ - `registerBackgroundTask(taskProvider)` / `startBackgroundTask()` / `stopBackgroundTask()` (Android).
381
59
 
382
- - **Notifications not showing**: Check POST_NOTIFICATIONS permission on Android 13+
383
- - **Call not answered on tap**: Ensure `handleCallingIntent` is called in both `onCreate` and `onNewIntent` in your MainActivity
60
+ ## Event names
384
61
 
385
- ## Contributing
62
+ Call events:
386
63
 
387
- See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
64
+ - `answerCall`
65
+ - `endCall`
66
+ - `didDisplayIncomingCall`
67
+ - `didToggleHoldCallAction`
68
+ - `didPerformSetMutedCallAction`
69
+ - `didChangeAudioRoute`
70
+ - `didReceiveStartCallAction`
71
+ - `didActivateAudioSession`
72
+ - `didDeactivateAudioSession`
388
73
 
389
- ## License
74
+ VoIP events:
390
75
 
391
- MIT
76
+ - `voipNotificationsRegistered`
77
+ - `voipNotificationReceived`
392
78
 
393
- ---
79
+ ## Notes
394
80
 
395
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
81
+ - Import from `@stream-io/react-native-callingx`.
82
+ - iOS-only helpers: `registerVoipToken`, `fulfillAnswerCallAction`, `fulfillEndCallAction`.
83
+ - Android helpers: `canPostNotifications`, `isOngoingCallsEnabled`.
@@ -2,9 +2,11 @@ package io.getstream.rn.callingx
2
2
 
3
3
  import android.os.Handler
4
4
  import android.os.Looper
5
- import android.util.Log
6
5
  import com.facebook.react.bridge.Promise
6
+ import io.getstream.rn.callingx.model.CallAction
7
+ import java.util.Collections
7
8
  import java.util.concurrent.ConcurrentHashMap
9
+ import kotlin.collections.emptyList
8
10
 
9
11
  object CallRegistrationStore {
10
12
 
@@ -13,10 +15,8 @@ object CallRegistrationStore {
13
15
 
14
16
  private val trackedCallIds: MutableSet<String> = ConcurrentHashMap.newKeySet()
15
17
 
16
- /** Pending disconnect cause code (android.telecom.DisconnectCause code) per callId. */
17
- private val pendingDisconnectCauseByCallId = ConcurrentHashMap<String, Int>()
18
- private val pendingAnswerByCallId = ConcurrentHashMap<String, Boolean>()
19
- private val pendingMuteByCallId = ConcurrentHashMap<String, Boolean>()
18
+ /** Pending actions per callId, queued until the call is registered in Telecom. */
19
+ private val pendingActionsByCallId = ConcurrentHashMap<String, MutableList<CallAction>>()
20
20
 
21
21
  // Per-callId pending promises for displayIncomingCall awaiting CALL_REGISTERED_INCOMING_ACTION
22
22
  private val pendingPromises = mutableMapOf<String, Promise>()
@@ -24,7 +24,10 @@ object CallRegistrationStore {
24
24
  private val mainHandler = Handler(Looper.getMainLooper())
25
25
 
26
26
  fun trackCallRegistration(callId: String, promise: Promise?) {
27
- debugLog(TAG, "[store] trackCallRegistration: Tracking call registration for callId: $callId")
27
+ debugLog(
28
+ TAG,
29
+ "[store] trackCallRegistration: Tracking call registration for callId: $callId"
30
+ )
28
31
  trackedCallIds.add(callId)
29
32
 
30
33
  if (promise == null) return
@@ -38,10 +41,9 @@ object CallRegistrationStore {
38
41
 
39
42
  val timeoutRunnable = Runnable {
40
43
  synchronized(pendingPromises) {
41
- pendingPromises.remove(callId)?.reject(
42
- "TIMEOUT",
43
- "Timed out waiting for call registration: $callId"
44
- )
44
+ pendingPromises
45
+ .remove(callId)
46
+ ?.reject("TIMEOUT", "Timed out waiting for call registration: $callId")
45
47
  pendingTimeouts.remove(callId)
46
48
  trackedCallIds.remove(callId)
47
49
  }
@@ -60,24 +62,25 @@ object CallRegistrationStore {
60
62
 
61
63
  fun onRegistrationFailed(callId: String) {
62
64
  reportRegistrationFail(
63
- callId,
64
- "REGISTRATION_FAILED",
65
- "Failed to register call with telecom: $callId",
66
- null
65
+ callId,
66
+ "REGISTRATION_FAILED",
67
+ "Failed to register call with telecom: $callId",
68
+ null
67
69
  )
68
70
  }
69
71
 
70
72
  fun reportRegistrationFail(
71
- callId: String,
72
- code: String,
73
- message: String?,
74
- throwable: Throwable?
73
+ callId: String,
74
+ code: String,
75
+ message: String?,
76
+ throwable: Throwable?
75
77
  ) {
76
78
  trackedCallIds.remove(callId)
77
79
 
78
80
  synchronized(pendingPromises) {
79
81
  pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
80
82
  val promise = pendingPromises.remove(callId)
83
+ pendingActionsByCallId.remove(callId)
81
84
  if (promise != null) {
82
85
  if (throwable != null) {
83
86
  promise.reject(code, message, throwable)
@@ -111,50 +114,23 @@ object CallRegistrationStore {
111
114
  }
112
115
 
113
116
  /**
114
- * Records that a disconnect was requested for this call before it was registered.
115
- * When the call becomes registered, the service should take this and run the disconnect action.
117
+ * Queues an action for a call that is not yet registered.
118
+ * Pending actions are drained and executed once registration completes.
116
119
  */
117
- fun setPendingDisconnect(callId: String, disconnectCauseCode: Int) {
118
- debugLog(TAG, "[store] setPendingDisconnect: callId=$callId causeCode=$disconnectCauseCode")
119
- pendingDisconnectCauseByCallId[callId] = disconnectCauseCode
120
- }
121
-
122
- fun setPendingAnswer(callId: String, isAudioCall: Boolean) {
123
- debugLog(TAG, "[store] setPendingAnswer: callId=$callId")
124
- pendingAnswerByCallId[callId] = isAudioCall
125
- }
126
-
127
- fun setPendingMute(callId: String, isMute: Boolean) {
128
- debugLog(TAG, "[store] setPendingMute: callId=$callId isMute=$isMute")
129
- pendingMuteByCallId[callId] = isMute
120
+ fun addPendingAction(callId: String, action: CallAction) {
121
+ debugLog(TAG, "[store] addPendingAction: callId=$callId action=${action::class.simpleName}")
122
+ pendingActionsByCallId
123
+ .computeIfAbsent(callId) { Collections.synchronizedList(mutableListOf()) }
124
+ .add(action)
130
125
  }
131
126
 
132
127
  /**
133
- * Returns and removes the pending disconnect cause code for this call, if any.
134
- * Used when the call has just become registered so the service can run the disconnect action.
128
+ * Returns and removes all queued actions for this call.
129
+ * Used once a call is registered so the service can replay pending actions.
135
130
  */
136
- fun takePendingDisconnect(callId: String): Int? {
137
- val code = pendingDisconnectCauseByCallId.remove(callId)
138
- if (code != null) {
139
- debugLog(TAG, "[store] takePendingDisconnect: callId=$callId causeCode=$code")
140
- }
141
- return code
142
- }
143
-
144
- fun takePendingAnswer(callId: String): Boolean? {
145
- val isAudioCall = pendingAnswerByCallId.remove(callId)
146
- if (isAudioCall != null) {
147
- debugLog(TAG, "[store] takePendingAnswer: callId=$callId isAudioCall=$isAudioCall")
148
- }
149
- return isAudioCall
150
- }
151
-
152
- fun takePendingMute(callId: String): Boolean? {
153
- val isMute = pendingMuteByCallId.remove(callId)
154
- if (isMute != null) {
155
- debugLog(TAG, "[store] takePendingMute: callId=$callId isMute=$isMute")
156
- }
157
- return isMute
131
+ fun takePendingActions(callId: String): List<CallAction> {
132
+ val list = pendingActionsByCallId.remove(callId) ?: return emptyList()
133
+ synchronized(list) { return list.toList() }
158
134
  }
159
135
 
160
136
  fun clearAll() {
@@ -164,13 +140,6 @@ object CallRegistrationStore {
164
140
  pendingPromises.clear()
165
141
  }
166
142
  trackedCallIds.clear()
167
- pendingDisconnectCauseByCallId.clear()
168
- pendingAnswerByCallId.clear()
169
- pendingMuteByCallId.clear()
170
- }
171
-
172
- private fun debugLog(tag: String, message: String) {
173
- Log.d(tag, message)
143
+ pendingActionsByCallId.clear()
174
144
  }
175
145
  }
176
-