@stream-io/react-native-callingx 0.1.0-beta.1 → 0.1.0-beta.3
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 +12 -8
- package/android/bin/src/main/AndroidManifest.xml +29 -0
- package/android/bin/src/main/java/io/getstream/rn/callingx/notifications/NotificationChannelsManager.kt +104 -0
- package/android/src/main/AndroidManifest.xml +0 -1
- package/android/src/main/java/io/getstream/rn/callingx/CallingxModule.kt +3 -3
- package/android/src/main/java/io/getstream/rn/callingx/notifications/NotificationChannelsManager.kt +9 -18
- package/dist/module/CallingxModule.js +2 -14
- package/dist/module/CallingxModule.js.map +1 -1
- package/dist/module/utils/constants.js +36 -9
- package/dist/module/utils/constants.js.map +1 -1
- package/dist/typescript/src/CallingxModule.d.ts +1 -5
- package/dist/typescript/src/CallingxModule.d.ts.map +1 -1
- package/dist/typescript/src/types.d.ts +41 -10
- package/dist/typescript/src/types.d.ts.map +1 -1
- package/dist/typescript/src/utils/constants.d.ts.map +1 -1
- package/ios/AudioSessionManager.swift +2 -7
- package/ios/CallingxImpl.swift +32 -27
- package/package.json +3 -3
- package/src/CallingxModule.ts +2 -30
- package/src/types.ts +35 -15
- package/src/utils/constants.ts +25 -16
- package/android/bin/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +0 -33
- package/android/bin/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +0 -18
- package/android/bin/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +0 -6
- package/android/bin/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +0 -4
- package/android/bin/build/intermediates/incremental/debug/packageDebugResources/merger.xml +0 -2
- package/android/bin/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +0 -60
- package/android/bin/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +0 -33
- package/android/bin/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +0 -1
- package/android/bin/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +0 -1
- package/android/bin/build/intermediates/packaged_res/debug/packageDebugResources/drawable/ic_phone_paused_24.xml +0 -11
- package/android/bin/build/intermediates/packaged_res/debug/packageDebugResources/drawable/ic_round_call_24.xml +0 -11
- package/android/bin/build/intermediates/packaged_res/debug/packageDebugResources/drawable/ic_user.xml +0 -27
- package/android/bin/build/outputs/logs/manifest-merger-debug-report.txt +0 -70
- package/android/bin/src/main/java/io/getstream/rn/callingx/CallService.kt +0 -402
- package/android/bin/src/main/java/io/getstream/rn/callingx/CallingxModule.kt +0 -644
- package/android/bin/src/main/java/io/getstream/rn/callingx/notifications/CallNotificationManager.kt +0 -245
- package/android/bin/src/main/java/io/getstream/rn/callingx/repo/CallRepository.kt +0 -163
- package/android/bin/src/main/java/io/getstream/rn/callingx/repo/LegacyCallRepository.kt +0 -139
- package/android/bin/src/main/java/io/getstream/rn/callingx/repo/TelecomCallRepository.kt +0 -432
- package/dist/module/utils/permissions.js +0 -86
- package/dist/module/utils/permissions.js.map +0 -1
- package/dist/typescript/src/utils/permissions.d.ts +0 -8
- package/dist/typescript/src/utils/permissions.d.ts.map +0 -1
- package/src/utils/permissions.ts +0 -111
|
@@ -1,432 +0,0 @@
|
|
|
1
|
-
package io.getstream.rn.callingx.repo
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.net.Uri
|
|
5
|
-
import android.os.Build
|
|
6
|
-
import android.os.Bundle
|
|
7
|
-
import android.telecom.DisconnectCause
|
|
8
|
-
import android.util.Log
|
|
9
|
-
import androidx.annotation.RequiresApi
|
|
10
|
-
import androidx.core.telecom.CallAttributesCompat
|
|
11
|
-
import androidx.core.telecom.CallControlResult
|
|
12
|
-
import androidx.core.telecom.CallControlScope
|
|
13
|
-
import androidx.core.telecom.CallsManager
|
|
14
|
-
import io.getstream.rn.callingx.debugLog
|
|
15
|
-
import io.getstream.rn.callingx.model.Call
|
|
16
|
-
import io.getstream.rn.callingx.model.CallAction
|
|
17
|
-
import kotlinx.coroutines.Job
|
|
18
|
-
import kotlinx.coroutines.cancel
|
|
19
|
-
import kotlinx.coroutines.channels.Channel
|
|
20
|
-
import kotlinx.coroutines.flow.Flow
|
|
21
|
-
import kotlinx.coroutines.flow.consumeAsFlow
|
|
22
|
-
import kotlinx.coroutines.flow.launchIn
|
|
23
|
-
import kotlinx.coroutines.flow.onEach
|
|
24
|
-
import kotlinx.coroutines.flow.scan
|
|
25
|
-
import kotlinx.coroutines.launch
|
|
26
|
-
import kotlinx.coroutines.sync.withLock
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* The central repository that keeps track of the current call and allows to register new calls.
|
|
30
|
-
*
|
|
31
|
-
* This class contains the main logic to integrate with Telecom SDK.
|
|
32
|
-
*
|
|
33
|
-
* @see registerCall
|
|
34
|
-
*/
|
|
35
|
-
@RequiresApi(Build.VERSION_CODES.O)
|
|
36
|
-
class TelecomCallRepository(context: Context) : CallRepository(context) {
|
|
37
|
-
|
|
38
|
-
companion object {
|
|
39
|
-
private const val TAG = "[Callingx] TelecomCallRepository"
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private var observeCallStateJob: Job? = null
|
|
43
|
-
|
|
44
|
-
private val callsManager: CallsManager
|
|
45
|
-
private var isSelfAnswered = false
|
|
46
|
-
private var isSelfDisconnected = false
|
|
47
|
-
|
|
48
|
-
init {
|
|
49
|
-
val capabilities =
|
|
50
|
-
CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
|
|
51
|
-
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING
|
|
52
|
-
callsManager =
|
|
53
|
-
CallsManager(context.applicationContext).apply {
|
|
54
|
-
registerAppWithTelecom(capabilities)
|
|
55
|
-
}
|
|
56
|
-
debugLog(TAG, "[repository] init: CallsManager created and registered")
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
override fun getTag(): String = TAG
|
|
60
|
-
|
|
61
|
-
override fun setListener(listener: Listener?) {
|
|
62
|
-
this._listener = listener
|
|
63
|
-
|
|
64
|
-
observeCallStateJob?.cancel()
|
|
65
|
-
observeCallStateJob = observeCallState()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
override fun release() {
|
|
69
|
-
val currentCall = currentCall.value
|
|
70
|
-
if (currentCall is Call.Registered) {
|
|
71
|
-
currentCall.processAction(CallAction.Disconnect(DisconnectCause(DisconnectCause.LOCAL)))
|
|
72
|
-
}
|
|
73
|
-
_currentCall.value = Call.None
|
|
74
|
-
|
|
75
|
-
observeCallStateJob?.cancel()
|
|
76
|
-
observeCallStateJob = null
|
|
77
|
-
_listener = null
|
|
78
|
-
|
|
79
|
-
scope.cancel()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Register a new call with the provided attributes. Use the [currentCall] StateFlow to receive
|
|
84
|
-
* status updates and process call related actions.
|
|
85
|
-
*/
|
|
86
|
-
override suspend fun registerCall(
|
|
87
|
-
callId: String,
|
|
88
|
-
displayName: String,
|
|
89
|
-
address: Uri,
|
|
90
|
-
isIncoming: Boolean,
|
|
91
|
-
isVideo: Boolean,
|
|
92
|
-
displayOptions: Bundle?,
|
|
93
|
-
) {
|
|
94
|
-
debugLog(
|
|
95
|
-
TAG,
|
|
96
|
-
"[repository] registerCall: Starting registration - Name: $displayName, Address: $address, Incoming: $isIncoming"
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
registrationMutex.withLock {
|
|
100
|
-
// For simplicity we don't support multiple calls
|
|
101
|
-
if (_currentCall.value is Call.Registered) {
|
|
102
|
-
Log.w(
|
|
103
|
-
TAG,
|
|
104
|
-
"[repository] registerCall: Call already registered, ignoring new call request"
|
|
105
|
-
)
|
|
106
|
-
return@withLock
|
|
107
|
-
}
|
|
108
|
-
debugLog(
|
|
109
|
-
TAG,
|
|
110
|
-
"[repository] registerCall: No existing call found, proceeding with registration"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
val attributes = createCallAttributes(displayName, address, isIncoming, isVideo)
|
|
114
|
-
val actionSource = Channel<CallAction>()
|
|
115
|
-
|
|
116
|
-
// Register the call and handle actions in the scope
|
|
117
|
-
try {
|
|
118
|
-
callsManager.addCall(
|
|
119
|
-
attributes,
|
|
120
|
-
onIsCallAnswered, // Watch needs to know if it can answer the call
|
|
121
|
-
onIsCallDisconnected,
|
|
122
|
-
onIsCallActive,
|
|
123
|
-
onIsCallInactive
|
|
124
|
-
) {
|
|
125
|
-
debugLog(
|
|
126
|
-
TAG,
|
|
127
|
-
"[repository] registerCall: Inside call scope, setting up call handlers"
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
// Consume the actions to interact with the call inside the scope
|
|
131
|
-
launch { processCallActions(actionSource.consumeAsFlow()) }
|
|
132
|
-
|
|
133
|
-
// Update the state to registered with default values while waiting for Telecom
|
|
134
|
-
// updates
|
|
135
|
-
debugLog(
|
|
136
|
-
TAG,
|
|
137
|
-
"[repository] registerCall: Creating Registered call state with ID: $callId"
|
|
138
|
-
)
|
|
139
|
-
_currentCall.value =
|
|
140
|
-
Call.Registered(
|
|
141
|
-
id = callId,
|
|
142
|
-
isActive = false, // can we just register the call as active?
|
|
143
|
-
isOnHold = false,
|
|
144
|
-
callAttributes = attributes,
|
|
145
|
-
displayOptions = displayOptions,
|
|
146
|
-
isMuted = false,
|
|
147
|
-
errorCode = null,
|
|
148
|
-
currentCallEndpoint = null,
|
|
149
|
-
availableCallEndpoints = emptyList(),
|
|
150
|
-
actionSource = actionSource,
|
|
151
|
-
)
|
|
152
|
-
debugLog(TAG, "[repository] registerCall: Call state updated to Registered")
|
|
153
|
-
|
|
154
|
-
launch {
|
|
155
|
-
currentCallEndpoint.collect {
|
|
156
|
-
updateCurrentCall { copy(currentCallEndpoint = it) }
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
launch {
|
|
160
|
-
availableEndpoints.collect {
|
|
161
|
-
updateCurrentCall { copy(availableCallEndpoints = it) }
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
launch { isMuted.collect { updateCurrentCall { copy(isMuted = it) } } }
|
|
165
|
-
}
|
|
166
|
-
debugLog(
|
|
167
|
-
TAG,
|
|
168
|
-
"[repository] registerCall: Call successfully registered with Telecom SDK"
|
|
169
|
-
)
|
|
170
|
-
} catch (e: Exception) {
|
|
171
|
-
Log.e(TAG, "[repository] registerCall: Error registering call", e)
|
|
172
|
-
throw e
|
|
173
|
-
} finally {
|
|
174
|
-
debugLog(TAG, "[repository] registerCall: Call scope ended, setting state to None")
|
|
175
|
-
_currentCall.value = Call.None
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
override fun updateCall(
|
|
181
|
-
callId: String,
|
|
182
|
-
displayName: String,
|
|
183
|
-
address: Uri,
|
|
184
|
-
isVideo: Boolean,
|
|
185
|
-
displayOptions: Bundle?,
|
|
186
|
-
) {
|
|
187
|
-
debugLog(
|
|
188
|
-
TAG,
|
|
189
|
-
"[repository] updateCall: Starting update - Name: $displayName, Address: $address, IsVideo: $isVideo"
|
|
190
|
-
)
|
|
191
|
-
super.updateCall(callId, displayName, address, isVideo, displayOptions)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private fun observeCallState(): Job {
|
|
195
|
-
return currentCall
|
|
196
|
-
.scan(Pair<Call?, Call>(null, currentCall.value)) { (_, prev), next ->
|
|
197
|
-
Pair(prev, next)
|
|
198
|
-
}
|
|
199
|
-
.onEach { (previous, current) ->
|
|
200
|
-
when {
|
|
201
|
-
previous is Call.None && current is Call.Registered -> {
|
|
202
|
-
_listener?.onCallRegistered(current.id, current.isIncoming())
|
|
203
|
-
}
|
|
204
|
-
previous is Call.Registered && current is Call.Registered -> {
|
|
205
|
-
if (previous.isMuted != current.isMuted) {
|
|
206
|
-
debugLog(
|
|
207
|
-
TAG,
|
|
208
|
-
"[repository] observeCallState: Mute changed: ${current.isMuted}"
|
|
209
|
-
)
|
|
210
|
-
_listener?.onMuteCallChanged(current.id, current.isMuted)
|
|
211
|
-
}
|
|
212
|
-
if (previous.currentCallEndpoint != current.currentCallEndpoint) {
|
|
213
|
-
current.currentCallEndpoint?.let {
|
|
214
|
-
_listener?.onCallEndpointChanged(current.id, it.name.toString())
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
_listener?.onCallStateChanged(current)
|
|
220
|
-
}
|
|
221
|
-
.launchIn(scope)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/** Collect the action source to handle client actions inside the call scope */
|
|
225
|
-
private suspend fun CallControlScope.processCallActions(actionSource: Flow<CallAction>) {
|
|
226
|
-
actionSource.collect { action ->
|
|
227
|
-
debugLog(TAG, "[repository] processCallActions: action: ${action::class.simpleName}")
|
|
228
|
-
when (action) {
|
|
229
|
-
is CallAction.Answer -> {
|
|
230
|
-
doAnswer(action.isAudioCall)
|
|
231
|
-
}
|
|
232
|
-
is CallAction.Disconnect -> {
|
|
233
|
-
doDisconnect(action)
|
|
234
|
-
}
|
|
235
|
-
is CallAction.SwitchAudioEndpoint -> {
|
|
236
|
-
doSwitchEndpoint(action)
|
|
237
|
-
}
|
|
238
|
-
is CallAction.TransferCall -> {
|
|
239
|
-
debugLog(
|
|
240
|
-
TAG,
|
|
241
|
-
"[repository] processCallActions: Transfer to endpoint: ${action.endpointId}"
|
|
242
|
-
)
|
|
243
|
-
val call = _currentCall.value as? Call.Registered
|
|
244
|
-
val endpoints =
|
|
245
|
-
call?.availableCallEndpoints?.firstOrNull {
|
|
246
|
-
it.identifier == action.endpointId
|
|
247
|
-
}
|
|
248
|
-
if (endpoints != null) {
|
|
249
|
-
requestEndpointChange(
|
|
250
|
-
endpoint = endpoints,
|
|
251
|
-
)
|
|
252
|
-
} else {
|
|
253
|
-
Log.w(
|
|
254
|
-
TAG,
|
|
255
|
-
"[repository] processCallActions: Endpoint not found for transfer, ignoring"
|
|
256
|
-
)
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
CallAction.Hold -> {
|
|
260
|
-
when (val result = setInactive()) {
|
|
261
|
-
is CallControlResult.Success -> {
|
|
262
|
-
onIsCallInactive()
|
|
263
|
-
}
|
|
264
|
-
is CallControlResult.Error -> {
|
|
265
|
-
Log.e(
|
|
266
|
-
TAG,
|
|
267
|
-
"[repository] processCallActions: Hold action failed with error code: ${result.errorCode}"
|
|
268
|
-
)
|
|
269
|
-
updateCurrentCall { copy(errorCode = result.errorCode) }
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
CallAction.Activate -> {
|
|
274
|
-
when (val result = setActive()) {
|
|
275
|
-
is CallControlResult.Success -> {
|
|
276
|
-
onIsCallActive()
|
|
277
|
-
}
|
|
278
|
-
is CallControlResult.Error -> {
|
|
279
|
-
Log.e(
|
|
280
|
-
TAG,
|
|
281
|
-
"[repository] processCallActions: Activate action failed with error code: ${result.errorCode}"
|
|
282
|
-
)
|
|
283
|
-
updateCurrentCall { copy(errorCode = result.errorCode) }
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
is CallAction.ToggleMute -> {
|
|
288
|
-
// We cannot programmatically mute the telecom stack. Instead we just update
|
|
289
|
-
// the state of the call and this will start/stop audio capturing.
|
|
290
|
-
debugLog(TAG, "[repository] processCallActions: Toggling mute: ${action.isMute}")
|
|
291
|
-
updateCurrentCall {
|
|
292
|
-
val newMutedState = action.isMute
|
|
293
|
-
copy(isMuted = newMutedState)
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
debugLog(TAG, "[repository] processCallActions: Action collection ended")
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
private suspend fun CallControlScope.doSwitchEndpoint(action: CallAction.SwitchAudioEndpoint) {
|
|
303
|
-
debugLog(TAG, "[repository] doSwitchEndpoint: Switching to endpoint: ${action.endpointId}")
|
|
304
|
-
if (_currentCall.value !is Call.Registered) {
|
|
305
|
-
Log.w(TAG, "[repository] doSwitchEndpoint: Call not registered, ignoring")
|
|
306
|
-
return
|
|
307
|
-
}
|
|
308
|
-
// TODO once availableCallEndpoints is a state flow we can just get the value
|
|
309
|
-
val endpoints = (_currentCall.value as Call.Registered).availableCallEndpoints
|
|
310
|
-
// Switch to the given endpoint or fallback to the best possible one.
|
|
311
|
-
val newEndpoint = endpoints.firstOrNull { it.identifier == action.endpointId }
|
|
312
|
-
|
|
313
|
-
if (newEndpoint != null) {
|
|
314
|
-
debugLog(
|
|
315
|
-
TAG,
|
|
316
|
-
"[repository] doSwitchEndpoint: Found endpoint: ${newEndpoint.name}, requesting change"
|
|
317
|
-
)
|
|
318
|
-
requestEndpointChange(newEndpoint).also {
|
|
319
|
-
debugLog(TAG, "[repository] doSwitchEndpoint: Endpoint change result: $it")
|
|
320
|
-
}
|
|
321
|
-
} else {
|
|
322
|
-
Log.w(TAG, "[repository] doSwitchEndpoint: Endpoint not found in available endpoints")
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
private suspend fun CallControlScope.doDisconnect(action: CallAction.Disconnect) {
|
|
327
|
-
isSelfDisconnected = true
|
|
328
|
-
debugLog(TAG, "[repository] doDisconnect: Disconnecting call with cause: ${action.cause}")
|
|
329
|
-
disconnect(action.cause)
|
|
330
|
-
debugLog(TAG, "[repository] doDisconnect: Disconnect called, triggering onIsCallDisconnected")
|
|
331
|
-
onIsCallDisconnected(action.cause)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
private suspend fun CallControlScope.doAnswer(isAudioCall: Boolean) {
|
|
335
|
-
isSelfAnswered = true
|
|
336
|
-
val callType =
|
|
337
|
-
if (isAudioCall) CallAttributesCompat.CALL_TYPE_AUDIO_CALL
|
|
338
|
-
else CallAttributesCompat.CALL_TYPE_VIDEO_CALL
|
|
339
|
-
|
|
340
|
-
when (val result = answer(callType)) {
|
|
341
|
-
is CallControlResult.Success -> {
|
|
342
|
-
onIsCallAnswered(callType)
|
|
343
|
-
}
|
|
344
|
-
is CallControlResult.Error -> {
|
|
345
|
-
Log.e(
|
|
346
|
-
TAG,
|
|
347
|
-
"[repository] doAnswer: Answer failed with error code: ${result.errorCode}"
|
|
348
|
-
)
|
|
349
|
-
isSelfAnswered = false
|
|
350
|
-
updateCurrentCall {
|
|
351
|
-
Call.Unregistered(
|
|
352
|
-
id = id,
|
|
353
|
-
callAttributes = callAttributes,
|
|
354
|
-
disconnectCause = DisconnectCause(DisconnectCause.BUSY),
|
|
355
|
-
)
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Can the call be successfully answered?? TIP: We would check the connection/call state to see
|
|
363
|
-
* if we can answer a call Example you may need to wait for another call to hold.
|
|
364
|
-
*/
|
|
365
|
-
val onIsCallAnswered: suspend (type: Int) -> Unit = {
|
|
366
|
-
debugLog(
|
|
367
|
-
TAG,
|
|
368
|
-
"[repository] onIsCallAnswered: Call answered, type: $it, isSelfAnswered: $isSelfAnswered"
|
|
369
|
-
)
|
|
370
|
-
updateCurrentCall { copy(isActive = true, isOnHold = false) }
|
|
371
|
-
|
|
372
|
-
val call = _currentCall.value
|
|
373
|
-
val source = if (isSelfAnswered) EventSource.APP else EventSource.SYS
|
|
374
|
-
if (call is Call.Registered) {
|
|
375
|
-
_listener?.onIsCallAnswered(call.id, source)
|
|
376
|
-
}
|
|
377
|
-
isSelfAnswered = false
|
|
378
|
-
debugLog(TAG, "[repository] onIsCallAnswered: Call state updated to active")
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/** Can the call perform a disconnect */
|
|
382
|
-
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {
|
|
383
|
-
debugLog(
|
|
384
|
-
TAG,
|
|
385
|
-
"[repository] onIsCallDisconnected: Call disconnected, cause: ${it.reason}, description: ${it.description}"
|
|
386
|
-
)
|
|
387
|
-
val source = if (isSelfDisconnected) EventSource.APP else EventSource.SYS
|
|
388
|
-
var callId: String? = null
|
|
389
|
-
if (_currentCall.value is Call.Registered) {
|
|
390
|
-
callId = (_currentCall.value as Call.Registered).id
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
updateCurrentCall { Call.Unregistered(id, callAttributes, it) }
|
|
394
|
-
_listener?.onIsCallDisconnected(callId, it, source)
|
|
395
|
-
isSelfDisconnected = false
|
|
396
|
-
debugLog(TAG, "[repository] onIsCallDisconnected: Call state updated to Unregistered")
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Check is see if we can make the call active. Other calls and state might stop us from
|
|
401
|
-
* activating the call
|
|
402
|
-
*/
|
|
403
|
-
val onIsCallActive: suspend () -> Unit = {
|
|
404
|
-
debugLog(TAG, "[repository] onIsCallActive: Call became active")
|
|
405
|
-
updateCurrentCall {
|
|
406
|
-
copy(
|
|
407
|
-
errorCode = null,
|
|
408
|
-
isActive = true,
|
|
409
|
-
isOnHold = false,
|
|
410
|
-
)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
val call = _currentCall.value
|
|
414
|
-
if (call is Call.Registered) {
|
|
415
|
-
_listener?.onIsCallActive(call.id)
|
|
416
|
-
}
|
|
417
|
-
debugLog(TAG, "[repository] onIsCallActive: Call state updated")
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/** Check to see if we can make the call inactivate */
|
|
421
|
-
val onIsCallInactive: suspend () -> Unit = {
|
|
422
|
-
debugLog(TAG, "[repository] onIsCallInactive: Call became inactive (on hold)")
|
|
423
|
-
updateCurrentCall { copy(errorCode = null, isOnHold = true) }
|
|
424
|
-
|
|
425
|
-
val call = _currentCall.value
|
|
426
|
-
if (call is Call.Registered) {
|
|
427
|
-
_listener?.onIsCallInactive(call.id)
|
|
428
|
-
}
|
|
429
|
-
debugLog(TAG, "[repository] onIsCallInactive: Call state updated to on hold")
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { Platform, PermissionsAndroid } from 'react-native';
|
|
4
|
-
const allowedPostNotifications = Platform.OS === 'android' && Platform.Version < 33;
|
|
5
|
-
export const requestCallPermissions = async () => {
|
|
6
|
-
if (Platform.OS !== 'android') {
|
|
7
|
-
return {
|
|
8
|
-
recordAudio: true,
|
|
9
|
-
postNotifications: true
|
|
10
|
-
}; // iOS handles permissions differently
|
|
11
|
-
}
|
|
12
|
-
const permissions = [PermissionsAndroid.PERMISSIONS.RECORD_AUDIO];
|
|
13
|
-
|
|
14
|
-
// Add POST_NOTIFICATIONS for Android 13+
|
|
15
|
-
if (Platform.Version >= 33) {
|
|
16
|
-
permissions.push(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Add WRITE_CALL_LOG for call history (required for Android 6.0+)
|
|
20
|
-
// Note: PermissionsAndroid doesn't have a constant for this, so we use the string directly
|
|
21
|
-
// permissions.push('android.permission.WRITE_CALL_LOG');
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const results = await PermissionsAndroid.requestMultiple(permissions);
|
|
25
|
-
|
|
26
|
-
// Check if all permissions are granted
|
|
27
|
-
const allGranted = Object.values(results).every(status => status === PermissionsAndroid.RESULTS.GRANTED);
|
|
28
|
-
if (!allGranted) {
|
|
29
|
-
const deniedPermissions = Object.entries(results).filter(([, status]) => status !== PermissionsAndroid.RESULTS.GRANTED).map(([permission]) => permission);
|
|
30
|
-
console.warn('Denied permissions:', deniedPermissions);
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
recordAudio: results[PermissionsAndroid.PERMISSIONS.RECORD_AUDIO] === PermissionsAndroid.RESULTS.GRANTED,
|
|
34
|
-
postNotifications: results[PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS] === PermissionsAndroid.RESULTS.GRANTED || allowedPostNotifications
|
|
35
|
-
};
|
|
36
|
-
} catch (err) {
|
|
37
|
-
console.warn('Error requesting permissions:', err);
|
|
38
|
-
return {
|
|
39
|
-
recordAudio: false,
|
|
40
|
-
postNotifications: false
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
export const checkCallPermissions = async () => {
|
|
45
|
-
if (Platform.OS !== 'android') {
|
|
46
|
-
return {
|
|
47
|
-
recordAudio: true,
|
|
48
|
-
postNotifications: true
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
const permissions = [PermissionsAndroid.PERMISSIONS.RECORD_AUDIO];
|
|
52
|
-
if (Platform.Version >= 33) {
|
|
53
|
-
permissions.push(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
|
|
54
|
-
}
|
|
55
|
-
try {
|
|
56
|
-
const results = await Promise.all(permissions.map(async permission => ({
|
|
57
|
-
permission,
|
|
58
|
-
granted: await PermissionsAndroid.check(permission)
|
|
59
|
-
})));
|
|
60
|
-
const resultsObject = results.reduce((acc, {
|
|
61
|
-
permission,
|
|
62
|
-
granted
|
|
63
|
-
}) => {
|
|
64
|
-
acc[permission] = granted;
|
|
65
|
-
return acc;
|
|
66
|
-
}, {});
|
|
67
|
-
return {
|
|
68
|
-
recordAudio: resultsObject[PermissionsAndroid.PERMISSIONS.RECORD_AUDIO],
|
|
69
|
-
postNotifications: resultsObject[PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS] || allowedPostNotifications
|
|
70
|
-
};
|
|
71
|
-
} catch (err) {
|
|
72
|
-
console.warn('Error checking permissions:', err);
|
|
73
|
-
return {
|
|
74
|
-
recordAudio: false,
|
|
75
|
-
postNotifications: false
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
export const requestPostNotificationPermissions = async () => {
|
|
80
|
-
if (Platform.OS !== 'android') {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
const results = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
|
|
84
|
-
return results === PermissionsAndroid.RESULTS.GRANTED || allowedPostNotifications;
|
|
85
|
-
};
|
|
86
|
-
//# sourceMappingURL=permissions.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"names":["Platform","PermissionsAndroid","allowedPostNotifications","OS","Version","requestCallPermissions","recordAudio","postNotifications","permissions","PERMISSIONS","RECORD_AUDIO","push","POST_NOTIFICATIONS","results","requestMultiple","allGranted","Object","values","every","status","RESULTS","GRANTED","deniedPermissions","entries","filter","map","permission","console","warn","err","checkCallPermissions","Promise","all","granted","check","resultsObject","reduce","acc","requestPostNotificationPermissions","request"],"sourceRoot":"../../../src","sources":["utils/permissions.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface PermissionsResult {
|
|
2
|
-
recordAudio: boolean;
|
|
3
|
-
postNotifications: boolean;
|
|
4
|
-
}
|
|
5
|
-
export declare const requestCallPermissions: () => Promise<PermissionsResult>;
|
|
6
|
-
export declare const checkCallPermissions: () => Promise<PermissionsResult>;
|
|
7
|
-
export declare const requestPostNotificationPermissions: () => Promise<boolean>;
|
|
8
|
-
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../../../src/utils/permissions.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD,eAAO,MAAM,sBAAsB,QAAa,OAAO,CAAC,iBAAiB,CA8CxE,CAAC;AAEF,eAAO,MAAM,oBAAoB,QAAa,OAAO,CAAC,iBAAiB,CAqCtE,CAAC;AAEF,eAAO,MAAM,kCAAkC,QACnC,OAAO,CAAC,OAAO,CAWxB,CAAC"}
|
package/src/utils/permissions.ts
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { Platform, PermissionsAndroid } from 'react-native';
|
|
2
|
-
import type { Permission } from 'react-native';
|
|
3
|
-
|
|
4
|
-
export interface PermissionsResult {
|
|
5
|
-
recordAudio: boolean;
|
|
6
|
-
postNotifications: boolean;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const allowedPostNotifications =
|
|
10
|
-
Platform.OS === 'android' && Platform.Version < 33;
|
|
11
|
-
|
|
12
|
-
export const requestCallPermissions = async (): Promise<PermissionsResult> => {
|
|
13
|
-
if (Platform.OS !== 'android') {
|
|
14
|
-
return { recordAudio: true, postNotifications: true }; // iOS handles permissions differently
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const permissions: string[] = [PermissionsAndroid.PERMISSIONS.RECORD_AUDIO];
|
|
18
|
-
|
|
19
|
-
// Add POST_NOTIFICATIONS for Android 13+
|
|
20
|
-
if (Platform.Version >= 33) {
|
|
21
|
-
permissions.push(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Add WRITE_CALL_LOG for call history (required for Android 6.0+)
|
|
25
|
-
// Note: PermissionsAndroid doesn't have a constant for this, so we use the string directly
|
|
26
|
-
// permissions.push('android.permission.WRITE_CALL_LOG');
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
const results = await PermissionsAndroid.requestMultiple(
|
|
30
|
-
permissions as Permission[],
|
|
31
|
-
);
|
|
32
|
-
|
|
33
|
-
// Check if all permissions are granted
|
|
34
|
-
const allGranted = Object.values(results).every(
|
|
35
|
-
(status) => status === PermissionsAndroid.RESULTS.GRANTED,
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
if (!allGranted) {
|
|
39
|
-
const deniedPermissions = Object.entries(results)
|
|
40
|
-
.filter(([, status]) => status !== PermissionsAndroid.RESULTS.GRANTED)
|
|
41
|
-
.map(([permission]) => permission);
|
|
42
|
-
|
|
43
|
-
console.warn('Denied permissions:', deniedPermissions);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return {
|
|
47
|
-
recordAudio:
|
|
48
|
-
results[PermissionsAndroid.PERMISSIONS.RECORD_AUDIO] ===
|
|
49
|
-
PermissionsAndroid.RESULTS.GRANTED,
|
|
50
|
-
postNotifications:
|
|
51
|
-
results[PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS] ===
|
|
52
|
-
PermissionsAndroid.RESULTS.GRANTED || allowedPostNotifications,
|
|
53
|
-
};
|
|
54
|
-
} catch (err) {
|
|
55
|
-
console.warn('Error requesting permissions:', err);
|
|
56
|
-
return { recordAudio: false, postNotifications: false };
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const checkCallPermissions = async (): Promise<PermissionsResult> => {
|
|
61
|
-
if (Platform.OS !== 'android') {
|
|
62
|
-
return { recordAudio: true, postNotifications: true };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const permissions: string[] = [PermissionsAndroid.PERMISSIONS.RECORD_AUDIO];
|
|
66
|
-
|
|
67
|
-
if (Platform.Version >= 33) {
|
|
68
|
-
permissions.push(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const results = await Promise.all(
|
|
73
|
-
permissions.map(async (permission) => ({
|
|
74
|
-
permission,
|
|
75
|
-
granted: await PermissionsAndroid.check(permission as Permission),
|
|
76
|
-
})),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const resultsObject = results.reduce(
|
|
80
|
-
(acc, { permission, granted }) => {
|
|
81
|
-
acc[permission as Permission] = granted;
|
|
82
|
-
return acc;
|
|
83
|
-
},
|
|
84
|
-
{} as { [key in Permission]: boolean },
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
recordAudio: resultsObject[PermissionsAndroid.PERMISSIONS.RECORD_AUDIO],
|
|
89
|
-
postNotifications:
|
|
90
|
-
resultsObject[PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS] ||
|
|
91
|
-
allowedPostNotifications,
|
|
92
|
-
};
|
|
93
|
-
} catch (err) {
|
|
94
|
-
console.warn('Error checking permissions:', err);
|
|
95
|
-
return { recordAudio: false, postNotifications: false };
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
export const requestPostNotificationPermissions =
|
|
100
|
-
async (): Promise<boolean> => {
|
|
101
|
-
if (Platform.OS !== 'android') {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const results = await PermissionsAndroid.request(
|
|
106
|
-
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
|
|
107
|
-
);
|
|
108
|
-
return (
|
|
109
|
-
results === PermissionsAndroid.RESULTS.GRANTED || allowedPostNotifications
|
|
110
|
-
);
|
|
111
|
-
};
|