@stream-io/react-native-callingx 0.1.0-beta.4 → 0.1.0-beta.6
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.
- package/README.md +0 -1
- package/android/build.gradle +9 -0
- package/android/src/main/java/io/getstream/rn/callingx/CallService.kt +69 -45
- package/android/src/main/java/io/getstream/rn/callingx/CallingxEventEmitterAdapter.kt +7 -0
- package/android/src/main/java/io/getstream/rn/callingx/{CallingxModule.kt → CallingxModuleImpl.kt} +194 -103
- package/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt +9 -9
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt +8 -8
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt +7 -7
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverService.kt +7 -7
- package/android/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt +3 -0
- package/android/src/newarch/java/io/getstream/rn/callingx/CallingxModule.kt +148 -0
- package/android/src/oldarch/java/io/getstream/rn/callingx/CallingxModule.kt +177 -0
- package/android/src/oldarch/java/io/getstream/rn/callingx/CallingxPackage.kt +16 -0
- package/dist/module/CallingxModule.js +2 -2
- package/dist/module/CallingxModule.js.map +1 -1
- package/dist/module/EventManager.js +11 -4
- package/dist/module/EventManager.js.map +1 -1
- package/dist/module/spec/NativeCallingx.js +4 -2
- package/dist/module/spec/NativeCallingx.js.map +1 -1
- package/dist/module/utils/utils.js +3 -0
- package/dist/module/utils/utils.js.map +1 -1
- package/dist/typescript/src/CallingxModule.d.ts +1 -1
- package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
- package/dist/typescript/src/EventManager.d.ts.map +1 -1
- package/dist/typescript/src/spec/NativeCallingx.d.ts +3 -3
- package/dist/typescript/src/spec/NativeCallingx.d.ts.map +1 -1
- package/dist/typescript/src/types.d.ts +2 -2
- package/dist/typescript/src/types.d.ts.map +1 -1
- package/dist/typescript/src/utils/utils.d.ts +1 -0
- package/dist/typescript/src/utils/utils.d.ts.map +1 -1
- package/ios/Callingx.mm +326 -22
- package/ios/CallingxCall.swift +105 -0
- package/ios/CallingxImpl.swift +136 -70
- package/ios/CallingxPublic.h +2 -5
- package/ios/UUIDStorage.swift +71 -26
- package/package.json +3 -3
- package/src/CallingxModule.ts +2 -2
- package/src/EventManager.ts +18 -5
- package/src/spec/NativeCallingx.ts +12 -3
- package/src/types.ts +2 -2
- package/src/utils/utils.ts +3 -0
- /package/android/src/{main → newarch}/java/io/getstream/rn/callingx/CallingxPackage.kt +0 -0
package/android/src/main/java/io/getstream/rn/callingx/{CallingxModule.kt → CallingxModuleImpl.kt}
RENAMED
|
@@ -8,7 +8,9 @@ import android.content.IntentFilter
|
|
|
8
8
|
import android.content.ServiceConnection
|
|
9
9
|
import android.os.Build
|
|
10
10
|
import android.os.Bundle
|
|
11
|
+
import android.os.Handler
|
|
11
12
|
import android.os.IBinder
|
|
13
|
+
import android.os.Looper
|
|
12
14
|
import android.telecom.DisconnectCause
|
|
13
15
|
import android.util.Log
|
|
14
16
|
import androidx.core.content.ContextCompat
|
|
@@ -21,14 +23,16 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
21
23
|
import com.facebook.react.bridge.WritableArray
|
|
22
24
|
import com.facebook.react.bridge.WritableMap
|
|
23
25
|
import com.facebook.react.bridge.WritableNativeArray
|
|
24
|
-
import com.facebook.react.module.annotations.ReactModule
|
|
25
26
|
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
26
27
|
import io.getstream.rn.callingx.model.CallAction
|
|
27
28
|
import io.getstream.rn.callingx.notifications.NotificationChannelsManager
|
|
28
29
|
import io.getstream.rn.callingx.notifications.NotificationsConfig
|
|
30
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
class CallingxModuleImpl(
|
|
33
|
+
private val reactApplicationContext: ReactApplicationContext,
|
|
34
|
+
private val eventEmitter: CallingxEventEmitterAdapter
|
|
35
|
+
) {
|
|
32
36
|
|
|
33
37
|
companion object {
|
|
34
38
|
const val TAG = "[Callingx] CallingxModule"
|
|
@@ -50,6 +54,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
50
54
|
const val CALL_MUTED_ACTION = "call_muted"
|
|
51
55
|
const val CALL_ENDPOINT_CHANGED_ACTION = "call_endpoint_changed"
|
|
52
56
|
const val CALL_END_ACTION = "call_end"
|
|
57
|
+
const val CALL_REGISTRATION_FAILED_ACTION = "call_registration_failed"
|
|
53
58
|
// Background task name
|
|
54
59
|
const val HEADLESS_TASK_NAME = "HandleCallBackgroundState"
|
|
55
60
|
const val SERVICE_READY_ACTION = "service_ready"
|
|
@@ -69,6 +74,16 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
69
74
|
private var canSendEvents = false
|
|
70
75
|
private var isHeadlessTaskRegistered = false
|
|
71
76
|
|
|
77
|
+
// Synchronous call tracking set, updated before async service start to mirror iOS semantics.
|
|
78
|
+
// This ensures isCallTracked() returns true immediately after displayIncomingCall/startCall.
|
|
79
|
+
private val trackedCallIds = ConcurrentHashMap.newKeySet<String>()
|
|
80
|
+
|
|
81
|
+
// Per-callId pending promises for displayIncomingCall awaiting CALL_REGISTERED_INCOMING_ACTION
|
|
82
|
+
private val pendingDisplayPromises = mutableMapOf<String, Promise>()
|
|
83
|
+
private val pendingTimeouts = mutableMapOf<String, Runnable>()
|
|
84
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
85
|
+
private val displayTimeoutMs = 10_000L // 10 second safety timeout
|
|
86
|
+
|
|
72
87
|
private val notificationChannelsManager = NotificationChannelsManager(reactApplicationContext)
|
|
73
88
|
private val callEventBroadcastReceiver = CallEventBroadcastReceiver()
|
|
74
89
|
private val appStateListener =
|
|
@@ -86,21 +101,18 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
86
101
|
|
|
87
102
|
init {
|
|
88
103
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
89
|
-
|
|
104
|
+
reactApplicationContext.registerReceiver(
|
|
90
105
|
callEventBroadcastReceiver,
|
|
91
106
|
getReceiverFilter(),
|
|
92
107
|
Context.RECEIVER_NOT_EXPORTED
|
|
93
108
|
)
|
|
94
109
|
} else {
|
|
95
110
|
@Suppress("UnspecifiedRegisterReceiverFlag")
|
|
96
|
-
|
|
111
|
+
reactApplicationContext.registerReceiver(callEventBroadcastReceiver, getReceiverFilter())
|
|
97
112
|
}
|
|
98
113
|
}
|
|
99
114
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
override fun initialize() {
|
|
103
|
-
super.initialize()
|
|
115
|
+
fun initialize() {
|
|
104
116
|
reactApplicationContext.addLifecycleEventListener(appStateListener)
|
|
105
117
|
|
|
106
118
|
tryToBindIfNeeded()
|
|
@@ -108,10 +120,17 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
108
120
|
debugLog(TAG, "[module] initialize: Initializing module")
|
|
109
121
|
}
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
super.invalidate()
|
|
123
|
+
fun invalidate() {
|
|
113
124
|
debugLog(TAG, "[module] invalidate: Invalidating module")
|
|
114
125
|
|
|
126
|
+
// Clean up pending display promises to prevent leaks
|
|
127
|
+
synchronized(pendingDisplayPromises) {
|
|
128
|
+
pendingTimeouts.values.forEach { mainHandler.removeCallbacks(it) }
|
|
129
|
+
pendingTimeouts.clear()
|
|
130
|
+
pendingDisplayPromises.clear()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
trackedCallIds.clear()
|
|
115
134
|
unbindServiceSafely()
|
|
116
135
|
|
|
117
136
|
reactApplicationContext.removeLifecycleEventListener(appStateListener)
|
|
@@ -119,11 +138,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
119
138
|
isModuleInitialized = false
|
|
120
139
|
}
|
|
121
140
|
|
|
122
|
-
|
|
123
|
-
// leave empty
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
override fun setupAndroid(options: ReadableMap) {
|
|
141
|
+
fun setupAndroid(options: ReadableMap) {
|
|
127
142
|
debugLog(TAG, "[module] setupAndroid: Setting up Android: $options")
|
|
128
143
|
val notificationsConfig =
|
|
129
144
|
NotificationsConfig.saveNotificationsConfig(reactApplicationContext, options)
|
|
@@ -133,24 +148,11 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
133
148
|
isModuleInitialized = true
|
|
134
149
|
}
|
|
135
150
|
|
|
136
|
-
|
|
151
|
+
fun canPostNotifications(): Boolean {
|
|
137
152
|
return notificationChannelsManager.getNotificationStatus().canPost
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
|
|
141
|
-
// leave empty
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
override fun getInitialVoipEvents(): WritableArray {
|
|
145
|
-
// leave empty
|
|
146
|
-
return Arguments.createArray()
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
override fun registerVoipToken() {
|
|
150
|
-
// leave empty
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
override fun getInitialEvents(): WritableArray {
|
|
155
|
+
fun getInitialEvents(): WritableArray {
|
|
154
156
|
// NOTE: writabel native array can be consumed only once, think of getting rid from clear
|
|
155
157
|
// event and clear eat immidiate after getting initial events
|
|
156
158
|
val events = delayedEvents
|
|
@@ -160,12 +162,12 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
160
162
|
return events
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
|
|
165
|
+
fun setCurrentCallActive(callId: String, promise: Promise) {
|
|
164
166
|
debugLog(TAG, "[module] activateCall: Activating call: $callId")
|
|
165
167
|
executeServiceAction(callId, CallAction.Activate, promise)
|
|
166
168
|
}
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
fun displayIncomingCall(
|
|
169
171
|
callId: String,
|
|
170
172
|
phoneNumber: String,
|
|
171
173
|
callerName: String,
|
|
@@ -182,19 +184,52 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
182
184
|
return
|
|
183
185
|
}
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
trackedCallIds.add(callId)
|
|
188
|
+
|
|
189
|
+
// Store the promise keyed by callId; it will be resolved when CALL_REGISTERED_INCOMING_ACTION
|
|
190
|
+
// broadcast is received, or rejected on timeout / registration failure.
|
|
191
|
+
synchronized(pendingDisplayPromises) {
|
|
192
|
+
// Cancel any existing timeout for this callId to avoid a stale
|
|
193
|
+
// runnable rejecting the new promise after it overwrites the old one.
|
|
194
|
+
pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
|
|
195
|
+
|
|
196
|
+
pendingDisplayPromises[callId] = promise
|
|
197
|
+
|
|
198
|
+
// Per-call timeout runnable
|
|
199
|
+
val timeoutRunnable = Runnable {
|
|
200
|
+
synchronized(pendingDisplayPromises) {
|
|
201
|
+
pendingDisplayPromises.remove(callId)?.reject(
|
|
202
|
+
"TIMEOUT",
|
|
203
|
+
"Timed out waiting for call registration: $callId"
|
|
204
|
+
)
|
|
205
|
+
pendingTimeouts.remove(callId)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
pendingTimeouts[callId] = timeoutRunnable
|
|
209
|
+
mainHandler.postDelayed(timeoutRunnable, displayTimeoutMs)
|
|
210
|
+
}
|
|
193
211
|
|
|
194
|
-
|
|
212
|
+
try {
|
|
213
|
+
startCallService(
|
|
214
|
+
CallService.ACTION_INCOMING_CALL,
|
|
215
|
+
callId,
|
|
216
|
+
callerName,
|
|
217
|
+
phoneNumber,
|
|
218
|
+
hasVideo,
|
|
219
|
+
displayOptions
|
|
220
|
+
)
|
|
221
|
+
} catch (e: Exception) {
|
|
222
|
+
Log.e(TAG, "[module] displayIncomingCall: Failed to start foreground service: ${e.message}", e)
|
|
223
|
+
trackedCallIds.remove(callId)
|
|
224
|
+
synchronized(pendingDisplayPromises) {
|
|
225
|
+
pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
|
|
226
|
+
pendingDisplayPromises.remove(callId)
|
|
227
|
+
}
|
|
228
|
+
promise.reject("START_FOREGROUND_SERVICE_ERROR", e.message, e)
|
|
229
|
+
}
|
|
195
230
|
}
|
|
196
231
|
|
|
197
|
-
|
|
232
|
+
fun answerIncomingCall(callId: String, promise: Promise) {
|
|
198
233
|
debugLog(TAG, "[module] answerIncomingCall: Answering call: $callId")
|
|
199
234
|
// TODO: get the call type from the call attributes
|
|
200
235
|
val isAudioCall = true // TODO: get the call type from the call attributes
|
|
@@ -204,7 +239,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
204
239
|
executeServiceAction(callId, CallAction.Answer(isAudioCall), promise)
|
|
205
240
|
}
|
|
206
241
|
|
|
207
|
-
|
|
242
|
+
fun startCall(
|
|
208
243
|
callId: String,
|
|
209
244
|
phoneNumber: String,
|
|
210
245
|
callerName: String,
|
|
@@ -221,19 +256,26 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
221
256
|
return
|
|
222
257
|
}
|
|
223
258
|
|
|
224
|
-
|
|
225
|
-
CallService.ACTION_OUTGOING_CALL,
|
|
226
|
-
callId,
|
|
227
|
-
callerName,
|
|
228
|
-
phoneNumber,
|
|
229
|
-
hasVideo,
|
|
230
|
-
displayOptions
|
|
231
|
-
)
|
|
259
|
+
trackedCallIds.add(callId)
|
|
232
260
|
|
|
233
|
-
|
|
261
|
+
try {
|
|
262
|
+
startCallService(
|
|
263
|
+
CallService.ACTION_OUTGOING_CALL,
|
|
264
|
+
callId,
|
|
265
|
+
callerName,
|
|
266
|
+
phoneNumber,
|
|
267
|
+
hasVideo,
|
|
268
|
+
displayOptions
|
|
269
|
+
)
|
|
270
|
+
promise.resolve(true)
|
|
271
|
+
} catch (e: Exception) {
|
|
272
|
+
Log.e(TAG, "[module] startCall: Failed to start foreground service: ${e.message}", e)
|
|
273
|
+
trackedCallIds.remove(callId)
|
|
274
|
+
promise.reject("START_FOREGROUND_SERVICE_ERROR", e.message, e)
|
|
275
|
+
}
|
|
234
276
|
}
|
|
235
277
|
|
|
236
|
-
|
|
278
|
+
fun updateDisplay(
|
|
237
279
|
callId: String,
|
|
238
280
|
phoneNumber: String,
|
|
239
281
|
callerName: String,
|
|
@@ -247,84 +289,101 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
247
289
|
}
|
|
248
290
|
|
|
249
291
|
// for now only display options will be updated, rest of the parameters will be ignored
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
292
|
+
try {
|
|
293
|
+
startCallService(
|
|
294
|
+
CallService.ACTION_UPDATE_CALL,
|
|
295
|
+
callId,
|
|
296
|
+
callerName,
|
|
297
|
+
phoneNumber,
|
|
298
|
+
true,
|
|
299
|
+
displayOptions,
|
|
300
|
+
)
|
|
301
|
+
promise.resolve(true)
|
|
302
|
+
} catch (e: Exception) {
|
|
303
|
+
Log.e(TAG, "[module] updateDisplay: Failed to start foreground service: ${e.message}", e)
|
|
304
|
+
promise.reject("START_FOREGROUND_SERVICE_ERROR", e.message, e)
|
|
305
|
+
}
|
|
259
306
|
}
|
|
260
307
|
|
|
261
|
-
|
|
308
|
+
fun endCallWithReason(callId: String, reason: Double, promise: Promise) {
|
|
262
309
|
debugLog(TAG, "[module] endCallWithReason: Ending call: $callId, $reason")
|
|
310
|
+
trackedCallIds.remove(callId)
|
|
263
311
|
val action = CallAction.Disconnect(DisconnectCause(reason.toInt()))
|
|
264
312
|
executeServiceAction(callId, action, promise)
|
|
265
313
|
}
|
|
266
314
|
|
|
267
|
-
|
|
315
|
+
fun endCall(callId: String, promise: Promise) {
|
|
268
316
|
debugLog(TAG, "[module] endCall: Ending call: $callId")
|
|
317
|
+
trackedCallIds.remove(callId)
|
|
269
318
|
val action = CallAction.Disconnect(DisconnectCause(DisconnectCause.LOCAL))
|
|
270
319
|
executeServiceAction(callId, action, promise)
|
|
271
320
|
}
|
|
272
321
|
|
|
273
|
-
|
|
274
|
-
val
|
|
275
|
-
debugLog(TAG, "[module]
|
|
276
|
-
return
|
|
322
|
+
fun isCallTracked(callId: String): Boolean {
|
|
323
|
+
val isTracked = trackedCallIds.contains(callId)
|
|
324
|
+
debugLog(TAG, "[module] isCallTracked: Is call tracked: $isTracked")
|
|
325
|
+
return isTracked
|
|
277
326
|
}
|
|
278
327
|
|
|
279
|
-
|
|
328
|
+
fun hasRegisteredCall(): Boolean {
|
|
280
329
|
val hasRegisteredCall = callService?.hasRegisteredCall() ?: false
|
|
281
330
|
debugLog(TAG, "[module] hasRegisteredCall: Has registered call: $hasRegisteredCall")
|
|
282
331
|
return hasRegisteredCall
|
|
283
332
|
}
|
|
284
333
|
|
|
285
|
-
|
|
334
|
+
fun setMutedCall(callId: String, isMuted: Boolean, promise: Promise) {
|
|
286
335
|
debugLog(TAG, "[module] setMutedCall: Setting muted call: $callId, $isMuted")
|
|
287
336
|
val action = CallAction.ToggleMute(isMuted)
|
|
288
337
|
executeServiceAction(callId, action, promise)
|
|
289
338
|
}
|
|
290
339
|
|
|
291
|
-
|
|
340
|
+
fun setOnHoldCall(callId: String, isOnHold: Boolean, promise: Promise) {
|
|
292
341
|
debugLog(TAG, "[module] setOnHoldCall: Setting on hold call: $callId, $isOnHold")
|
|
293
342
|
val action = if (isOnHold) CallAction.Hold else CallAction.Activate
|
|
294
343
|
executeServiceAction(callId, action, promise)
|
|
295
344
|
}
|
|
296
345
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
346
|
+
fun startBackgroundTask(taskName: String, timeout: Double, promise: Promise) {
|
|
347
|
+
try {
|
|
348
|
+
Intent(reactApplicationContext, CallService::class.java)
|
|
349
|
+
.apply {
|
|
350
|
+
this.action = CallService.ACTION_START_BACKGROUND_TASK
|
|
351
|
+
putExtra(CallService.EXTRA_TASK_NAME, taskName)
|
|
352
|
+
putExtra(CallService.EXTRA_TASK_DATA, Bundle())
|
|
353
|
+
putExtra(CallService.EXTRA_TASK_TIMEOUT, timeout.toLong())
|
|
354
|
+
}
|
|
355
|
+
.also { ContextCompat.startForegroundService(reactApplicationContext, it) }
|
|
306
356
|
|
|
307
|
-
|
|
357
|
+
promise.resolve(true)
|
|
358
|
+
} catch (e: Exception) {
|
|
359
|
+
Log.e(TAG, "[module] startBackgroundTask: Failed to start foreground service: ${e.message}", e)
|
|
360
|
+
promise.reject("START_FOREGROUND_SERVICE_ERROR", e.message, e)
|
|
361
|
+
}
|
|
308
362
|
}
|
|
309
363
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
364
|
+
fun stopBackgroundTask(taskName: String, promise: Promise) {
|
|
365
|
+
try {
|
|
366
|
+
Intent(reactApplicationContext, CallService::class.java)
|
|
367
|
+
.apply {
|
|
368
|
+
this.action = CallService.ACTION_STOP_BACKGROUND_TASK
|
|
369
|
+
putExtra(CallService.EXTRA_TASK_NAME, taskName)
|
|
370
|
+
}
|
|
371
|
+
.also { ContextCompat.startForegroundService(reactApplicationContext, it) }
|
|
317
372
|
|
|
318
|
-
|
|
319
|
-
|
|
373
|
+
isHeadlessTaskRegistered = false
|
|
374
|
+
promise.resolve(true)
|
|
375
|
+
} catch (e: Exception) {
|
|
376
|
+
Log.e(TAG, "[module] stopBackgroundTask: Failed to start foreground service: ${e.message}", e)
|
|
377
|
+
promise.reject("START_FOREGROUND_SERVICE_ERROR", e.message, e)
|
|
378
|
+
}
|
|
320
379
|
}
|
|
321
380
|
|
|
322
|
-
|
|
381
|
+
fun registerBackgroundTaskAvailable() {
|
|
323
382
|
debugLog(TAG, "[module] registerBackgroundTaskAvailable: Headless task registered")
|
|
324
383
|
isHeadlessTaskRegistered = true
|
|
325
384
|
}
|
|
326
385
|
|
|
327
|
-
|
|
386
|
+
fun isServiceStarted(promise: Promise) {
|
|
328
387
|
val isStarted =
|
|
329
388
|
bindingState == BindingState.BOUND ||
|
|
330
389
|
bindingState == BindingState.BINDING ||
|
|
@@ -333,7 +392,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
333
392
|
promise.resolve(isStarted)
|
|
334
393
|
}
|
|
335
394
|
|
|
336
|
-
|
|
395
|
+
fun log(message: String, level: String) {
|
|
337
396
|
when (level) {
|
|
338
397
|
"debug" -> debugLog(TAG, "[module] log: $message")
|
|
339
398
|
"info" -> Log.i(TAG, "[module] log: $message")
|
|
@@ -371,14 +430,18 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
371
430
|
return
|
|
372
431
|
}
|
|
373
432
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
433
|
+
try {
|
|
434
|
+
Intent(reactApplicationContext, CallService::class.java)
|
|
435
|
+
.apply {
|
|
436
|
+
this.action = CallService.ACTION_START_BACKGROUND_TASK
|
|
437
|
+
putExtra(CallService.EXTRA_TASK_NAME, taskName)
|
|
438
|
+
putExtra(CallService.EXTRA_TASK_DATA, Bundle())
|
|
439
|
+
putExtra(CallService.EXTRA_TASK_TIMEOUT, timeout.toLong())
|
|
440
|
+
}
|
|
441
|
+
.also { ContextCompat.startForegroundService(reactApplicationContext, it) }
|
|
442
|
+
} catch (e: Exception) {
|
|
443
|
+
Log.e(TAG, "[module] startBackgroundTaskAutomatically: Failed to start foreground service: ${e.message}", e)
|
|
444
|
+
}
|
|
382
445
|
}
|
|
383
446
|
|
|
384
447
|
private fun executeServiceAction(callId: String, action: CallAction, promise: Promise) {
|
|
@@ -432,7 +495,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
432
495
|
putString("eventName", eventName)
|
|
433
496
|
putMap("params", paramsMap)
|
|
434
497
|
}
|
|
435
|
-
|
|
498
|
+
eventEmitter.emitNewEvent(value)
|
|
436
499
|
} else {
|
|
437
500
|
debugLog(TAG, "[module] sendJSEvent: Queueing event: $eventName, $params")
|
|
438
501
|
Arguments.createMap()
|
|
@@ -454,6 +517,7 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
454
517
|
addAction(CALL_MUTED_ACTION)
|
|
455
518
|
addAction(CALL_ENDPOINT_CHANGED_ACTION)
|
|
456
519
|
addAction(CALL_END_ACTION)
|
|
520
|
+
addAction(CALL_REGISTRATION_FAILED_ACTION)
|
|
457
521
|
addAction(SERVICE_READY_ACTION)
|
|
458
522
|
}
|
|
459
523
|
|
|
@@ -563,8 +627,30 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
563
627
|
sendJSEvent("didReceiveStartCallAction", params)
|
|
564
628
|
}
|
|
565
629
|
CALL_REGISTERED_INCOMING_ACTION -> {
|
|
630
|
+
// Resolve the pending displayIncomingCall promise for this callId
|
|
631
|
+
if (callId != null) {
|
|
632
|
+
synchronized(pendingDisplayPromises) {
|
|
633
|
+
pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
|
|
634
|
+
pendingDisplayPromises.remove(callId)?.resolve(true)
|
|
635
|
+
}
|
|
636
|
+
}
|
|
566
637
|
sendJSEvent("didDisplayIncomingCall", params)
|
|
567
638
|
}
|
|
639
|
+
CALL_REGISTRATION_FAILED_ACTION -> {
|
|
640
|
+
if (callId != null) {
|
|
641
|
+
trackedCallIds.remove(callId)
|
|
642
|
+
}
|
|
643
|
+
// Reject the pending displayIncomingCall promise for this callId
|
|
644
|
+
if (callId != null) {
|
|
645
|
+
synchronized(pendingDisplayPromises) {
|
|
646
|
+
pendingTimeouts.remove(callId)?.let { mainHandler.removeCallbacks(it) }
|
|
647
|
+
pendingDisplayPromises.remove(callId)?.reject(
|
|
648
|
+
"REGISTRATION_FAILED",
|
|
649
|
+
"Failed to register call with telecom: $callId"
|
|
650
|
+
)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
568
654
|
CALL_ANSWERED_ACTION -> {
|
|
569
655
|
if (intent.hasExtra(EXTRA_SOURCE)) {
|
|
570
656
|
params.putString("source", intent.getStringExtra(EXTRA_SOURCE))
|
|
@@ -577,6 +663,11 @@ class CallingxModule(reactContext: ReactApplicationContext) : NativeCallingxSpec
|
|
|
577
663
|
params.putString("source", source)
|
|
578
664
|
}
|
|
579
665
|
if (source == "app") {
|
|
666
|
+
if (callId != null) {
|
|
667
|
+
//we stop tracking the call only when it was handled by the app
|
|
668
|
+
//in case source is "sys" we still need to know that call is tracked, otherwise we'll unable to end call from js side
|
|
669
|
+
trackedCallIds.remove(callId)
|
|
670
|
+
}
|
|
580
671
|
// means the call was disconnected, we're ready to unbind the service
|
|
581
672
|
unbindServiceSafely()
|
|
582
673
|
}
|
package/android/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt
CHANGED
|
@@ -13,7 +13,7 @@ import androidx.core.app.NotificationManagerCompat
|
|
|
13
13
|
import androidx.core.app.Person
|
|
14
14
|
import androidx.core.graphics.drawable.IconCompat
|
|
15
15
|
import io.getstream.rn.callingx.CallService
|
|
16
|
-
import io.getstream.rn.callingx.
|
|
16
|
+
import io.getstream.rn.callingx.CallingxModuleImpl
|
|
17
17
|
import io.getstream.rn.callingx.R
|
|
18
18
|
import io.getstream.rn.callingx.ResourceUtils
|
|
19
19
|
import io.getstream.rn.callingx.debugLog
|
|
@@ -78,7 +78,7 @@ class CallNotificationManager(
|
|
|
78
78
|
val contentIntent =
|
|
79
79
|
NotificationIntentFactory.getLaunchActivityIntent(
|
|
80
80
|
context,
|
|
81
|
-
|
|
81
|
+
CallingxModuleImpl.CALL_ANSWERED_ACTION,
|
|
82
82
|
call.id
|
|
83
83
|
)
|
|
84
84
|
val callStyle = createCallStyle(call)
|
|
@@ -190,21 +190,21 @@ class CallNotificationManager(
|
|
|
190
190
|
caller,
|
|
191
191
|
NotificationIntentFactory.getPendingBroadcastIntent(
|
|
192
192
|
context,
|
|
193
|
-
|
|
193
|
+
CallingxModuleImpl.CALL_END_ACTION,
|
|
194
194
|
call.id
|
|
195
195
|
) {
|
|
196
196
|
putExtra(
|
|
197
|
-
|
|
197
|
+
CallingxModuleImpl.EXTRA_DISCONNECT_CAUSE,
|
|
198
198
|
getDisconnectCauseString(DisconnectCause(DisconnectCause.REJECTED))
|
|
199
199
|
)
|
|
200
200
|
putExtra(
|
|
201
|
-
|
|
201
|
+
CallingxModuleImpl.EXTRA_SOURCE,
|
|
202
202
|
CallRepository.EventSource.SYS.name.lowercase()
|
|
203
203
|
)
|
|
204
204
|
},
|
|
205
205
|
NotificationIntentFactory.getPendingNotificationIntent(
|
|
206
206
|
context,
|
|
207
|
-
|
|
207
|
+
CallingxModuleImpl.CALL_ANSWERED_ACTION,
|
|
208
208
|
call.id,
|
|
209
209
|
CallRepository.EventSource.SYS.name.lowercase()
|
|
210
210
|
)
|
|
@@ -215,15 +215,15 @@ class CallNotificationManager(
|
|
|
215
215
|
caller,
|
|
216
216
|
NotificationIntentFactory.getPendingBroadcastIntent(
|
|
217
217
|
context,
|
|
218
|
-
|
|
218
|
+
CallingxModuleImpl.CALL_END_ACTION,
|
|
219
219
|
call.id
|
|
220
220
|
) {
|
|
221
221
|
putExtra(
|
|
222
|
-
|
|
222
|
+
CallingxModuleImpl.EXTRA_DISCONNECT_CAUSE,
|
|
223
223
|
getDisconnectCauseString(DisconnectCause(DisconnectCause.LOCAL))
|
|
224
224
|
)
|
|
225
225
|
putExtra(
|
|
226
|
-
|
|
226
|
+
CallingxModuleImpl.EXTRA_SOURCE,
|
|
227
227
|
CallRepository.EventSource.SYS.name.lowercase()
|
|
228
228
|
)
|
|
229
229
|
},
|
package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationIntentFactory.kt
CHANGED
|
@@ -4,7 +4,7 @@ import android.app.PendingIntent
|
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.content.Intent
|
|
6
6
|
import android.os.Build
|
|
7
|
-
import io.getstream.rn.callingx.
|
|
7
|
+
import io.getstream.rn.callingx.CallingxModuleImpl
|
|
8
8
|
|
|
9
9
|
object NotificationIntentFactory {
|
|
10
10
|
// Stable request codes for PendingIntents
|
|
@@ -29,8 +29,8 @@ object NotificationIntentFactory {
|
|
|
29
29
|
val intent =
|
|
30
30
|
Intent(context, NotificationReceiverService::class.java).apply {
|
|
31
31
|
this.action = action
|
|
32
|
-
putExtra(
|
|
33
|
-
putExtra(
|
|
32
|
+
putExtra(CallingxModuleImpl.EXTRA_CALL_ID, callId)
|
|
33
|
+
putExtra(CallingxModuleImpl.EXTRA_SOURCE, source)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
return PendingIntent.getService(
|
|
@@ -45,8 +45,8 @@ object NotificationIntentFactory {
|
|
|
45
45
|
val receiverIntent =
|
|
46
46
|
Intent(context, NotificationReceiverActivity::class.java).apply {
|
|
47
47
|
this.action = action
|
|
48
|
-
putExtra(
|
|
49
|
-
putExtra(
|
|
48
|
+
putExtra(CallingxModuleImpl.EXTRA_CALL_ID, callId)
|
|
49
|
+
putExtra(CallingxModuleImpl.EXTRA_SOURCE, source)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
val launchActivity = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
@@ -68,8 +68,8 @@ object NotificationIntentFactory {
|
|
|
68
68
|
val callIntent =
|
|
69
69
|
Intent(launchIntent).apply {
|
|
70
70
|
this.action = action
|
|
71
|
-
putExtra(
|
|
72
|
-
source?.let { putExtra(
|
|
71
|
+
putExtra(CallingxModuleImpl.EXTRA_CALL_ID, callId)
|
|
72
|
+
source?.let { putExtra(CallingxModuleImpl.EXTRA_SOURCE, it) }
|
|
73
73
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
74
74
|
}
|
|
75
75
|
|
|
@@ -90,7 +90,7 @@ object NotificationIntentFactory {
|
|
|
90
90
|
val intent =
|
|
91
91
|
Intent(action).apply {
|
|
92
92
|
setPackage(context.packageName)
|
|
93
|
-
putExtra(
|
|
93
|
+
putExtra(CallingxModuleImpl.EXTRA_CALL_ID, callId)
|
|
94
94
|
addExtras(this)
|
|
95
95
|
}
|
|
96
96
|
|
package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationReceiverActivity.kt
CHANGED
|
@@ -3,7 +3,7 @@ package io.getstream.rn.callingx.notifications
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.content.Intent
|
|
5
5
|
import android.os.Bundle
|
|
6
|
-
import io.getstream.rn.callingx.
|
|
6
|
+
import io.getstream.rn.callingx.CallingxModuleImpl
|
|
7
7
|
|
|
8
8
|
// For Android 12+
|
|
9
9
|
class NotificationReceiverActivity : Activity() {
|
|
@@ -27,14 +27,14 @@ class NotificationReceiverActivity : Activity() {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
//we need it only for answered call event, as for cold start case we need to send broadcast event and to launch the app
|
|
30
|
-
if (intent.action ==
|
|
31
|
-
val callId = intent.getStringExtra(
|
|
32
|
-
val source = intent.getStringExtra(
|
|
33
|
-
Intent(
|
|
30
|
+
if (intent.action == CallingxModuleImpl.CALL_ANSWERED_ACTION) {
|
|
31
|
+
val callId = intent.getStringExtra(CallingxModuleImpl.EXTRA_CALL_ID)
|
|
32
|
+
val source = intent.getStringExtra(CallingxModuleImpl.EXTRA_SOURCE)
|
|
33
|
+
Intent(CallingxModuleImpl.CALL_ANSWERED_ACTION)
|
|
34
34
|
.apply {
|
|
35
35
|
setPackage(packageName)
|
|
36
|
-
putExtra(
|
|
37
|
-
putExtra(
|
|
36
|
+
putExtra(CallingxModuleImpl.EXTRA_CALL_ID, callId)
|
|
37
|
+
putExtra(CallingxModuleImpl.EXTRA_SOURCE, source)
|
|
38
38
|
}
|
|
39
39
|
.also { sendBroadcast(it) }
|
|
40
40
|
}
|