@stream-io/react-native-callingx 0.1.0-beta.7 → 0.1.1-beta.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 (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 +145 -0
  5. package/android/src/main/java/io/getstream/rn/callingx/CallService.kt +301 -83
  6. package/android/src/main/java/io/getstream/rn/callingx/CallingxModuleImpl.kt +148 -390
  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 +188 -48
  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 +20 -24
  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 -14
  24. package/dist/module/utils/constants.js.map +1 -1
  25. package/dist/typescript/src/CallingxModule.d.ts +4 -2
  26. package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
  27. package/dist/typescript/src/spec/NativeCallingx.d.ts +7 -4
  28. package/dist/typescript/src/spec/NativeCallingx.d.ts.map +1 -1
  29. package/dist/typescript/src/types.d.ts +33 -5
  30. package/dist/typescript/src/types.d.ts.map +1 -1
  31. package/dist/typescript/src/utils/constants.d.ts +2 -3
  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 +20 -21
  41. package/src/spec/NativeCallingx.ts +10 -6
  42. package/src/types.ts +36 -4
  43. package/src/utils/constants.ts +23 -12
  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
@@ -65,7 +65,7 @@ android {
65
65
  "generated/java",
66
66
  "generated/jni",
67
67
  "build/generated/source/codegen/java",
68
- "build/generated/source/codegen/jni"
68
+ "build/generated/source/codegen/jni",
69
69
  ]
70
70
  if (isNewArchitectureEnabled()) {
71
71
  java.srcDirs += ["src/newarch"]
@@ -87,4 +87,10 @@ dependencies {
87
87
  implementation "com.facebook.react:react-android"
88
88
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
89
89
  implementation "androidx.core:core-telecom:1.0.1"
90
+ // Compile-time only dependency so StreamMessagingService can reference RemoteMessage.
91
+ // The consuming app (via @react-native-firebase/messaging) must provide the actual runtime version.
92
+ compileOnly "com.google.firebase:firebase-messaging-ktx:24.1.2"
93
+ // Optional: use Firebase native code when the app has react-native-firebase installed (peer deps)
94
+ implementation project(':react-native-firebase_app')
95
+ implementation project(':react-native-firebase_messaging')
90
96
  }
@@ -1,4 +1,5 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ xmlns:tools="http://schemas.android.com/tools">
2
3
 
3
4
  <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
4
5
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
@@ -25,5 +26,34 @@
25
26
  android:foregroundServiceType="phoneCall"
26
27
  android:stopWithTask="true" />
27
28
 
29
+ <service
30
+ android:name="io.getstream.rn.callingx.StreamMessagingService"
31
+ android:exported="false">
32
+ <intent-filter>
33
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
34
+ </intent-filter>
35
+ </service>
36
+
37
+ <!-- Action values must match constants in CallingxModuleImpl.kt -->
38
+ <receiver
39
+ android:name="io.getstream.rn.callingx.CallEventBroadcastReceiver"
40
+ android:exported="false">
41
+ <intent-filter>
42
+ <action android:name="io.getstream.CALL_REGISTERED" />
43
+ <action android:name="io.getstream.CALL_REGISTERED_INCOMING" />
44
+ <action android:name="io.getstream.CALL_ANSWERED" />
45
+ <action android:name="io.getstream.CALL_INACTIVE" />
46
+ <action android:name="io.getstream.CALL_ACTIVE" />
47
+ <action android:name="io.getstream.CALL_MUTED" />
48
+ <action android:name="io.getstream.CALL_ENDPOINT_CHANGED" />
49
+ <action android:name="io.getstream.CALL_END" />
50
+ <action android:name="io.getstream.CALL_REGISTRATION_FAILED" />
51
+ </intent-filter>
52
+ </receiver>
53
+
54
+ <!-- Remove default FCM service so StreamMessagingService is the single handler -->
55
+ <service
56
+ android:name="io.invertase.firebase.messaging.ReactNativeFirebaseMessagingService"
57
+ tools:node="remove" />
28
58
  </application>
29
59
  </manifest>
@@ -0,0 +1,17 @@
1
+ package io.getstream.rn.callingx
2
+
3
+ import android.content.BroadcastReceiver
4
+ import android.content.Context
5
+ import android.content.Intent
6
+ import android.os.Bundle
7
+
8
+ class CallEventBroadcastReceiver : BroadcastReceiver() {
9
+
10
+ override fun onReceive(context: Context, intent: Intent) {
11
+ val action = intent.action ?: return
12
+ val extras = intent.extras ?: Bundle()
13
+
14
+ CallEventBus.publish(CallEvent(action = action, extras = extras))
15
+ }
16
+ }
17
+
@@ -0,0 +1,145 @@
1
+ package io.getstream.rn.callingx
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import com.facebook.react.bridge.Promise
6
+ import io.getstream.rn.callingx.model.CallAction
7
+ import java.util.Collections
8
+ import java.util.concurrent.ConcurrentHashMap
9
+ import kotlin.collections.emptyList
10
+
11
+ object CallRegistrationStore {
12
+
13
+ private const val TAG = "[Callingx] CallRegistrationStore"
14
+ private const val DISPLAY_TIMEOUT_MS = 10_000L
15
+
16
+ private val trackedCallIds: MutableSet<String> = ConcurrentHashMap.newKeySet()
17
+
18
+ /** Pending actions per callId, queued until the call is registered in Telecom. */
19
+ private val pendingActionsByCallId = ConcurrentHashMap<String, MutableList<CallAction>>()
20
+
21
+ // Per-callId pending promises for displayIncomingCall awaiting CALL_REGISTERED_INCOMING_ACTION
22
+ private val pendingPromises = mutableMapOf<String, Promise>()
23
+ private val pendingTimeouts = mutableMapOf<String, Runnable>()
24
+ private val mainHandler = Handler(Looper.getMainLooper())
25
+
26
+ fun trackCallRegistration(callId: String, promise: Promise?) {
27
+ debugLog(
28
+ TAG,
29
+ "[store] trackCallRegistration: Tracking call registration for callId: $callId"
30
+ )
31
+ trackedCallIds.add(callId)
32
+
33
+ if (promise == null) return
34
+
35
+ synchronized(pendingPromises) {
36
+ // Cancel any existing timeout for this callId to avoid a stale runnable
37
+ // rejecting the new promise after it overwrites the old one.
38
+ pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
39
+
40
+ pendingPromises[callId] = promise
41
+
42
+ val timeoutRunnable = Runnable {
43
+ synchronized(pendingPromises) {
44
+ pendingPromises
45
+ .remove(callId)
46
+ ?.reject("TIMEOUT", "Timed out waiting for call registration: $callId")
47
+ pendingTimeouts.remove(callId)
48
+ trackedCallIds.remove(callId)
49
+ }
50
+ }
51
+ pendingTimeouts[callId] = timeoutRunnable
52
+ mainHandler.postDelayed(timeoutRunnable, DISPLAY_TIMEOUT_MS)
53
+ }
54
+ }
55
+
56
+ fun onRegistrationSuccess(callId: String) {
57
+ synchronized(pendingPromises) {
58
+ pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
59
+ pendingPromises.remove(callId)?.resolve(true)
60
+ }
61
+ }
62
+
63
+ fun onRegistrationFailed(callId: String) {
64
+ reportRegistrationFail(
65
+ callId,
66
+ "REGISTRATION_FAILED",
67
+ "Failed to register call with telecom: $callId",
68
+ null
69
+ )
70
+ }
71
+
72
+ fun reportRegistrationFail(
73
+ callId: String,
74
+ code: String,
75
+ message: String?,
76
+ throwable: Throwable?
77
+ ) {
78
+ trackedCallIds.remove(callId)
79
+
80
+ synchronized(pendingPromises) {
81
+ pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
82
+ val promise = pendingPromises.remove(callId)
83
+ pendingActionsByCallId.remove(callId)
84
+ if (promise != null) {
85
+ if (throwable != null) {
86
+ promise.reject(code, message, throwable)
87
+ } else if (message != null) {
88
+ promise.reject(code, message)
89
+ } else {
90
+ promise.reject(code, "Unknown error")
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ fun addTrackedCall(callId: String) {
97
+ debugLog(TAG, "[store] addTrackedCall: Adding tracked call: $callId")
98
+ trackedCallIds.add(callId)
99
+ }
100
+
101
+ fun removeTrackedCall(callId: String) {
102
+ debugLog(TAG, "[store] removeTrackedCall: Removing tracked call: $callId")
103
+ trackedCallIds.remove(callId)
104
+ }
105
+
106
+ fun isCallTracked(callId: String): Boolean {
107
+ val isTracked = trackedCallIds.contains(callId)
108
+ debugLog(TAG, "[store] isCallTracked: Is call $callId tracked: $isTracked")
109
+ return isTracked
110
+ }
111
+
112
+ fun hasRegisteredCall(): Boolean {
113
+ return trackedCallIds.isNotEmpty()
114
+ }
115
+
116
+ /**
117
+ * Queues an action for a call that is not yet registered.
118
+ * Pending actions are drained and executed once registration completes.
119
+ */
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)
125
+ }
126
+
127
+ /**
128
+ * Returns and removes all queued actions for this call.
129
+ * Used once a call is registered so the service can replay pending actions.
130
+ */
131
+ fun takePendingActions(callId: String): List<CallAction> {
132
+ val list = pendingActionsByCallId.remove(callId) ?: return emptyList()
133
+ synchronized(list) { return list.toList() }
134
+ }
135
+
136
+ fun clearAll() {
137
+ synchronized(pendingPromises) {
138
+ pendingTimeouts.values.forEach { mainHandler.removeCallbacks(it) }
139
+ pendingTimeouts.clear()
140
+ pendingPromises.clear()
141
+ }
142
+ trackedCallIds.clear()
143
+ pendingActionsByCallId.clear()
144
+ }
145
+ }