@stream-io/react-native-callingx 0.1.0-beta.7 → 0.1.1-beta.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 (45) hide show
  1. package/android/build.gradle +7 -1
  2. package/android/src/main/AndroidManifest.xml +31 -1
  3. package/android/src/main/java/io/getstream/rn/callingx/CallEventBroadcastReceiver.kt +17 -0
  4. package/android/src/main/java/io/getstream/rn/callingx/CallRegistrationStore.kt +176 -0
  5. package/android/src/main/java/io/getstream/rn/callingx/CallService.kt +302 -80
  6. package/android/src/main/java/io/getstream/rn/callingx/CallingxModuleImpl.kt +176 -191
  7. package/android/src/main/java/io/getstream/rn/callingx/StreamMessagingService.kt +48 -0
  8. package/android/src/main/java/io/getstream/rn/callingx/model/Call.kt +1 -0
  9. package/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt +196 -46
  10. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt +14 -8
  11. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt +12 -1
  12. package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverService.kt +7 -0
  13. package/android/src/main/java/io/getstream/rn/callingx/repo/CallRepository.kt +38 -19
  14. package/android/src/main/java/io/getstream/rn/callingx/repo/LegacyCallRepository.kt +64 -55
  15. package/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt +241 -195
  16. package/android/src/main/java/io/getstream/rn/callingx/utils/CallEventBus.kt +61 -0
  17. package/android/src/main/java/io/getstream/rn/callingx/utils/SettingsStore.kt +51 -0
  18. package/android/src/newarch/java/io/getstream/rn/callingx/CallingxModule.kt +12 -3
  19. package/android/src/oldarch/java/io/getstream/rn/callingx/CallingxModule.kt +13 -3
  20. package/dist/module/CallingxModule.js +13 -10
  21. package/dist/module/CallingxModule.js.map +1 -1
  22. package/dist/module/spec/NativeCallingx.js.map +1 -1
  23. package/dist/module/utils/constants.js +24 -13
  24. package/dist/module/utils/constants.js.map +1 -1
  25. package/dist/typescript/src/CallingxModule.d.ts +3 -0
  26. package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
  27. package/dist/typescript/src/spec/NativeCallingx.d.ts +7 -1
  28. package/dist/typescript/src/spec/NativeCallingx.d.ts.map +1 -1
  29. package/dist/typescript/src/types.d.ts +31 -0
  30. package/dist/typescript/src/types.d.ts.map +1 -1
  31. package/dist/typescript/src/utils/constants.d.ts +1 -1
  32. package/dist/typescript/src/utils/constants.d.ts.map +1 -1
  33. package/ios/AudioSessionManager.swift +2 -2
  34. package/ios/Callingx.mm +41 -17
  35. package/ios/CallingxImpl.swift +213 -83
  36. package/ios/Settings.swift +2 -2
  37. package/ios/UUIDStorage.swift +10 -10
  38. package/ios/VoipNotificationsManager.swift +8 -8
  39. package/package.json +4 -2
  40. package/src/CallingxModule.ts +14 -10
  41. package/src/spec/NativeCallingx.ts +10 -3
  42. package/src/types.ts +34 -0
  43. package/src/utils/constants.ts +23 -9
  44. /package/android/src/main/java/io/getstream/rn/callingx/{ResourceUtils.kt → utils/ResourceUtils.kt} +0 -0
  45. /package/android/src/main/java/io/getstream/rn/callingx/{Utils.kt → utils/Utils.kt} +0 -0
@@ -18,7 +18,7 @@ import Foundation
18
18
  return queue.sync {
19
19
  if let existing = callsByCid[cid] {
20
20
  #if DEBUG
21
- print("[UUIDStorage] getOrCreateCall: found existing \(existing)")
21
+ NSLog("%@","[UUIDStorage] getOrCreateCall: found existing \(existing)")
22
22
  #endif
23
23
  return existing
24
24
  }
@@ -29,7 +29,7 @@ import Foundation
29
29
  callsByCid[cid] = call
30
30
  callsByUUID[uuidString] = call
31
31
  #if DEBUG
32
- print("[UUIDStorage] getOrCreateCall: created \(call)")
32
+ NSLog("%@","[UUIDStorage] getOrCreateCall: created \(call)")
33
33
  #endif
34
34
  return call
35
35
  }
@@ -63,7 +63,7 @@ import Foundation
63
63
  return queue.sync {
64
64
  if let existing = callsByCid[cid] {
65
65
  #if DEBUG
66
- print("[UUIDStorage] getUUIDForCid: found existing UUID \(existing.uuid.uuidString.lowercased()) for cid \(cid)")
66
+ NSLog("%@","[UUIDStorage] getUUIDForCid: found existing UUID \(existing.uuid.uuidString.lowercased()) for cid \(cid)")
67
67
  #endif
68
68
  return existing.uuid
69
69
  }
@@ -74,7 +74,7 @@ import Foundation
74
74
  callsByCid[cid] = call
75
75
  callsByUUID[uuidString] = call
76
76
  #if DEBUG
77
- print("[UUIDStorage] getUUIDForCid: created new UUID \(uuidString) for cid \(cid)")
77
+ NSLog("%@","[UUIDStorage] getUUIDForCid: created new UUID \(uuidString) for cid \(cid)")
78
78
  #endif
79
79
  return uuid
80
80
  }
@@ -91,7 +91,7 @@ import Foundation
91
91
  let uuidString = uuid.uuidString.lowercased()
92
92
  let cid = callsByUUID[uuidString]?.cid
93
93
  #if DEBUG
94
- print("[UUIDStorage] getCidForUUID: UUID \(uuidString) -> cid \(cid ?? "(not found)")")
94
+ NSLog("%@","[UUIDStorage] getCidForUUID: UUID \(uuidString) -> cid \(cid ?? "(not found)")")
95
95
  #endif
96
96
  return cid
97
97
  }
@@ -104,11 +104,11 @@ import Foundation
104
104
  callsByCid.removeValue(forKey: call.cid)
105
105
  callsByUUID.removeValue(forKey: uuidString)
106
106
  #if DEBUG
107
- print("[UUIDStorage] removeCidForUUID: removed cid \(call.cid) for UUID \(uuidString)")
107
+ NSLog("%@","[UUIDStorage] removeCidForUUID: removed cid \(call.cid) for UUID \(uuidString)")
108
108
  #endif
109
109
  } else {
110
110
  #if DEBUG
111
- print("[UUIDStorage] removeCidForUUID: no cid found for UUID \(uuidString)")
111
+ NSLog("%@","[UUIDStorage] removeCidForUUID: no cid found for UUID \(uuidString)")
112
112
  #endif
113
113
  }
114
114
  }
@@ -121,11 +121,11 @@ import Foundation
121
121
  callsByUUID.removeValue(forKey: uuidString)
122
122
  callsByCid.removeValue(forKey: cid)
123
123
  #if DEBUG
124
- print("[UUIDStorage] removeCid: removed cid \(cid) with UUID \(uuidString)")
124
+ NSLog("%@","[UUIDStorage] removeCid: removed cid \(cid) with UUID \(uuidString)")
125
125
  #endif
126
126
  } else {
127
127
  #if DEBUG
128
- print("[UUIDStorage] removeCid: no UUID found for cid \(cid)")
128
+ NSLog("%@","[UUIDStorage] removeCid: no UUID found for cid \(cid)")
129
129
  #endif
130
130
  }
131
131
  }
@@ -137,7 +137,7 @@ import Foundation
137
137
  callsByCid.removeAll()
138
138
  callsByUUID.removeAll()
139
139
  #if DEBUG
140
- print("[UUIDStorage] removeAllObjects: cleared \(count) entries")
140
+ NSLog("%@","[UUIDStorage] removeAllObjects: cleared \(count) entries")
141
141
  #endif
142
142
  }
143
143
  }
@@ -56,13 +56,13 @@ typealias RNVoipPushNotificationCompletion = () -> Void
56
56
  @objc public static func voipRegistration() {
57
57
  if isVoipRegistered {
58
58
  #if DEBUG
59
- print("[VoipNotificationsManager] voipRegistration is already registered. return _lastVoipToken = \(lastVoipToken)")
59
+ NSLog("%@","[VoipNotificationsManager] voipRegistration is already registered. return _lastVoipToken = \(lastVoipToken)")
60
60
  #endif
61
61
  let voipPushManager = VoipNotificationsManager.shared()
62
62
  voipPushManager.sendEventWithNameWrapper(name: VoipNotificationsEvents.registered, body: ["token": lastVoipToken])
63
63
  } else {
64
64
  #if DEBUG
65
- print("[VoipNotificationsManager] voipRegistration enter")
65
+ NSLog("%@","[VoipNotificationsManager] voipRegistration enter")
66
66
  #endif
67
67
  DispatchQueue.main.async {
68
68
  let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)
@@ -78,7 +78,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
78
78
  isVoipRegistered = true
79
79
  } else {
80
80
  #if DEBUG
81
- print("[VoipNotificationsManager] voipRegistration appDelegate not found. return")
81
+ NSLog("%@","[VoipNotificationsManager] voipRegistration appDelegate not found. return")
82
82
  #endif
83
83
  }
84
84
  }
@@ -87,7 +87,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
87
87
 
88
88
  @objc public static func didUpdatePushCredentials(_ credentials: PKPushCredentials, forType type: String) {
89
89
  #if DEBUG
90
- print("[VoipNotificationsManager] didUpdatePushCredentials credentials.token = \(credentials.token), type = \(type)")
90
+ NSLog("%@","[VoipNotificationsManager] didUpdatePushCredentials credentials.token = \(credentials.token), type = \(type)")
91
91
  #endif
92
92
 
93
93
  let voipTokenLength = credentials.token.count
@@ -103,7 +103,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
103
103
 
104
104
  @objc public static func didReceiveIncomingPushWithPayload(_ payload: PKPushPayload, forType type: String) {
105
105
  #if DEBUG
106
- print("[VoipNotificationsManager] didReceiveIncomingPushWithPayload payload.dictionaryPayload = \(payload.dictionaryPayload), type = \(type)")
106
+ NSLog("%@","[VoipNotificationsManager] didReceiveIncomingPushWithPayload payload.dictionaryPayload = \(payload.dictionaryPayload), type = \(type)")
107
107
  #endif
108
108
 
109
109
  let dictionaryPayload: [String: Any] = Dictionary(uniqueKeysWithValues: payload.dictionaryPayload.map { (key, value) in
@@ -119,7 +119,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
119
119
  var events: [[String: Any]] = []
120
120
  let action = {
121
121
  #if DEBUG
122
- print("[VoipNotificationsManager][getInitialEvents] delayedEvents = \(self.delayedEvents)")
122
+ NSLog("%@","[VoipNotificationsManager][getInitialEvents] delayedEvents = \(self.delayedEvents)")
123
123
  #endif
124
124
 
125
125
  events = self.delayedEvents
@@ -148,7 +148,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
148
148
 
149
149
  private func sendEventWithNameWrapper(name: String, body: [String: Any]?) {
150
150
  #if DEBUG
151
- print("[VoipNotificationsManager] sendEventWithNameWrapper: \(name)")
151
+ NSLog("%@","[VoipNotificationsManager] sendEventWithNameWrapper: \(name)")
152
152
  #endif
153
153
 
154
154
  let sendEventAction = {
@@ -162,7 +162,7 @@ typealias RNVoipPushNotificationCompletion = () -> Void
162
162
  } else {
163
163
  self.delayedEvents.append(dictionary)
164
164
  #if DEBUG
165
- print("[VoipNotificationsManager] delayedEvents: \(self.delayedEvents)")
165
+ NSLog("%@","[VoipNotificationsManager] delayedEvents: \(self.delayedEvents)")
166
166
  #endif
167
167
  }
168
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/react-native-callingx",
3
- "version": "0.1.0-beta.7",
3
+ "version": "0.1.1-beta.0",
4
4
  "description": "CallKit and Telecom API capabilities for React Native",
5
5
  "main": "./dist/module/index.js",
6
6
  "module": "./dist/module/index.js",
@@ -70,6 +70,8 @@
70
70
  "typescript": "^5.9.2"
71
71
  },
72
72
  "peerDependencies": {
73
+ "@react-native-firebase/app": ">=23.0.0",
74
+ "@react-native-firebase/messaging": ">=23.0.0",
73
75
  "@stream-io/react-native-webrtc": ">=137.1.2",
74
76
  "react": "*",
75
77
  "react-native": "*"
@@ -109,4 +111,4 @@
109
111
  "tools": [],
110
112
  "version": "0.55.0"
111
113
  }
112
- }
114
+ }
@@ -77,6 +77,7 @@ class CallingxModule implements ICallingxModule {
77
77
  subtitleTransformer,
78
78
  incomingChannel,
79
79
  ongoingChannel,
80
+ notificationTexts,
80
81
  } = options.android ?? {};
81
82
 
82
83
  this.titleTransformer = titleTransformer ?? defaultTextTransformer;
@@ -91,6 +92,7 @@ class CallingxModule implements ICallingxModule {
91
92
  ...defaultAndroidOptions.ongoingChannel,
92
93
  ...(ongoingChannel ?? {}),
93
94
  },
95
+ notificationTexts,
94
96
  };
95
97
 
96
98
  if (
@@ -237,16 +239,6 @@ class CallingxModule implements ICallingxModule {
237
239
  );
238
240
  }
239
241
 
240
- // Check if service is started (Android only)
241
- if (Platform.OS === 'android') {
242
- const isServiceStarted = await NativeCallingModule.isServiceStarted();
243
- if (!isServiceStarted) {
244
- throw new Error(
245
- 'Service is not started. Call displayIncomingCall or startCall first.',
246
- );
247
- }
248
- }
249
-
250
242
  return NativeCallingModule.startBackgroundTask(HEADLESS_TASK_NAME, 0);
251
243
  }
252
244
 
@@ -255,10 +247,22 @@ class CallingxModule implements ICallingxModule {
255
247
  return NativeCallingModule.stopBackgroundTask(HEADLESS_TASK_NAME);
256
248
  }
257
249
 
250
+ fulfillAnswerCallAction(callId: string, didFail: boolean): void {
251
+ NativeCallingModule.fulfillAnswerCallAction(callId, didFail);
252
+ }
253
+
254
+ fulfillEndCallAction(callId: string, didFail: boolean): void {
255
+ NativeCallingModule.fulfillEndCallAction(callId, didFail);
256
+ }
257
+
258
258
  registerVoipToken(): void {
259
259
  NativeCallingModule.registerVoipToken();
260
260
  }
261
261
 
262
+ stopService(): Promise<void> {
263
+ return NativeCallingModule.stopService();
264
+ }
265
+
262
266
  addEventListener<T extends EventName | VoipEventName>(
263
267
  eventName: T,
264
268
  callback: EventListener<
@@ -31,6 +31,10 @@ export interface Spec extends TurboModule {
31
31
  id: string;
32
32
  name: string;
33
33
  };
34
+ notificationTexts?: {
35
+ accepting?: string;
36
+ rejecting?: string;
37
+ };
34
38
  }): void;
35
39
 
36
40
  setShouldRejectCallWhenBusy(shouldReject: boolean): void;
@@ -127,15 +131,18 @@ export interface Spec extends TurboModule {
127
131
  setOnHoldCall(callId: string, isOnHold: boolean): Promise<void>;
128
132
 
129
133
  registerBackgroundTaskAvailable(): void;
130
-
131
- isServiceStarted(): Promise<boolean>;
132
-
133
134
  startBackgroundTask(taskName: string, timeout: number): Promise<void>;
134
135
 
135
136
  stopBackgroundTask(taskName: string): Promise<void>;
136
137
 
138
+ fulfillAnswerCallAction(callId: string, didFail: boolean): void;
139
+
140
+ fulfillEndCallAction(callId: string, didFail: boolean): void;
141
+
137
142
  registerVoipToken(): void;
138
143
 
144
+ stopService(): Promise<void>;
145
+
139
146
  readonly onNewEvent: EventEmitter<{
140
147
  eventName: string;
141
148
  params: {
package/src/types.ts CHANGED
@@ -121,8 +121,26 @@ export interface ICallingxModule {
121
121
 
122
122
  stopBackgroundTask(taskName: string): Promise<void>;
123
123
 
124
+ /**
125
+ * Fulfill or fail a pending CXAnswerCallAction on iOS.
126
+ * Must be called after starting the JS-side joining process (e.g: without awaiting for call.join() to complete)
127
+ * @param callId - The call id.
128
+ * @param didFail - If true, calls action.fail(); otherwise calls action.fulfill().
129
+ */
130
+ fulfillAnswerCallAction(callId: string, didFail: boolean): void;
131
+
132
+ /**
133
+ * Fulfill or fail a pending CXEndCallAction on iOS.
134
+ * Must be called after completetion of the JS-side processing (e.g: after call.leave() is done).
135
+ * @param callId - The call id.
136
+ * @param didFail - If true, calls action.fail(); otherwise calls action.fulfill().
137
+ */
138
+ fulfillEndCallAction(callId: string, didFail: boolean): void;
139
+
124
140
  registerVoipToken(): void;
125
141
 
142
+ stopService(): Promise<void>;
143
+
126
144
  /**
127
145
  * Single entry point for adding event listeners.
128
146
  * Automatically routes to the appropriate manager based on event type.
@@ -195,6 +213,22 @@ export type InternalAndroidOptions = {
195
213
  id?: string;
196
214
  name?: string;
197
215
  };
216
+ /**
217
+ * Texts used for call state notifications while the system is connecting or declining the call.
218
+ * If not provided, platform defaults will be used.
219
+ */
220
+ notificationTexts?: {
221
+ /**
222
+ * Text shown while optimistically accepting a call.
223
+ * @default "Connecting..."
224
+ */
225
+ accepting?: string;
226
+ /**
227
+ * Text shown while optimistically rejecting a call.
228
+ * @default "Declining..."
229
+ */
230
+ rejecting?: string;
231
+ };
198
232
  };
199
233
  type AndroidOptions = InternalAndroidOptions & NotificationTransformers;
200
234
 
@@ -19,7 +19,10 @@ export const defaultiOSOptions: Required<InternalIOSOptions> = {
19
19
  displayCallTimeout: 60000, // 1 minute
20
20
  };
21
21
 
22
- export const defaultAndroidOptions: DeepRequired<InternalAndroidOptions> = {
22
+ export const defaultAndroidOptions: Omit<
23
+ DeepRequired<InternalAndroidOptions>,
24
+ 'notificationTexts'
25
+ > = {
23
26
  incomingChannel: {
24
27
  id: 'stream_incoming_calls_channel',
25
28
  name: 'Incoming calls',
@@ -48,17 +51,28 @@ export const iosEndCallReasonMap: Record<EndCallReason, number> = {
48
51
  unknown: 1, // .failed (no iOS equivalent)
49
52
  };
50
53
 
51
- // Android: maps to android.telecom.DisconnectCause constants.
52
- // See https://developer.android.com/reference/android/telecom/DisconnectCause
54
+ // Android: maps to a limited subset of android.telecom.DisconnectCause constants
55
+ // that are allowed when using the CallControl / core-telecom APIs.
56
+ //
57
+ // Per platform docs, only the following codes are valid when disconnecting a call:
58
+ // - DisconnectCause.LOCAL
59
+ // - DisconnectCause.REMOTE
60
+ // - DisconnectCause.REJECTED
61
+ // - DisconnectCause.MISSED
62
+ //
63
+ // Numeric values (from android.telecom.DisconnectCause):
64
+ // LOCAL = 2, REMOTE = 3, REJECTED = 6, MISSED = 5
65
+ //
66
+ // We therefore collapse all high-level EndCallReason variants to this allowed set.
53
67
  export const androidEndCallReasonMap: Record<EndCallReason, number> = {
54
68
  local: 2, // LOCAL
55
69
  remote: 3, // REMOTE
56
70
  rejected: 6, // REJECTED
57
- busy: 7, // BUSY
58
- answeredElsewhere: 11, // ANSWERED_ELSEWHERE
71
+ busy: 6, // map busy -> REJECTED
72
+ answeredElsewhere: 3, // map answeredElsewhere -> REMOTE
59
73
  missed: 5, // MISSED
60
- error: 1, // ERROR
61
- canceled: 4, // CANCELED
62
- restricted: 8, // RESTRICTED
63
- unknown: 0, // UNKNOWN
74
+ error: 2, // map error -> LOCAL
75
+ canceled: 2, // map canceled -> LOCAL
76
+ restricted: 6, // map restricted -> REJECTED
77
+ unknown: 2, // map unknown -> LOCAL
64
78
  };