@capgo/capacitor-stream-call 0.0.81 → 0.0.83
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/android/src/main/java/ee/forgr/capacitor/streamcall/CustomNotificationHandler.kt +2 -2
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallFragment.kt +8 -36
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +290 -308
- package/android/src/main/java/ee/forgr/capacitor/streamcall/TouchInterceptWrapper.kt +5 -3
- package/android/src/main/java/ee/forgr/capacitor/streamcall/UserRepository.kt +4 -5
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +0 -4
- package/package.json +1 -1
- package/android/src/main/java/ee/forgr/capacitor/streamcall/RingtonePlayer.kt +0 -162
|
@@ -20,15 +20,14 @@ import android.os.Bundle
|
|
|
20
20
|
import android.os.Handler
|
|
21
21
|
import android.os.Looper
|
|
22
22
|
import android.provider.Settings
|
|
23
|
+
import android.util.Log
|
|
23
24
|
import android.view.View
|
|
24
25
|
import android.view.ViewGroup
|
|
25
26
|
import android.view.WindowManager
|
|
26
27
|
import android.widget.FrameLayout
|
|
27
28
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
28
29
|
import androidx.compose.runtime.collectAsState
|
|
29
|
-
import androidx.compose.runtime.derivedStateOf
|
|
30
30
|
import androidx.compose.runtime.getValue
|
|
31
|
-
import androidx.compose.runtime.remember
|
|
32
31
|
import androidx.compose.ui.Modifier
|
|
33
32
|
import androidx.compose.ui.draw.clip
|
|
34
33
|
import androidx.compose.ui.platform.ComposeView
|
|
@@ -87,11 +86,13 @@ import kotlinx.coroutines.launch
|
|
|
87
86
|
import kotlinx.coroutines.tasks.await
|
|
88
87
|
import androidx.core.net.toUri
|
|
89
88
|
import org.json.JSONObject
|
|
89
|
+
import androidx.core.graphics.toColorInt
|
|
90
|
+
import androidx.core.content.edit
|
|
90
91
|
|
|
91
92
|
// I am not a religious pearson, but at this point, I am not sure even god himself would understand this code
|
|
92
93
|
// It's a spaghetti-like, tangled, unreadable mess and frankly, I am deeply sorry for the code crimes commited in the Android impl
|
|
93
94
|
@CapacitorPlugin(name = "StreamCall")
|
|
94
|
-
|
|
95
|
+
class StreamCallPlugin : Plugin() {
|
|
95
96
|
private var streamVideoClient: StreamVideo? = null
|
|
96
97
|
private var state: State = State.NOT_INITIALIZED
|
|
97
98
|
private var overlayView: ComposeView? = null
|
|
@@ -108,6 +109,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
108
109
|
private var activeCallStateJob: Job? = null
|
|
109
110
|
private var cameraStatusJob: Job? = null
|
|
110
111
|
private var microphoneStatusJob: Job? = null
|
|
112
|
+
private var lastEventSent: String? = null
|
|
111
113
|
|
|
112
114
|
// Store current call info
|
|
113
115
|
private var currentCallId: String = ""
|
|
@@ -116,7 +118,6 @@ public class StreamCallPlugin : Plugin() {
|
|
|
116
118
|
|
|
117
119
|
// Add a field for the fragment
|
|
118
120
|
private var callFragment: StreamCallFragment? = null
|
|
119
|
-
private var streamVideo: StreamVideo? = null
|
|
120
121
|
private var touchInterceptWrapper: TouchInterceptWrapper? = null
|
|
121
122
|
|
|
122
123
|
// Track permission request timing and attempts
|
|
@@ -138,7 +139,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
138
139
|
INITIALIZED
|
|
139
140
|
}
|
|
140
141
|
|
|
141
|
-
|
|
142
|
+
fun incomingOnlyRingingConfig(): RingingConfig = object : RingingConfig {
|
|
142
143
|
override val incomingCallSoundUri: Uri? = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
|
|
143
144
|
override val outgoingCallSoundUri: Uri? = null
|
|
144
145
|
}
|
|
@@ -154,41 +155,41 @@ public class StreamCallPlugin : Plugin() {
|
|
|
154
155
|
override fun handleOnResume() {
|
|
155
156
|
super.handleOnResume()
|
|
156
157
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
Log.d("StreamCallPlugin", "handleOnResume: App resumed, checking permissions and pending operations")
|
|
159
|
+
Log.d("StreamCallPlugin", "handleOnResume: Have pendingCall: ${pendingCall != null}")
|
|
160
|
+
Log.d("StreamCallPlugin", "handleOnResume: Have pendingCallUserIds: ${pendingCallUserIds != null}")
|
|
161
|
+
Log.d("StreamCallPlugin", "handleOnResume: Have pendingAcceptCall: ${pendingAcceptCall != null}")
|
|
162
|
+
Log.d("StreamCallPlugin", "handleOnResume: Permission attempt count: $permissionAttemptCount")
|
|
162
163
|
|
|
163
164
|
// Check if permissions were granted after returning from settings or permission dialog
|
|
164
165
|
if (checkPermissions()) {
|
|
165
|
-
|
|
166
|
+
Log.d("StreamCallPlugin", "handleOnResume: Permissions are now granted")
|
|
166
167
|
// Handle any pending calls that were waiting for permissions
|
|
167
168
|
handlePermissionGranted()
|
|
168
169
|
} else if (pendingCall != null || pendingAcceptCall != null) {
|
|
169
|
-
|
|
170
|
+
Log.d("StreamCallPlugin", "handleOnResume: Permissions still not granted, but have pending operations")
|
|
170
171
|
// If we have pending operations but permissions are still not granted,
|
|
171
172
|
// it means the permission dialog was dismissed without granting
|
|
172
173
|
// We should trigger our retry logic if we haven't exhausted attempts
|
|
173
174
|
if (permissionAttemptCount > 0) {
|
|
174
|
-
|
|
175
|
+
Log.d("StreamCallPlugin", "handleOnResume: Permission dialog was dismissed, treating as denial (attempt: $permissionAttemptCount)")
|
|
175
176
|
val timeSinceRequest = System.currentTimeMillis() - permissionRequestStartTime
|
|
176
177
|
handlePermissionDenied(timeSinceRequest)
|
|
177
178
|
} else {
|
|
178
|
-
|
|
179
|
+
Log.d("StreamCallPlugin", "handleOnResume: No permission attempts yet, starting permission request")
|
|
179
180
|
// If we have pending operations but no attempts yet, start the permission flow
|
|
180
181
|
if (pendingAcceptCall != null) {
|
|
181
|
-
|
|
182
|
+
Log.d("StreamCallPlugin", "handleOnResume: Have active call waiting for permissions, requesting now")
|
|
182
183
|
permissionAttemptCount = 0
|
|
183
184
|
requestPermissions()
|
|
184
185
|
} else if (pendingCall != null && pendingCallUserIds != null) {
|
|
185
|
-
|
|
186
|
+
Log.d("StreamCallPlugin", "handleOnResume: Have outgoing call waiting for permissions, requesting now")
|
|
186
187
|
permissionAttemptCount = 0
|
|
187
188
|
requestPermissions()
|
|
188
189
|
}
|
|
189
190
|
}
|
|
190
191
|
} else {
|
|
191
|
-
|
|
192
|
+
Log.d("StreamCallPlugin", "handleOnResume: No pending operations, nothing to handle")
|
|
192
193
|
}
|
|
193
194
|
}
|
|
194
195
|
|
|
@@ -201,36 +202,36 @@ public class StreamCallPlugin : Plugin() {
|
|
|
201
202
|
// Register broadcast receiver for ACCEPT_CALL action with high priority
|
|
202
203
|
val filter = IntentFilter("io.getstream.video.android.action.ACCEPT_CALL")
|
|
203
204
|
filter.priority = 999 // Set high priority to ensure it captures the intent
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
ContextCompat.registerReceiver(activity, acceptCallReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
|
206
|
+
Log.d("StreamCallPlugin", "Registered broadcast receiver for ACCEPT_CALL action with high priority")
|
|
206
207
|
|
|
207
208
|
// Start the background service to keep the app alive
|
|
208
209
|
val serviceIntent = Intent(activity, StreamCallBackgroundService::class.java)
|
|
209
210
|
activity.startService(serviceIntent)
|
|
210
|
-
|
|
211
|
+
Log.d("StreamCallPlugin", "Started StreamCallBackgroundService to keep app alive")
|
|
211
212
|
}
|
|
212
213
|
|
|
213
214
|
@OptIn(DelicateCoroutinesApi::class)
|
|
214
|
-
override fun handleOnNewIntent(intent:
|
|
215
|
-
|
|
215
|
+
override fun handleOnNewIntent(intent: Intent) {
|
|
216
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent called: action=${intent.action}, data=${intent.data}, extras=${intent.extras}")
|
|
216
217
|
super.handleOnNewIntent(intent)
|
|
217
218
|
|
|
218
219
|
val action = intent.action
|
|
219
220
|
val data = intent.data
|
|
220
221
|
val extras = intent.extras
|
|
221
|
-
|
|
222
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: Parsed action: $action")
|
|
222
223
|
|
|
223
224
|
if (action === "io.getstream.video.android.action.INCOMING_CALL") {
|
|
224
|
-
|
|
225
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: Matched INCOMING_CALL action")
|
|
225
226
|
// We need to make sure the activity is visible on locked screen in such case
|
|
226
227
|
changeActivityAsVisibleOnLockScreen(this@StreamCallPlugin.activity, true)
|
|
227
228
|
activity?.runOnUiThread {
|
|
228
229
|
val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
|
|
229
|
-
|
|
230
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - Extracted cid: $cid")
|
|
230
231
|
if (cid != null) {
|
|
231
|
-
|
|
232
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - cid is not null, processing.")
|
|
232
233
|
val call = streamVideoClient?.call(id = cid.id, type = cid.type)
|
|
233
|
-
|
|
234
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - Got call object: ${call?.id}")
|
|
234
235
|
|
|
235
236
|
// Try to get caller information from the call
|
|
236
237
|
kotlinx.coroutines.GlobalScope.launch {
|
|
@@ -239,15 +240,15 @@ public class StreamCallPlugin : Plugin() {
|
|
|
239
240
|
val callerInfo = callInfo?.getOrNull()?.call?.createdBy
|
|
240
241
|
val custom = callInfo?.getOrNull()?.call?.custom
|
|
241
242
|
|
|
242
|
-
val payload =
|
|
243
|
+
val payload = JSObject().apply {
|
|
243
244
|
put("cid", cid.cid)
|
|
244
245
|
put("type", "incoming")
|
|
245
246
|
if (callerInfo != null) {
|
|
246
|
-
val caller =
|
|
247
|
+
val caller = JSObject().apply {
|
|
247
248
|
put("userId", callerInfo.id)
|
|
248
249
|
put("name", callerInfo.name ?: "")
|
|
249
250
|
put("imageURL", callerInfo.image ?: "")
|
|
250
|
-
put("role", callerInfo.role
|
|
251
|
+
put("role", callerInfo.role)
|
|
251
252
|
}
|
|
252
253
|
put("caller", caller)
|
|
253
254
|
}
|
|
@@ -263,9 +264,9 @@ public class StreamCallPlugin : Plugin() {
|
|
|
263
264
|
kotlinx.coroutines.delay(500) // 500ms delay
|
|
264
265
|
bringAppToForeground()
|
|
265
266
|
} catch (e: Exception) {
|
|
266
|
-
|
|
267
|
+
Log.e("StreamCallPlugin", "Error getting call info for incoming call", e)
|
|
267
268
|
// Fallback to basic payload without caller info
|
|
268
|
-
val payload =
|
|
269
|
+
val payload = JSObject().apply {
|
|
269
270
|
put("cid", cid.cid)
|
|
270
271
|
put("type", "incoming")
|
|
271
272
|
}
|
|
@@ -277,41 +278,41 @@ public class StreamCallPlugin : Plugin() {
|
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
} else {
|
|
280
|
-
|
|
281
|
+
Log.w("StreamCallPlugin", "handleOnNewIntent: INCOMING_CALL - cid is null. Cannot process.")
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
284
|
} else if (action === "io.getstream.video.android.action.ACCEPT_CALL") {
|
|
284
|
-
|
|
285
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: Matched ACCEPT_CALL action")
|
|
285
286
|
val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
|
|
286
|
-
|
|
287
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: ACCEPT_CALL - Extracted cid: $cid")
|
|
287
288
|
if (cid != null) {
|
|
288
|
-
|
|
289
|
+
Log.d("StreamCallPlugin", "handleOnNewIntent: ACCEPT_CALL - Accepting call with cid: $cid")
|
|
289
290
|
val call = streamVideoClient?.call(id = cid.id, type = cid.type)
|
|
290
291
|
if (call != null) {
|
|
291
292
|
// Log the full stack trace to see exactly where this is called from
|
|
292
293
|
val stackTrace = Thread.currentThread().stackTrace
|
|
293
|
-
|
|
294
|
+
Log.d("StreamCallPlugin", "internalAcceptCall STACK TRACE:")
|
|
294
295
|
stackTrace.forEachIndexed { index, element ->
|
|
295
|
-
|
|
296
|
+
Log.d("StreamCallPlugin", " [$index] ${element.className}.${element.methodName}(${element.fileName}:${element.lineNumber})")
|
|
296
297
|
}
|
|
297
298
|
kotlinx.coroutines.GlobalScope.launch {
|
|
298
299
|
internalAcceptCall(call, requestPermissionsAfter = !checkPermissions())
|
|
299
300
|
}
|
|
300
301
|
bringAppToForeground()
|
|
301
302
|
} else {
|
|
302
|
-
|
|
303
|
+
Log.e("StreamCallPlugin", "handleOnNewIntent: ACCEPT_CALL - Call object is null for cid: $cid")
|
|
303
304
|
}
|
|
304
305
|
}
|
|
305
306
|
}
|
|
306
307
|
// Log the intent information
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
308
|
+
Log.d("StreamCallPlugin", "New Intent - Action: $action")
|
|
309
|
+
Log.d("StreamCallPlugin", "New Intent - Data: $data")
|
|
310
|
+
Log.d("StreamCallPlugin", "New Intent - Extras: $extras")
|
|
310
311
|
}
|
|
311
312
|
|
|
312
313
|
@OptIn(DelicateCoroutinesApi::class)
|
|
313
314
|
private fun declineCall(call: Call) {
|
|
314
|
-
|
|
315
|
+
Log.d("StreamCallPlugin", "declineCall called for call: ${call.id}")
|
|
315
316
|
kotlinx.coroutines.GlobalScope.launch {
|
|
316
317
|
try {
|
|
317
318
|
call.reject()
|
|
@@ -322,7 +323,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
322
323
|
|
|
323
324
|
hideIncomingCall()
|
|
324
325
|
} catch (e: Exception) {
|
|
325
|
-
|
|
326
|
+
Log.e("StreamCallPlugin", "Error declining call: ${e.message}")
|
|
326
327
|
}
|
|
327
328
|
}
|
|
328
329
|
}
|
|
@@ -333,18 +334,6 @@ public class StreamCallPlugin : Plugin() {
|
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
|
|
336
|
-
private fun showBarrier() {
|
|
337
|
-
activity?.runOnUiThread {
|
|
338
|
-
barrierView?.isVisible = true
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
private fun hideBarrier() {
|
|
343
|
-
activity?.runOnUiThread {
|
|
344
|
-
barrierView?.isVisible = false
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
337
|
@OptIn(InternalStreamVideoApi::class)
|
|
349
338
|
private fun setupViews() {
|
|
350
339
|
val context = context
|
|
@@ -356,7 +345,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
356
345
|
if (rootParent != null && indexInRoot >= 0) {
|
|
357
346
|
rootParent.removeViewAt(indexInRoot)
|
|
358
347
|
touchInterceptWrapper = TouchInterceptWrapper(originalParent).apply {
|
|
359
|
-
setBackgroundColor(
|
|
348
|
+
setBackgroundColor(Color.TRANSPARENT)
|
|
360
349
|
}
|
|
361
350
|
rootParent.addView(touchInterceptWrapper, indexInRoot)
|
|
362
351
|
}
|
|
@@ -386,7 +375,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
386
375
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
387
376
|
ViewGroup.LayoutParams.MATCH_PARENT
|
|
388
377
|
)
|
|
389
|
-
setBackgroundColor(
|
|
378
|
+
setBackgroundColor("#1a242c".toColorInt())
|
|
390
379
|
}
|
|
391
380
|
parent.addView(barrierView, parent.indexOfChild(bridge?.webView) + 1) // Add above WebView
|
|
392
381
|
}
|
|
@@ -400,17 +389,6 @@ public class StreamCallPlugin : Plugin() {
|
|
|
400
389
|
VideoTheme {
|
|
401
390
|
val activeCall = call ?: streamVideoClient?.state?.activeCall?.collectAsState()?.value
|
|
402
391
|
if (activeCall != null) {
|
|
403
|
-
val participants by activeCall.state.participants.collectAsStateWithLifecycle()
|
|
404
|
-
val sortedParticipants by activeCall.state.sortedParticipants.collectAsStateWithLifecycle(emptyList())
|
|
405
|
-
val callParticipants by remember(participants) {
|
|
406
|
-
derivedStateOf {
|
|
407
|
-
if (sortedParticipants.size > 6) {
|
|
408
|
-
sortedParticipants
|
|
409
|
-
} else {
|
|
410
|
-
participants
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
392
|
|
|
415
393
|
val currentLocal by activeCall.state.me.collectAsStateWithLifecycle()
|
|
416
394
|
|
|
@@ -489,7 +467,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
489
467
|
SecureUserRepository.getInstance(context).save(credentials)
|
|
490
468
|
|
|
491
469
|
// Initialize Stream Video with new credentials
|
|
492
|
-
if (!hadSavedCredentials || (savedCredentials
|
|
470
|
+
if (!hadSavedCredentials || (savedCredentials.user.id != userId)) {
|
|
493
471
|
initializeStreamVideo()
|
|
494
472
|
}
|
|
495
473
|
|
|
@@ -532,10 +510,10 @@ public class StreamCallPlugin : Plugin() {
|
|
|
532
510
|
}
|
|
533
511
|
|
|
534
512
|
@OptIn(DelicateCoroutinesApi::class)
|
|
535
|
-
|
|
536
|
-
|
|
513
|
+
fun initializeStreamVideo(passedContext: Context? = null, passedApplication: Application? = null) {
|
|
514
|
+
Log.d("StreamCallPlugin", "initializeStreamVideo called")
|
|
537
515
|
if (state == State.INITIALIZING) {
|
|
538
|
-
|
|
516
|
+
Log.v("StreamCallPlugin", "Returning, already in the process of initializing")
|
|
539
517
|
return
|
|
540
518
|
}
|
|
541
519
|
state = State.INITIALIZING
|
|
@@ -548,7 +526,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
548
526
|
// Try to get user credentials from repository
|
|
549
527
|
val savedCredentials = SecureUserRepository.getInstance(contextToUse).loadCurrentUser()
|
|
550
528
|
if (savedCredentials == null) {
|
|
551
|
-
|
|
529
|
+
Log.v("StreamCallPlugin", "Saved credentials are null")
|
|
552
530
|
state = State.NOT_INITIALIZED
|
|
553
531
|
return
|
|
554
532
|
}
|
|
@@ -556,14 +534,14 @@ public class StreamCallPlugin : Plugin() {
|
|
|
556
534
|
try {
|
|
557
535
|
// Check if we can reuse existing StreamVideo singleton client
|
|
558
536
|
if (StreamVideo.isInstalled) {
|
|
559
|
-
|
|
537
|
+
Log.v("StreamCallPlugin", "Found existing StreamVideo singleton client")
|
|
560
538
|
if (streamVideoClient == null) {
|
|
561
|
-
|
|
539
|
+
Log.v("StreamCallPlugin", "Plugin's streamVideoClient is null, reusing singleton and registering event handlers")
|
|
562
540
|
streamVideoClient = StreamVideo.instance()
|
|
563
541
|
// Register event handlers since streamVideoClient was null
|
|
564
542
|
registerEventHandlers()
|
|
565
543
|
} else {
|
|
566
|
-
|
|
544
|
+
Log.v("StreamCallPlugin", "Plugin already has streamVideoClient, skipping event handler registration")
|
|
567
545
|
}
|
|
568
546
|
state = State.INITIALIZED
|
|
569
547
|
initializationTime = System.currentTimeMillis()
|
|
@@ -571,11 +549,11 @@ public class StreamCallPlugin : Plugin() {
|
|
|
571
549
|
}
|
|
572
550
|
|
|
573
551
|
// If we reach here, we need to create a new client
|
|
574
|
-
|
|
552
|
+
Log.v("StreamCallPlugin", "No existing StreamVideo singleton client, creating new one")
|
|
575
553
|
|
|
576
554
|
// unsafe cast, add better handling
|
|
577
555
|
val application = contextToUse.applicationContext as Application
|
|
578
|
-
|
|
556
|
+
Log.d("StreamCallPlugin", "No existing StreamVideo singleton client, creating new one")
|
|
579
557
|
val notificationHandler = CustomNotificationHandler(
|
|
580
558
|
application = application,
|
|
581
559
|
endCall = { callId ->
|
|
@@ -583,13 +561,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
583
561
|
|
|
584
562
|
kotlinx.coroutines.GlobalScope.launch {
|
|
585
563
|
try {
|
|
586
|
-
|
|
564
|
+
Log.i(
|
|
587
565
|
"StreamCallPlugin",
|
|
588
566
|
"Attempt to endCallRaw, activeCall == null: ${activeCall == null}",
|
|
589
567
|
)
|
|
590
568
|
activeCall?.let { endCallRaw(it) }
|
|
591
569
|
} catch (e: Exception) {
|
|
592
|
-
|
|
570
|
+
Log.e(
|
|
593
571
|
"StreamCallPlugin",
|
|
594
572
|
"Error ending after missed call notif action",
|
|
595
573
|
e
|
|
@@ -603,12 +581,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
603
581
|
val now = System.currentTimeMillis()
|
|
604
582
|
val isWithinOneSecond = (now - contextCreatedAt) <= 1000L
|
|
605
583
|
|
|
606
|
-
|
|
584
|
+
Log.i(
|
|
607
585
|
"StreamCallPlugin",
|
|
608
586
|
"Time between context creation and activity created (incoming call notif): ${now - contextCreatedAt}"
|
|
609
587
|
)
|
|
610
588
|
if (isWithinOneSecond && !bootedToHandleCall) {
|
|
611
|
-
|
|
589
|
+
Log.i(
|
|
612
590
|
"StreamCallPlugin",
|
|
613
591
|
"Notification incomingCall received less than 1 second after the creation of streamVideoSDK. Booted FOR SURE in order to handle the notification"
|
|
614
592
|
)
|
|
@@ -644,7 +622,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
644
622
|
|
|
645
623
|
// don't do event handler registration when activity may be null
|
|
646
624
|
if (passedContext != null) {
|
|
647
|
-
|
|
625
|
+
Log.w("StreamCallPlugin", "Ignoring event listeners for initializeStreamVideo")
|
|
648
626
|
passedApplication?.let {
|
|
649
627
|
registerActivityEventListener(it)
|
|
650
628
|
}
|
|
@@ -655,7 +633,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
655
633
|
|
|
656
634
|
registerEventHandlers()
|
|
657
635
|
|
|
658
|
-
|
|
636
|
+
Log.v("StreamCallPlugin", "Initialization finished")
|
|
659
637
|
initializationTime = System.currentTimeMillis()
|
|
660
638
|
state = State.INITIALIZED
|
|
661
639
|
} catch (e: Exception) {
|
|
@@ -667,7 +645,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
667
645
|
private fun moveAllActivitiesToBackgroundOrKill(context: Context, allowKill: Boolean = false) {
|
|
668
646
|
try {
|
|
669
647
|
if (allowKill && bootedToHandleCall && savedActivity != null) {
|
|
670
|
-
|
|
648
|
+
Log.d("StreamCallPlugin", "App was booted to handle call and allowKill is true, killing app")
|
|
671
649
|
savedActivity?.let { act ->
|
|
672
650
|
try {
|
|
673
651
|
// Get the ActivityManager
|
|
@@ -687,7 +665,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
687
665
|
android.os.Process.killProcess(android.os.Process.myPid())
|
|
688
666
|
}, 100)
|
|
689
667
|
} catch (e: Exception) {
|
|
690
|
-
|
|
668
|
+
Log.e("StreamCallPlugin", "Error during aggressive cleanup", e)
|
|
691
669
|
// Fallback to direct process kill
|
|
692
670
|
android.os.Process.killProcess(android.os.Process.myPid())
|
|
693
671
|
}
|
|
@@ -695,19 +673,20 @@ public class StreamCallPlugin : Plugin() {
|
|
|
695
673
|
return
|
|
696
674
|
}
|
|
697
675
|
|
|
698
|
-
val intent =
|
|
699
|
-
addCategory(
|
|
676
|
+
val intent = Intent(Intent.ACTION_MAIN).apply {
|
|
677
|
+
addCategory(Intent.CATEGORY_HOME)
|
|
700
678
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
|
701
679
|
}
|
|
702
680
|
context.startActivity(intent)
|
|
703
|
-
|
|
681
|
+
Log.d("StreamCallPlugin", "Moving app to background using HOME intent")
|
|
704
682
|
} catch (e: Exception) {
|
|
705
|
-
|
|
683
|
+
Log.e("StreamCallPlugin", "Failed to move app to background", e)
|
|
706
684
|
}
|
|
707
685
|
}
|
|
708
686
|
|
|
709
687
|
@OptIn(DelicateCoroutinesApi::class)
|
|
710
688
|
private fun registerEventHandlers() {
|
|
689
|
+
Log.d("StreamCallPlugin", "registerEventHandlers called")
|
|
711
690
|
eventSubscription?.dispose()
|
|
712
691
|
activeCallStateJob?.cancel()
|
|
713
692
|
cameraStatusJob?.cancel()
|
|
@@ -715,7 +694,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
715
694
|
// Subscribe to call events
|
|
716
695
|
streamVideoClient?.let { client ->
|
|
717
696
|
eventSubscription = client.subscribe { event: VideoEvent ->
|
|
718
|
-
|
|
697
|
+
Log.v("StreamCallPlugin", "Received an event ${event.getEventType()} $event")
|
|
719
698
|
when (event) {
|
|
720
699
|
is CallRingEvent -> {
|
|
721
700
|
// Extract caller information from the ringing call
|
|
@@ -736,7 +715,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
736
715
|
"userId" to callerInfo.id,
|
|
737
716
|
"name" to (callerInfo.name ?: ""),
|
|
738
717
|
"imageURL" to (callerInfo.image ?: ""),
|
|
739
|
-
"role" to (callerInfo.role
|
|
718
|
+
"role" to (callerInfo.role)
|
|
740
719
|
)
|
|
741
720
|
updateCallStatusAndNotify(event.callCid, "ringing", null, null, null, caller)
|
|
742
721
|
} else {
|
|
@@ -746,7 +725,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
746
725
|
updateCallStatusAndNotify(event.callCid, "ringing")
|
|
747
726
|
}
|
|
748
727
|
} catch (e: Exception) {
|
|
749
|
-
|
|
728
|
+
Log.e("StreamCallPlugin", "Error getting caller info for ringing event", e)
|
|
750
729
|
updateCallStatusAndNotify(event.callCid, "ringing")
|
|
751
730
|
}
|
|
752
731
|
}
|
|
@@ -754,9 +733,9 @@ public class StreamCallPlugin : Plugin() {
|
|
|
754
733
|
// Handle CallCreatedEvent differently - only log it but don't try to access members yet
|
|
755
734
|
is CallCreatedEvent -> {
|
|
756
735
|
val callCid = event.callCid
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
736
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: Received for $callCid")
|
|
737
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: All members from event: ${event.members.joinToString { it.user.id + " (role: " + it.user.role + ")" }}")
|
|
738
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: Self user ID from SDK: ${this@StreamCallPlugin.streamVideoClient?.userId}")
|
|
760
739
|
|
|
761
740
|
// Only send "created" event for outgoing calls (calls created by current user)
|
|
762
741
|
// For incoming calls, we'll only send "ringing" event in CallRingEvent handler
|
|
@@ -771,21 +750,21 @@ public class StreamCallPlugin : Plugin() {
|
|
|
771
750
|
val createdBy = callInfo?.getOrNull()?.call?.createdBy
|
|
772
751
|
val currentUserId = streamVideoClient?.userId
|
|
773
752
|
|
|
774
|
-
|
|
753
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: Call created by: ${createdBy?.id}, Current user: $currentUserId")
|
|
775
754
|
|
|
776
755
|
// Only notify for outgoing calls (where current user is the creator)
|
|
777
756
|
if (createdBy?.id == currentUserId) {
|
|
778
|
-
|
|
757
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: This is an outgoing call, sending created event")
|
|
779
758
|
|
|
780
759
|
val callParticipants = event.members.filter {
|
|
781
760
|
val selfId = this@StreamCallPlugin.streamVideoClient?.userId
|
|
782
761
|
val memberId = it.user.id
|
|
783
762
|
val isSelf = memberId == selfId
|
|
784
|
-
|
|
763
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: Filtering member $memberId. Self ID: $selfId. Is self: $isSelf")
|
|
785
764
|
!isSelf
|
|
786
765
|
}.map { it.user.id }
|
|
787
766
|
|
|
788
|
-
|
|
767
|
+
Log.d("StreamCallPlugin", "Call created for $callCid with ${callParticipants.size} remote participants: ${callParticipants.joinToString()}.")
|
|
789
768
|
|
|
790
769
|
// Start tracking this call now that we have the member list
|
|
791
770
|
startCallTimeoutMonitor(callCid, callParticipants)
|
|
@@ -796,19 +775,19 @@ public class StreamCallPlugin : Plugin() {
|
|
|
796
775
|
"userId" to member.user.id,
|
|
797
776
|
"name" to (member.user.name ?: ""),
|
|
798
777
|
"imageURL" to (member.user.image ?: ""),
|
|
799
|
-
"role" to (member.user.role
|
|
778
|
+
"role" to (member.user.role)
|
|
800
779
|
)
|
|
801
780
|
}
|
|
802
781
|
|
|
803
782
|
updateCallStatusAndNotify(callCid, "created", null, null, allMembers)
|
|
804
783
|
} else {
|
|
805
|
-
|
|
784
|
+
Log.d("StreamCallPlugin", "CallCreatedEvent: This is an incoming call (created by ${createdBy?.id}), not sending created event")
|
|
806
785
|
}
|
|
807
786
|
} else {
|
|
808
|
-
|
|
787
|
+
Log.w("StreamCallPlugin", "CallCreatedEvent: Invalid call CID format: $callCid")
|
|
809
788
|
}
|
|
810
789
|
} catch (e: Exception) {
|
|
811
|
-
|
|
790
|
+
Log.e("StreamCallPlugin", "Error processing CallCreatedEvent", e)
|
|
812
791
|
}
|
|
813
792
|
}
|
|
814
793
|
}
|
|
@@ -844,7 +823,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
844
823
|
|
|
845
824
|
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
|
846
825
|
if (keyguardManager.isKeyguardLocked) {
|
|
847
|
-
|
|
826
|
+
Log.d("StreamCallPlugin", "Stop ringing and move to background")
|
|
848
827
|
moveAllActivitiesToBackgroundOrKill(context)
|
|
849
828
|
}
|
|
850
829
|
|
|
@@ -863,7 +842,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
863
842
|
callState.participantResponses[userId] = "accepted"
|
|
864
843
|
|
|
865
844
|
// Since someone accepted, cancel the timeout timer
|
|
866
|
-
|
|
845
|
+
Log.d("StreamCallPlugin", "Call accepted by $userId, canceling timeout timer for $callCid")
|
|
867
846
|
callState.timer?.removeCallbacksAndMessages(null)
|
|
868
847
|
callState.timer = null
|
|
869
848
|
}
|
|
@@ -873,7 +852,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
873
852
|
|
|
874
853
|
is CallEndedEvent -> {
|
|
875
854
|
runOnMainThread {
|
|
876
|
-
|
|
855
|
+
Log.d("StreamCallPlugin", "Setting overlay invisible due to CallEndedEvent for call ${event.callCid}")
|
|
877
856
|
// Clean up call resources
|
|
878
857
|
val callCid = event.callCid
|
|
879
858
|
cleanupCall(callCid)
|
|
@@ -883,7 +862,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
883
862
|
|
|
884
863
|
is CallSessionEndedEvent -> {
|
|
885
864
|
runOnMainThread {
|
|
886
|
-
|
|
865
|
+
Log.d("StreamCallPlugin", "Setting overlay invisible due to CallSessionEndedEvent for call ${event.callCid}. Test session: ${event.call.session?.endedAt}")
|
|
887
866
|
// Clean up call resources
|
|
888
867
|
val callCid = event.callCid
|
|
889
868
|
cleanupCall(callCid)
|
|
@@ -907,23 +886,23 @@ public class StreamCallPlugin : Plugin() {
|
|
|
907
886
|
}
|
|
908
887
|
}
|
|
909
888
|
|
|
910
|
-
|
|
889
|
+
Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: Received for call $callId. Active call: ${activeCall?.cid}")
|
|
911
890
|
|
|
912
891
|
|
|
913
892
|
if (activeCall != null && activeCall.cid == callId) {
|
|
914
893
|
val connectionState = activeCall.state.connection.value
|
|
915
894
|
if (connectionState != RealtimeConnection.Disconnected) {
|
|
916
895
|
val total = activeCall.state.participantCounts.value?.total
|
|
917
|
-
|
|
896
|
+
Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: Participant left, remaining: $total")
|
|
918
897
|
if (total != null && total <= 1) {
|
|
919
|
-
|
|
898
|
+
Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: All remote participants have left call ${activeCall.cid}. Ending call.")
|
|
920
899
|
kotlinx.coroutines.GlobalScope.launch(Dispatchers.IO) {
|
|
921
900
|
endCallRaw(activeCall)
|
|
922
901
|
}
|
|
923
902
|
}
|
|
924
903
|
}
|
|
925
904
|
} else {
|
|
926
|
-
|
|
905
|
+
Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: Conditions not met (activeCall null, or cid mismatch, or local user not joined). ActiveCall CID: ${activeCall?.cid}")
|
|
927
906
|
}
|
|
928
907
|
}
|
|
929
908
|
|
|
@@ -940,13 +919,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
940
919
|
// used so that it follows the same patterns as iOS
|
|
941
920
|
activeCallStateJob = kotlinx.coroutines.GlobalScope.launch {
|
|
942
921
|
client.state.activeCall.collect { call ->
|
|
943
|
-
|
|
944
|
-
|
|
922
|
+
Log.d("StreamCallPlugin", "Call State Update:")
|
|
923
|
+
Log.d("StreamCallPlugin", "- Call is null: ${call == null}")
|
|
945
924
|
|
|
946
925
|
call?.state?.let { state ->
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
926
|
+
Log.d("StreamCallPlugin", "- Session ID: ${state.session.value?.id}")
|
|
927
|
+
Log.d("StreamCallPlugin", "- All participants: ${state.participants}")
|
|
928
|
+
Log.d("StreamCallPlugin", "- Remote participants: ${state.remoteParticipants}")
|
|
950
929
|
|
|
951
930
|
// Notify that a call has started or state updated (e.g., participants changed but still active)
|
|
952
931
|
// The actual check for "last participant" is now handled by CallSessionParticipantLeftEvent
|
|
@@ -960,7 +939,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
960
939
|
// Listen to camera status changes
|
|
961
940
|
cameraStatusJob = kotlinx.coroutines.GlobalScope.launch {
|
|
962
941
|
call.camera.isEnabled.collect { isEnabled ->
|
|
963
|
-
|
|
942
|
+
Log.d("StreamCallPlugin", "Camera status changed for call ${call.id}: enabled=$isEnabled")
|
|
964
943
|
updateCallStatusAndNotify(call.cid, if (isEnabled) "camera_enabled" else "camera_disabled")
|
|
965
944
|
}
|
|
966
945
|
}
|
|
@@ -968,7 +947,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
968
947
|
// Listen to microphone status changes
|
|
969
948
|
microphoneStatusJob = kotlinx.coroutines.GlobalScope.launch {
|
|
970
949
|
call.microphone.isEnabled.collect { isEnabled ->
|
|
971
|
-
|
|
950
|
+
Log.d("StreamCallPlugin", "Microphone status changed for call ${call.id}: enabled=$isEnabled")
|
|
972
951
|
updateCallStatusAndNotify(call.cid, if (isEnabled) "microphone_enabled" else "microphone_disabled")
|
|
973
952
|
}
|
|
974
953
|
}
|
|
@@ -985,18 +964,18 @@ public class StreamCallPlugin : Plugin() {
|
|
|
985
964
|
}
|
|
986
965
|
|
|
987
966
|
private fun registerActivityEventListener(application: Application) {
|
|
988
|
-
|
|
967
|
+
Log.i("StreamCallPlugin", "Registering activity event listener")
|
|
989
968
|
application.registerActivityLifecycleCallbacks(object: ActivityLifecycleCallbacks() {
|
|
990
969
|
override fun onActivityCreated(activity: Activity, bunlde: Bundle?) {
|
|
991
|
-
|
|
970
|
+
Log.d("StreamCallPlugin", "onActivityCreated called")
|
|
992
971
|
savedContext?.let {
|
|
993
972
|
if (this@StreamCallPlugin.savedActivity != null && activity is BridgeActivity) {
|
|
994
|
-
|
|
995
|
-
this@StreamCallPlugin.savedActivity = activity
|
|
973
|
+
Log.d("StreamCallPlugin", "Activity created before, but got re-created. saving and returning")
|
|
974
|
+
this@StreamCallPlugin.savedActivity = activity
|
|
996
975
|
return
|
|
997
976
|
}
|
|
998
977
|
if (initializationTime == 0L) {
|
|
999
|
-
|
|
978
|
+
Log.w("StreamCallPlugin", "initializationTime is zero. Not continuing with onActivityCreated")
|
|
1000
979
|
return
|
|
1001
980
|
}
|
|
1002
981
|
|
|
@@ -1004,8 +983,8 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1004
983
|
val isLocked = keyguardManager.isKeyguardLocked
|
|
1005
984
|
|
|
1006
985
|
if (isLocked) {
|
|
1007
|
-
this@StreamCallPlugin.bootedToHandleCall = true
|
|
1008
|
-
|
|
986
|
+
this@StreamCallPlugin.bootedToHandleCall = true
|
|
987
|
+
Log.d("StreamCallPlugin", "Detected that the app booted an activity while locked. We will kill after the call fails")
|
|
1009
988
|
}
|
|
1010
989
|
|
|
1011
990
|
if (this@StreamCallPlugin.bridge == null && activity is BridgeActivity) {
|
|
@@ -1027,7 +1006,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1027
1006
|
this@StreamCallPlugin.savedActivityPaused = false
|
|
1028
1007
|
}
|
|
1029
1008
|
for (call in this@StreamCallPlugin.savedCallsToEndOnResume) {
|
|
1030
|
-
|
|
1009
|
+
Log.d("StreamCallPlugin", "Trying to end call with ID ${call.id} on resume")
|
|
1031
1010
|
transEndCallRaw(call)
|
|
1032
1011
|
}
|
|
1033
1012
|
super.onActivityResumed(activity)
|
|
@@ -1036,8 +1015,8 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1036
1015
|
}
|
|
1037
1016
|
|
|
1038
1017
|
@PluginMethod
|
|
1039
|
-
|
|
1040
|
-
|
|
1018
|
+
fun acceptCall(call: PluginCall) {
|
|
1019
|
+
Log.d("StreamCallPlugin", "acceptCall called")
|
|
1041
1020
|
try {
|
|
1042
1021
|
val streamVideoCall = streamVideoClient?.state?.ringingCall?.value
|
|
1043
1022
|
if (streamVideoCall == null) {
|
|
@@ -1045,7 +1024,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1045
1024
|
return
|
|
1046
1025
|
}
|
|
1047
1026
|
|
|
1048
|
-
|
|
1027
|
+
Log.d("StreamCallPlugin", "acceptCall: Accepting call immediately, will handle permissions after")
|
|
1049
1028
|
|
|
1050
1029
|
// Accept call immediately regardless of permissions - time is critical!
|
|
1051
1030
|
kotlinx.coroutines.GlobalScope.launch {
|
|
@@ -1055,19 +1034,19 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1055
1034
|
put("success", true)
|
|
1056
1035
|
})
|
|
1057
1036
|
} catch (e: Exception) {
|
|
1058
|
-
|
|
1037
|
+
Log.e("StreamCallPlugin", "Error accepting call", e)
|
|
1059
1038
|
call.reject("Failed to accept call: ${e.message}")
|
|
1060
1039
|
}
|
|
1061
1040
|
}
|
|
1062
1041
|
} catch (t: Throwable) {
|
|
1063
|
-
|
|
1042
|
+
Log.d("StreamCallPlugin", "JS -> acceptCall fail", t)
|
|
1064
1043
|
call.reject("Cannot acceptCall")
|
|
1065
1044
|
}
|
|
1066
1045
|
}
|
|
1067
1046
|
|
|
1068
1047
|
@PluginMethod
|
|
1069
|
-
|
|
1070
|
-
|
|
1048
|
+
fun rejectCall(call: PluginCall) {
|
|
1049
|
+
Log.d("StreamCallPlugin", "rejectCall called")
|
|
1071
1050
|
try {
|
|
1072
1051
|
val streamVideoCall = streamVideoClient?.state?.ringingCall?.value
|
|
1073
1052
|
if (streamVideoCall == null) {
|
|
@@ -1078,103 +1057,103 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1078
1057
|
declineCall(streamVideoCall)
|
|
1079
1058
|
}
|
|
1080
1059
|
} catch (t: Throwable) {
|
|
1081
|
-
|
|
1060
|
+
Log.d("StreamCallPlugin", "JS -> rejectCall fail", t)
|
|
1082
1061
|
call.reject("Cannot rejectCall")
|
|
1083
1062
|
}
|
|
1084
1063
|
}
|
|
1085
1064
|
|
|
1086
1065
|
@OptIn(DelicateCoroutinesApi::class, InternalStreamVideoApi::class)
|
|
1087
1066
|
internal fun internalAcceptCall(call: Call, requestPermissionsAfter: Boolean = false) {
|
|
1088
|
-
|
|
1067
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Entered for call: ${call.id}, requestPermissionsAfter: $requestPermissionsAfter")
|
|
1089
1068
|
|
|
1090
1069
|
kotlinx.coroutines.GlobalScope.launch {
|
|
1091
1070
|
try {
|
|
1092
|
-
|
|
1071
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Coroutine started for call ${call.id}")
|
|
1093
1072
|
|
|
1094
1073
|
// Hide incoming call view first
|
|
1095
1074
|
runOnMainThread {
|
|
1096
|
-
|
|
1075
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Hiding incoming call view for call ${call.id}")
|
|
1097
1076
|
// No dedicated incoming-call native view anymore; UI handled by web layer
|
|
1098
1077
|
}
|
|
1099
|
-
|
|
1078
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Incoming call view hidden for call ${call.id}")
|
|
1100
1079
|
|
|
1101
1080
|
// Accept and join call immediately - don't wait for permissions!
|
|
1102
|
-
|
|
1081
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Accepting call immediately for ${call.id}")
|
|
1103
1082
|
call.accept()
|
|
1104
|
-
|
|
1083
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: call.accept() completed for call ${call.id}")
|
|
1105
1084
|
call.join()
|
|
1106
|
-
|
|
1085
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: call.join() completed for call ${call.id}")
|
|
1107
1086
|
streamVideoClient?.state?.setActiveCall(call)
|
|
1108
|
-
|
|
1087
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: setActiveCall completed for call ${call.id}")
|
|
1109
1088
|
|
|
1110
1089
|
// Notify that call has started using helper
|
|
1111
1090
|
updateCallStatusAndNotify(call.id, "joined")
|
|
1112
|
-
|
|
1091
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: updateCallStatusAndNotify(joined) called for ${call.id}")
|
|
1113
1092
|
|
|
1114
1093
|
// Show overlay view with the active call and make webview transparent
|
|
1115
1094
|
runOnMainThread {
|
|
1116
|
-
|
|
1095
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Updating UI for active call ${call.id} - setting overlay visible.")
|
|
1117
1096
|
bridge?.webView?.setBackgroundColor(Color.TRANSPARENT) // Make webview transparent
|
|
1118
|
-
|
|
1097
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: WebView background set to transparent for call ${call.id}")
|
|
1119
1098
|
bridge?.webView?.bringToFront() // Ensure WebView is on top and transparent
|
|
1120
|
-
|
|
1099
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: WebView brought to front for call ${call.id}")
|
|
1121
1100
|
|
|
1122
1101
|
// Enable camera/microphone based on permissions
|
|
1123
1102
|
val hasPermissions = checkPermissions()
|
|
1124
|
-
|
|
1103
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Has permissions: $hasPermissions for call ${call.id}")
|
|
1125
1104
|
|
|
1126
|
-
call.microphone
|
|
1127
|
-
call.camera
|
|
1128
|
-
|
|
1105
|
+
call.microphone.setEnabled(hasPermissions)
|
|
1106
|
+
call.camera.setEnabled(hasPermissions)
|
|
1107
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Microphone and camera set to $hasPermissions for call ${call.id}")
|
|
1129
1108
|
|
|
1130
|
-
|
|
1109
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Setting CallContent with active call ${call.id}")
|
|
1131
1110
|
setOverlayContent(call)
|
|
1132
|
-
|
|
1111
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Content set for overlayView for call ${call.id}")
|
|
1133
1112
|
overlayView?.isVisible = true
|
|
1134
|
-
|
|
1113
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: OverlayView set to visible for call ${call.id}, isVisible: ${overlayView?.isVisible}")
|
|
1135
1114
|
|
|
1136
1115
|
// Ensure overlay is behind WebView by adjusting its position in the parent
|
|
1137
1116
|
val parent = overlayView?.parent as? ViewGroup
|
|
1138
1117
|
parent?.removeView(overlayView)
|
|
1139
1118
|
parent?.addView(overlayView, 0) // Add at index 0 to ensure it's behind other views
|
|
1140
|
-
|
|
1119
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: OverlayView re-added to parent at index 0 for call ${call.id}")
|
|
1141
1120
|
|
|
1142
1121
|
// Add a small delay to ensure UI refresh
|
|
1143
1122
|
mainHandler.postDelayed({
|
|
1144
|
-
|
|
1123
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Delayed UI check, overlay visible: ${overlayView?.isVisible} for call ${call.id}")
|
|
1145
1124
|
if (overlayView?.isVisible == true) {
|
|
1146
1125
|
overlayView?.invalidate()
|
|
1147
1126
|
overlayView?.requestLayout()
|
|
1148
|
-
|
|
1127
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: UI invalidated and layout requested for call ${call.id}")
|
|
1149
1128
|
// Force refresh with active call from client
|
|
1150
1129
|
val activeCall = streamVideoClient?.state?.activeCall?.value
|
|
1151
1130
|
if (activeCall != null) {
|
|
1152
|
-
|
|
1131
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Force refreshing CallContent with active call ${activeCall.id}")
|
|
1153
1132
|
setOverlayContent(activeCall)
|
|
1154
|
-
|
|
1133
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Content force refreshed for call ${activeCall.id}")
|
|
1155
1134
|
} else {
|
|
1156
|
-
|
|
1135
|
+
Log.w("StreamCallPlugin", "internalAcceptCall: Active call is null during force refresh for call ${call.id}")
|
|
1157
1136
|
}
|
|
1158
1137
|
} else {
|
|
1159
|
-
|
|
1138
|
+
Log.w("StreamCallPlugin", "internalAcceptCall: overlayView not visible after delay for call ${call.id}")
|
|
1160
1139
|
}
|
|
1161
1140
|
}, 1000) // Increased delay to ensure all events are processed
|
|
1162
1141
|
}
|
|
1163
1142
|
|
|
1164
1143
|
// Request permissions after joining if needed
|
|
1165
1144
|
if (requestPermissionsAfter) {
|
|
1166
|
-
|
|
1145
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Requesting permissions after call acceptance for ${call.id}")
|
|
1167
1146
|
runOnMainThread {
|
|
1168
1147
|
// Store reference to the active call for enabling camera/mic later
|
|
1169
1148
|
pendingAcceptCall = call
|
|
1170
|
-
|
|
1149
|
+
Log.d("StreamCallPlugin", "internalAcceptCall: Set pendingAcceptCall to ${call.id}, resetting attempt count")
|
|
1171
1150
|
permissionAttemptCount = 0
|
|
1172
1151
|
requestPermissions()
|
|
1173
1152
|
}
|
|
1174
1153
|
}
|
|
1175
1154
|
|
|
1176
1155
|
} catch (e: Exception) {
|
|
1177
|
-
|
|
1156
|
+
Log.e("StreamCallPlugin", "internalAcceptCall: Error accepting call ${call.id}: ${e.message}", e)
|
|
1178
1157
|
runOnMainThread {
|
|
1179
1158
|
android.widget.Toast.makeText(
|
|
1180
1159
|
context,
|
|
@@ -1188,54 +1167,54 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1188
1167
|
|
|
1189
1168
|
// Function to check required permissions
|
|
1190
1169
|
private fun checkPermissions(): Boolean {
|
|
1191
|
-
|
|
1170
|
+
Log.d("StreamCallPlugin", "checkPermissions: Entered")
|
|
1192
1171
|
val audioPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
|
|
1193
|
-
|
|
1172
|
+
Log.d("StreamCallPlugin", "checkPermissions: RECORD_AUDIO permission status: $audioPermission (Granted=${PackageManager.PERMISSION_GRANTED})")
|
|
1194
1173
|
val cameraPermission = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
|
1195
|
-
|
|
1174
|
+
Log.d("StreamCallPlugin", "checkPermissions: CAMERA permission status: $cameraPermission (Granted=${PackageManager.PERMISSION_GRANTED})")
|
|
1196
1175
|
val allGranted = audioPermission == PackageManager.PERMISSION_GRANTED && cameraPermission == PackageManager.PERMISSION_GRANTED
|
|
1197
|
-
|
|
1176
|
+
Log.d("StreamCallPlugin", "checkPermissions: All permissions granted: $allGranted")
|
|
1198
1177
|
return allGranted
|
|
1199
1178
|
}
|
|
1200
1179
|
|
|
1201
1180
|
// Override to handle permission results
|
|
1202
1181
|
override fun handleRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
1203
1182
|
super.handleRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
1204
|
-
|
|
1205
|
-
|
|
1183
|
+
Log.d("StreamCallPlugin", "handleRequestPermissionsResult: Entered. RequestCode: $requestCode, Attempt: $permissionAttemptCount")
|
|
1184
|
+
Log.d("StreamCallPlugin", "handleRequestPermissionsResult: Expected requestCode: 9001")
|
|
1206
1185
|
|
|
1207
1186
|
if (requestCode == 9001) {
|
|
1208
1187
|
val responseTime = System.currentTimeMillis() - permissionRequestStartTime
|
|
1209
|
-
|
|
1188
|
+
Log.d("StreamCallPlugin", "handleRequestPermissionsResult: Response time: ${responseTime}ms")
|
|
1210
1189
|
|
|
1211
1190
|
logPermissionResults(permissions, grantResults)
|
|
1212
1191
|
|
|
1213
1192
|
if (grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
|
|
1214
|
-
|
|
1193
|
+
Log.i("StreamCallPlugin", "handleRequestPermissionsResult: All permissions GRANTED.")
|
|
1215
1194
|
// Reset attempt count on success
|
|
1216
1195
|
permissionAttemptCount = 0
|
|
1217
1196
|
handlePermissionGranted()
|
|
1218
1197
|
} else {
|
|
1219
|
-
|
|
1198
|
+
Log.e("StreamCallPlugin", "handleRequestPermissionsResult: Permissions DENIED. Attempt: $permissionAttemptCount")
|
|
1220
1199
|
handlePermissionDenied(responseTime)
|
|
1221
1200
|
}
|
|
1222
1201
|
} else {
|
|
1223
|
-
|
|
1202
|
+
Log.w("StreamCallPlugin", "handleRequestPermissionsResult: Received unknown requestCode: $requestCode")
|
|
1224
1203
|
}
|
|
1225
1204
|
}
|
|
1226
1205
|
|
|
1227
1206
|
private fun logPermissionResults(permissions: Array<out String>, grantResults: IntArray) {
|
|
1228
|
-
|
|
1207
|
+
Log.d("StreamCallPlugin", "logPermissionResults: Logging permission results:")
|
|
1229
1208
|
for (i in permissions.indices) {
|
|
1230
1209
|
val permission = permissions[i]
|
|
1231
1210
|
val grantResult = if (grantResults.size > i) grantResults[i] else -999 // -999 for safety if arrays mismatch
|
|
1232
1211
|
val resultString = if (grantResult == PackageManager.PERMISSION_GRANTED) "GRANTED" else "DENIED ($grantResult)"
|
|
1233
|
-
|
|
1212
|
+
Log.d("StreamCallPlugin", " Permission: $permission, Result: $resultString")
|
|
1234
1213
|
}
|
|
1235
1214
|
}
|
|
1236
1215
|
|
|
1237
1216
|
private fun handlePermissionGranted() {
|
|
1238
|
-
|
|
1217
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Processing granted permissions")
|
|
1239
1218
|
|
|
1240
1219
|
// Reset attempt count since permissions are now granted
|
|
1241
1220
|
permissionAttemptCount = 0
|
|
@@ -1244,12 +1223,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1244
1223
|
val hasOutgoingCall = pendingCall != null && pendingCallUserIds != null
|
|
1245
1224
|
val hasActiveCallNeedingPermissions = pendingAcceptCall != null
|
|
1246
1225
|
|
|
1247
|
-
|
|
1226
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: hasOutgoingCall=$hasOutgoingCall, hasActiveCallNeedingPermissions=$hasActiveCallNeedingPermissions")
|
|
1248
1227
|
|
|
1249
1228
|
when {
|
|
1250
1229
|
hasOutgoingCall -> {
|
|
1251
1230
|
// Outgoing call creation was waiting for permissions
|
|
1252
|
-
|
|
1231
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Executing pending outgoing call with ${pendingCallUserIds?.size} users")
|
|
1253
1232
|
executePendingCall()
|
|
1254
1233
|
}
|
|
1255
1234
|
|
|
@@ -1258,17 +1237,17 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1258
1237
|
val callToHandle = pendingAcceptCall!!
|
|
1259
1238
|
val activeCall = streamVideoClient?.state?.activeCall?.value
|
|
1260
1239
|
|
|
1261
|
-
|
|
1262
|
-
|
|
1240
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Processing call ${callToHandle.id}")
|
|
1241
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Active call in state: ${activeCall?.id}")
|
|
1263
1242
|
|
|
1264
1243
|
if (activeCall != null && activeCall.id == callToHandle.id) {
|
|
1265
1244
|
// Call is already active - enable camera/microphone
|
|
1266
|
-
|
|
1245
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Enabling camera/microphone for active call ${callToHandle.id}")
|
|
1267
1246
|
runOnMainThread {
|
|
1268
1247
|
try {
|
|
1269
|
-
callToHandle.microphone
|
|
1270
|
-
callToHandle.camera
|
|
1271
|
-
|
|
1248
|
+
callToHandle.microphone.setEnabled(true)
|
|
1249
|
+
callToHandle.camera.setEnabled(true)
|
|
1250
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Camera and microphone enabled for call ${callToHandle.id}")
|
|
1272
1251
|
|
|
1273
1252
|
// Show success message
|
|
1274
1253
|
android.widget.Toast.makeText(
|
|
@@ -1277,13 +1256,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1277
1256
|
android.widget.Toast.LENGTH_SHORT
|
|
1278
1257
|
).show()
|
|
1279
1258
|
} catch (e: Exception) {
|
|
1280
|
-
|
|
1259
|
+
Log.e("StreamCallPlugin", "Error enabling camera/microphone", e)
|
|
1281
1260
|
}
|
|
1282
1261
|
clearPendingCall()
|
|
1283
1262
|
}
|
|
1284
1263
|
} else if (pendingCall != null) {
|
|
1285
1264
|
// Call not active yet - accept it (old flow, shouldn't happen with new flow)
|
|
1286
|
-
|
|
1265
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Accepting pending incoming call ${callToHandle.id}")
|
|
1287
1266
|
kotlinx.coroutines.GlobalScope.launch {
|
|
1288
1267
|
try {
|
|
1289
1268
|
internalAcceptCall(callToHandle)
|
|
@@ -1291,7 +1270,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1291
1270
|
put("success", true)
|
|
1292
1271
|
})
|
|
1293
1272
|
} catch (e: Exception) {
|
|
1294
|
-
|
|
1273
|
+
Log.e("StreamCallPlugin", "Error accepting call after permission grant", e)
|
|
1295
1274
|
pendingCall?.reject("Failed to accept call: ${e.message}")
|
|
1296
1275
|
} finally {
|
|
1297
1276
|
clearPendingCall()
|
|
@@ -1299,12 +1278,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1299
1278
|
}
|
|
1300
1279
|
} else {
|
|
1301
1280
|
// Just enable camera/mic for the stored call even if not currently active
|
|
1302
|
-
|
|
1281
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Enabling camera/microphone for stored call ${callToHandle.id}")
|
|
1303
1282
|
runOnMainThread {
|
|
1304
1283
|
try {
|
|
1305
|
-
callToHandle.microphone
|
|
1306
|
-
callToHandle.camera
|
|
1307
|
-
|
|
1284
|
+
callToHandle.microphone.setEnabled(true)
|
|
1285
|
+
callToHandle.camera.setEnabled(true)
|
|
1286
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Camera and microphone enabled for stored call ${callToHandle.id}")
|
|
1308
1287
|
|
|
1309
1288
|
android.widget.Toast.makeText(
|
|
1310
1289
|
context,
|
|
@@ -1312,7 +1291,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1312
1291
|
android.widget.Toast.LENGTH_SHORT
|
|
1313
1292
|
).show()
|
|
1314
1293
|
} catch (e: Exception) {
|
|
1315
|
-
|
|
1294
|
+
Log.e("StreamCallPlugin", "Error enabling camera/microphone for stored call", e)
|
|
1316
1295
|
}
|
|
1317
1296
|
clearPendingCall()
|
|
1318
1297
|
}
|
|
@@ -1321,14 +1300,14 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1321
1300
|
|
|
1322
1301
|
pendingCall != null -> {
|
|
1323
1302
|
// We have a pending call but unclear what type - fallback handling
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1303
|
+
Log.w("StreamCallPlugin", "handlePermissionGranted: Have pendingCall but unclear operation type")
|
|
1304
|
+
Log.w("StreamCallPlugin", " - pendingCallUserIds: ${pendingCallUserIds != null}")
|
|
1305
|
+
Log.w("StreamCallPlugin", " - pendingAcceptCall: ${pendingAcceptCall != null}")
|
|
1327
1306
|
|
|
1328
1307
|
// Try fallback to current ringing call for acceptance
|
|
1329
1308
|
val ringingCall = streamVideoClient?.state?.ringingCall?.value
|
|
1330
1309
|
if (ringingCall != null) {
|
|
1331
|
-
|
|
1310
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: Fallback - accepting current ringing call ${ringingCall.id}")
|
|
1332
1311
|
kotlinx.coroutines.GlobalScope.launch {
|
|
1333
1312
|
try {
|
|
1334
1313
|
internalAcceptCall(ringingCall)
|
|
@@ -1336,43 +1315,43 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1336
1315
|
put("success", true)
|
|
1337
1316
|
})
|
|
1338
1317
|
} catch (e: Exception) {
|
|
1339
|
-
|
|
1318
|
+
Log.e("StreamCallPlugin", "Error accepting fallback call after permission grant", e)
|
|
1340
1319
|
pendingCall?.reject("Failed to accept call: ${e.message}")
|
|
1341
1320
|
} finally {
|
|
1342
1321
|
clearPendingCall()
|
|
1343
1322
|
}
|
|
1344
1323
|
}
|
|
1345
1324
|
} else {
|
|
1346
|
-
|
|
1325
|
+
Log.w("StreamCallPlugin", "handlePermissionGranted: No ringing call found for fallback")
|
|
1347
1326
|
pendingCall?.reject("Unable to determine pending operation")
|
|
1348
1327
|
clearPendingCall()
|
|
1349
1328
|
}
|
|
1350
1329
|
}
|
|
1351
1330
|
|
|
1352
1331
|
else -> {
|
|
1353
|
-
|
|
1332
|
+
Log.d("StreamCallPlugin", "handlePermissionGranted: No pending operations to handle")
|
|
1354
1333
|
}
|
|
1355
1334
|
}
|
|
1356
1335
|
}
|
|
1357
1336
|
|
|
1358
1337
|
private fun handlePermissionDenied(responseTime: Long) {
|
|
1359
|
-
|
|
1338
|
+
Log.d("StreamCallPlugin", "handlePermissionDenied: Response time: ${responseTime}ms, Attempt: $permissionAttemptCount")
|
|
1360
1339
|
|
|
1361
1340
|
// Check if the response was instant (< 500ms) indicating "don't ask again"
|
|
1362
1341
|
val instantDenial = responseTime < 500
|
|
1363
|
-
|
|
1342
|
+
Log.d("StreamCallPlugin", "handlePermissionDenied: Instant denial detected: $instantDenial")
|
|
1364
1343
|
|
|
1365
1344
|
if (instantDenial) {
|
|
1366
1345
|
// If it's an instant denial (don't ask again), go straight to settings dialog
|
|
1367
|
-
|
|
1346
|
+
Log.d("StreamCallPlugin", "handlePermissionDenied: Instant denial, showing settings dialog")
|
|
1368
1347
|
showPermissionSettingsDialog()
|
|
1369
1348
|
} else if (permissionAttemptCount < 2) {
|
|
1370
1349
|
// Try asking again immediately if this is the first denial
|
|
1371
|
-
|
|
1350
|
+
Log.d("StreamCallPlugin", "handlePermissionDenied: First denial (attempt $permissionAttemptCount), asking again immediately")
|
|
1372
1351
|
requestPermissions() // This will increment the attempt count
|
|
1373
1352
|
} else {
|
|
1374
1353
|
// Second denial - show settings dialog (final ask)
|
|
1375
|
-
|
|
1354
|
+
Log.d("StreamCallPlugin", "handlePermissionDenied: Second denial (attempt $permissionAttemptCount), showing settings dialog (final ask)")
|
|
1376
1355
|
showPermissionSettingsDialog()
|
|
1377
1356
|
}
|
|
1378
1357
|
}
|
|
@@ -1386,7 +1365,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1386
1365
|
val custom = pendingCustomObject
|
|
1387
1366
|
|
|
1388
1367
|
if (call != null && userIds != null && callType != null && shouldRing != null) {
|
|
1389
|
-
|
|
1368
|
+
Log.d("StreamCallPlugin", "executePendingCall: Executing call with ${userIds.size} users")
|
|
1390
1369
|
|
|
1391
1370
|
// Clear pending call data
|
|
1392
1371
|
clearPendingCall()
|
|
@@ -1394,7 +1373,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1394
1373
|
// Execute the call creation logic
|
|
1395
1374
|
createAndStartCall(call, userIds, callType, shouldRing, team, custom)
|
|
1396
1375
|
} else {
|
|
1397
|
-
|
|
1376
|
+
Log.w("StreamCallPlugin", "executePendingCall: Missing pending call data")
|
|
1398
1377
|
call?.reject("Internal error: missing call parameters")
|
|
1399
1378
|
clearPendingCall()
|
|
1400
1379
|
}
|
|
@@ -1433,7 +1412,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1433
1412
|
// instead, which contains the actual participant list
|
|
1434
1413
|
|
|
1435
1414
|
|
|
1436
|
-
|
|
1415
|
+
Log.d("StreamCallPlugin", "Creating call with members...")
|
|
1437
1416
|
// Create the call with all members
|
|
1438
1417
|
val createResult = streamCall?.create(
|
|
1439
1418
|
memberIds = userIds + selfUserId,
|
|
@@ -1446,7 +1425,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1446
1425
|
throw (createResult.errorOrNull() ?: RuntimeException("Unknown error creating call")) as Throwable
|
|
1447
1426
|
}
|
|
1448
1427
|
|
|
1449
|
-
|
|
1428
|
+
Log.d("StreamCallPlugin", "Setting overlay visible for outgoing call $callId")
|
|
1450
1429
|
// Show overlay view
|
|
1451
1430
|
activity?.runOnUiThread {
|
|
1452
1431
|
streamCall?.microphone?.setEnabled(true)
|
|
@@ -1467,7 +1446,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1467
1446
|
put("success", true)
|
|
1468
1447
|
})
|
|
1469
1448
|
} catch (e: Exception) {
|
|
1470
|
-
|
|
1449
|
+
Log.e("StreamCallPlugin", "Error making call: ${e.message}")
|
|
1471
1450
|
call.reject("Failed to make call: ${e.message}")
|
|
1472
1451
|
}
|
|
1473
1452
|
}
|
|
@@ -1476,11 +1455,11 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1476
1455
|
// Function to request required permissions
|
|
1477
1456
|
private fun requestPermissions() {
|
|
1478
1457
|
permissionAttemptCount++
|
|
1479
|
-
|
|
1458
|
+
Log.d("StreamCallPlugin", "requestPermissions: Attempt #$permissionAttemptCount - Requesting RECORD_AUDIO and CAMERA permissions.")
|
|
1480
1459
|
|
|
1481
1460
|
// Record timing for instant denial detection
|
|
1482
1461
|
permissionRequestStartTime = System.currentTimeMillis()
|
|
1483
|
-
|
|
1462
|
+
Log.d("StreamCallPlugin", "requestPermissions: Starting permission request at $permissionRequestStartTime")
|
|
1484
1463
|
|
|
1485
1464
|
ActivityCompat.requestPermissions(
|
|
1486
1465
|
activity,
|
|
@@ -1488,7 +1467,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1488
1467
|
9001 // Use high request code to avoid Capacitor conflicts
|
|
1489
1468
|
)
|
|
1490
1469
|
|
|
1491
|
-
|
|
1470
|
+
Log.d("StreamCallPlugin", "requestPermissions: Permission request initiated with code 9001")
|
|
1492
1471
|
}
|
|
1493
1472
|
|
|
1494
1473
|
private fun showPermissionSettingsDialog() {
|
|
@@ -1502,19 +1481,19 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1502
1481
|
if (hasActiveCall) {
|
|
1503
1482
|
builder.setMessage("Your call is active but camera and microphone are disabled.\n\nWould you like to open Settings to enable video and audio?")
|
|
1504
1483
|
builder.setNegativeButton("Continue without") { _, _ ->
|
|
1505
|
-
|
|
1484
|
+
Log.d("StreamCallPlugin", "User chose to continue call without permissions")
|
|
1506
1485
|
showPermissionRequiredMessage()
|
|
1507
1486
|
}
|
|
1508
1487
|
} else {
|
|
1509
1488
|
builder.setMessage("To make video calls, this app needs Camera and Microphone permissions.\n\nWould you like to open Settings to enable them?")
|
|
1510
1489
|
builder.setNegativeButton("Cancel") { _, _ ->
|
|
1511
|
-
|
|
1490
|
+
Log.d("StreamCallPlugin", "User declined to grant permissions - final rejection")
|
|
1512
1491
|
showPermissionRequiredMessage()
|
|
1513
1492
|
}
|
|
1514
1493
|
}
|
|
1515
1494
|
|
|
1516
1495
|
builder.setPositiveButton("Open Settings") { _, _ ->
|
|
1517
|
-
|
|
1496
|
+
Log.d("StreamCallPlugin", "User chose to open app settings")
|
|
1518
1497
|
openAppSettings()
|
|
1519
1498
|
// Don't reject the call yet - let them go to settings and come back
|
|
1520
1499
|
}
|
|
@@ -1548,7 +1527,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1548
1527
|
}
|
|
1549
1528
|
|
|
1550
1529
|
private fun handleFinalPermissionDenial() {
|
|
1551
|
-
|
|
1530
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: Processing final permission denial")
|
|
1552
1531
|
|
|
1553
1532
|
val hasOutgoingCall = pendingCall != null && pendingCallUserIds != null
|
|
1554
1533
|
val hasIncomingCall = pendingCall != null && pendingAcceptCall != null
|
|
@@ -1557,22 +1536,22 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1557
1536
|
when {
|
|
1558
1537
|
hasOutgoingCall -> {
|
|
1559
1538
|
// Outgoing call that couldn't be created due to permissions
|
|
1560
|
-
|
|
1539
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: Rejecting outgoing call creation")
|
|
1561
1540
|
pendingCall?.reject("Permissions required for call. Please grant them.")
|
|
1562
1541
|
clearPendingCall()
|
|
1563
1542
|
}
|
|
1564
1543
|
|
|
1565
1544
|
hasIncomingCall && activeCall != null && activeCall.id == pendingAcceptCall?.id -> {
|
|
1566
1545
|
// Incoming call that's already active - DON'T end the call, just keep it without camera/mic
|
|
1567
|
-
|
|
1546
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: Incoming call already active, keeping call without camera/mic")
|
|
1568
1547
|
|
|
1569
1548
|
// Ensure camera and microphone are disabled since no permissions
|
|
1570
1549
|
try {
|
|
1571
|
-
activeCall.microphone
|
|
1572
|
-
activeCall.camera
|
|
1573
|
-
|
|
1550
|
+
activeCall.microphone.setEnabled(false)
|
|
1551
|
+
activeCall.camera.setEnabled(false)
|
|
1552
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: Disabled camera/microphone for call ${activeCall.id}")
|
|
1574
1553
|
} catch (e: Exception) {
|
|
1575
|
-
|
|
1554
|
+
Log.w("StreamCallPlugin", "handleFinalPermissionDenial: Error disabling camera/mic", e)
|
|
1576
1555
|
}
|
|
1577
1556
|
|
|
1578
1557
|
android.widget.Toast.makeText(
|
|
@@ -1591,13 +1570,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1591
1570
|
|
|
1592
1571
|
hasIncomingCall -> {
|
|
1593
1572
|
// Incoming call that wasn't accepted yet (old flow)
|
|
1594
|
-
|
|
1573
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: Rejecting incoming call acceptance")
|
|
1595
1574
|
pendingCall?.reject("Permissions required for call. Please grant them.")
|
|
1596
1575
|
clearPendingCall()
|
|
1597
1576
|
}
|
|
1598
1577
|
|
|
1599
1578
|
else -> {
|
|
1600
|
-
|
|
1579
|
+
Log.d("StreamCallPlugin", "handleFinalPermissionDenial: No pending operations to handle")
|
|
1601
1580
|
clearPendingCall()
|
|
1602
1581
|
}
|
|
1603
1582
|
}
|
|
@@ -1610,10 +1589,10 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1610
1589
|
try {
|
|
1611
1590
|
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
|
1612
1591
|
("package:" + activity.packageName).toUri())
|
|
1613
|
-
intent.addCategory(Intent.CATEGORY_DEFAULT)
|
|
1614
|
-
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
1592
|
+
intent.addCategory(Intent.CATEGORY_DEFAULT)
|
|
1593
|
+
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
1615
1594
|
context.startActivity(intent)
|
|
1616
|
-
|
|
1595
|
+
Log.d("StreamCallPlugin", "Opened app details settings (Android 11+)")
|
|
1617
1596
|
|
|
1618
1597
|
// Show toast with specific instructions
|
|
1619
1598
|
runOnMainThread {
|
|
@@ -1625,17 +1604,17 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1625
1604
|
}
|
|
1626
1605
|
return
|
|
1627
1606
|
} catch (e: Exception) {
|
|
1628
|
-
|
|
1607
|
+
Log.w("StreamCallPlugin", "Failed to open app details, falling back", e)
|
|
1629
1608
|
}
|
|
1630
1609
|
}
|
|
1631
1610
|
|
|
1632
1611
|
// Fallback for older Android versions or if the above fails
|
|
1633
|
-
val intent =
|
|
1612
|
+
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
|
|
1634
1613
|
data = Uri.fromParts("package", context.packageName, null)
|
|
1635
1614
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
1636
1615
|
}
|
|
1637
1616
|
context.startActivity(intent)
|
|
1638
|
-
|
|
1617
|
+
Log.d("StreamCallPlugin", "Opened app settings via fallback")
|
|
1639
1618
|
|
|
1640
1619
|
// Show more specific instructions for older versions
|
|
1641
1620
|
runOnMainThread {
|
|
@@ -1647,15 +1626,15 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1647
1626
|
}
|
|
1648
1627
|
|
|
1649
1628
|
} catch (e: Exception) {
|
|
1650
|
-
|
|
1629
|
+
Log.e("StreamCallPlugin", "Error opening app settings", e)
|
|
1651
1630
|
|
|
1652
1631
|
// Final fallback - open general settings
|
|
1653
1632
|
try {
|
|
1654
|
-
val intent =
|
|
1633
|
+
val intent = Intent(Settings.ACTION_SETTINGS).apply {
|
|
1655
1634
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
1656
1635
|
}
|
|
1657
1636
|
context.startActivity(intent)
|
|
1658
|
-
|
|
1637
|
+
Log.d("StreamCallPlugin", "Opened general settings as final fallback")
|
|
1659
1638
|
|
|
1660
1639
|
runOnMainThread {
|
|
1661
1640
|
android.widget.Toast.makeText(
|
|
@@ -1665,7 +1644,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1665
1644
|
).show()
|
|
1666
1645
|
}
|
|
1667
1646
|
} catch (finalException: Exception) {
|
|
1668
|
-
|
|
1647
|
+
Log.e("StreamCallPlugin", "All settings intents failed", finalException)
|
|
1669
1648
|
runOnMainThread {
|
|
1670
1649
|
android.widget.Toast.makeText(
|
|
1671
1650
|
context,
|
|
@@ -1701,11 +1680,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1701
1680
|
put("success", true)
|
|
1702
1681
|
})
|
|
1703
1682
|
} catch (e: Exception) {
|
|
1704
|
-
|
|
1683
|
+
Log.e("StreamCallPlugin", "Error setting microphone: ${e.message}")
|
|
1705
1684
|
call.reject("Failed to set microphone: ${e.message}")
|
|
1706
1685
|
}
|
|
1707
1686
|
}
|
|
1708
1687
|
} catch (e: Exception) {
|
|
1688
|
+
Log.e("StreamCallPlugin", "Error setting microphone: ${e.message}")
|
|
1709
1689
|
call.reject("StreamVideo not initialized")
|
|
1710
1690
|
}
|
|
1711
1691
|
}
|
|
@@ -1731,11 +1711,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1731
1711
|
put("enabled", enabled)
|
|
1732
1712
|
})
|
|
1733
1713
|
} catch (e: Exception) {
|
|
1734
|
-
|
|
1714
|
+
Log.e("StreamCallPlugin", "Error checking the camera status: ${e.message}")
|
|
1735
1715
|
call.reject("Failed to check if camera is enabled: ${e.message}")
|
|
1736
1716
|
}
|
|
1737
1717
|
}
|
|
1738
1718
|
} catch (e: Exception) {
|
|
1719
|
+
Log.e("StreamCallPlugin", "Error checking camera status: ${e.message}")
|
|
1739
1720
|
call.reject("StreamVideo not initialized")
|
|
1740
1721
|
}
|
|
1741
1722
|
}
|
|
@@ -1762,11 +1743,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1762
1743
|
put("success", true)
|
|
1763
1744
|
})
|
|
1764
1745
|
} catch (e: Exception) {
|
|
1765
|
-
|
|
1746
|
+
Log.e("StreamCallPlugin", "Error setting camera: ${e.message}")
|
|
1766
1747
|
call.reject("Failed to set camera: ${e.message}")
|
|
1767
1748
|
}
|
|
1768
1749
|
}
|
|
1769
1750
|
} catch (e: Exception) {
|
|
1751
|
+
Log.e("StreamCallPlugin", "Error setting camera: ${e.message}")
|
|
1770
1752
|
call.reject("StreamVideo not initialized")
|
|
1771
1753
|
}
|
|
1772
1754
|
}
|
|
@@ -1774,29 +1756,29 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1774
1756
|
@OptIn(InternalStreamVideoApi::class)
|
|
1775
1757
|
private suspend fun endCallRaw(call: Call) {
|
|
1776
1758
|
val callId = call.id
|
|
1777
|
-
|
|
1759
|
+
Log.d("StreamCallPlugin", "Attempting to end call $callId")
|
|
1778
1760
|
|
|
1779
1761
|
try {
|
|
1780
1762
|
// Get call information to make the decision
|
|
1781
1763
|
val callInfo = call.get()
|
|
1782
|
-
val callData = callInfo
|
|
1764
|
+
val callData = callInfo.getOrNull()?.call
|
|
1783
1765
|
val currentUserId = streamVideoClient?.userId
|
|
1784
1766
|
val createdBy = callData?.createdBy?.id
|
|
1785
1767
|
val isCreator = createdBy == currentUserId
|
|
1786
1768
|
|
|
1787
1769
|
// Use call.state.totalParticipants to get participant count (as per StreamVideo Android SDK docs)
|
|
1788
|
-
val totalParticipants = call.state.totalParticipants.value
|
|
1770
|
+
val totalParticipants = call.state.totalParticipants.value
|
|
1789
1771
|
val shouldEndCall = isCreator || totalParticipants <= 1
|
|
1790
1772
|
|
|
1791
|
-
|
|
1773
|
+
Log.d("StreamCallPlugin", "Call $callId - Creator: $createdBy, CurrentUser: $currentUserId, IsCreator: $isCreator, TotalParticipants: $totalParticipants, ShouldEnd: $shouldEndCall")
|
|
1792
1774
|
|
|
1793
1775
|
if (shouldEndCall) {
|
|
1794
1776
|
// End the call for everyone if I'm the creator or only 1 person
|
|
1795
|
-
|
|
1777
|
+
Log.d("StreamCallPlugin", "Ending call $callId for all participants (creator: $isCreator, participants: $totalParticipants)")
|
|
1796
1778
|
call.end()
|
|
1797
1779
|
} else {
|
|
1798
1780
|
// Just leave the call if there are more than 1 person and I'm not the creator
|
|
1799
|
-
|
|
1781
|
+
Log.d("StreamCallPlugin", "Leaving call $callId (not creator, >1 participants)")
|
|
1800
1782
|
call.leave()
|
|
1801
1783
|
}
|
|
1802
1784
|
|
|
@@ -1806,7 +1788,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1806
1788
|
}
|
|
1807
1789
|
|
|
1808
1790
|
} catch (e: Exception) {
|
|
1809
|
-
|
|
1791
|
+
Log.e("StreamCallPlugin", "Error getting call info for $callId, defaulting to leave()", e)
|
|
1810
1792
|
// Fallback to leave if we can't determine the call info
|
|
1811
1793
|
call.leave()
|
|
1812
1794
|
}
|
|
@@ -1814,12 +1796,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1814
1796
|
// Capture context from the overlayView
|
|
1815
1797
|
val currentContext = overlayView?.context ?: this.savedContext
|
|
1816
1798
|
if (currentContext == null) {
|
|
1817
|
-
|
|
1799
|
+
Log.w("StreamCallPlugin", "Cannot end call $callId because context is null")
|
|
1818
1800
|
return
|
|
1819
1801
|
}
|
|
1820
1802
|
|
|
1821
1803
|
runOnMainThread {
|
|
1822
|
-
|
|
1804
|
+
Log.d("StreamCallPlugin", "Setting overlay invisible after ending call $callId")
|
|
1823
1805
|
|
|
1824
1806
|
|
|
1825
1807
|
currentContext.let { ctx ->
|
|
@@ -1838,7 +1820,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1838
1820
|
if (savedCapacitorActivity != null) {
|
|
1839
1821
|
|
|
1840
1822
|
if (savedActivityPaused) {
|
|
1841
|
-
|
|
1823
|
+
Log.d("StreamCallPlugin", "Activity is paused. Adding call ${call.id} to savedCallsToEndOnResume")
|
|
1842
1824
|
savedCallsToEndOnResume.add(call)
|
|
1843
1825
|
} else {
|
|
1844
1826
|
transEndCallRaw(call)
|
|
@@ -1852,7 +1834,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1852
1834
|
bridge?.webView?.setBackgroundColor(Color.WHITE) // Restore webview opacity
|
|
1853
1835
|
|
|
1854
1836
|
// Also hide incoming call view if visible
|
|
1855
|
-
|
|
1837
|
+
Log.d("StreamCallPlugin", "Hiding incoming call view for call $callId")
|
|
1856
1838
|
// No dedicated incoming-call native view anymore; UI handled by web layer
|
|
1857
1839
|
}
|
|
1858
1840
|
|
|
@@ -1863,21 +1845,21 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1863
1845
|
private fun changeActivityAsVisibleOnLockScreen(activity: Activity, visible: Boolean) {
|
|
1864
1846
|
if (visible) {
|
|
1865
1847
|
// Ensure the activity is visible over the lock screen when launched via full-screen intent
|
|
1866
|
-
|
|
1848
|
+
Log.d("StreamCallPlugin", "Mark the mainActivity as visible on the lockscreen")
|
|
1867
1849
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
|
1868
1850
|
activity.setShowWhenLocked(true)
|
|
1869
1851
|
activity.setTurnScreenOn(true)
|
|
1870
1852
|
} else {
|
|
1871
|
-
activity.
|
|
1853
|
+
activity.window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
|
1872
1854
|
}
|
|
1873
1855
|
} else {
|
|
1874
1856
|
// Ensure the activity is NOT visible over the lock screen when launched via full-screen intent
|
|
1875
|
-
|
|
1857
|
+
Log.d("StreamCallPlugin", "Clear the flag for the mainActivity for visible on the lockscreen")
|
|
1876
1858
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
|
1877
1859
|
activity.setShowWhenLocked(false)
|
|
1878
1860
|
activity.setTurnScreenOn(false)
|
|
1879
1861
|
} else {
|
|
1880
|
-
activity.
|
|
1862
|
+
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
|
1881
1863
|
}
|
|
1882
1864
|
}
|
|
1883
1865
|
|
|
@@ -1888,21 +1870,21 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1888
1870
|
val callId = call.id
|
|
1889
1871
|
val savedCapacitorActivity = savedActivity
|
|
1890
1872
|
if (savedCapacitorActivity == null) {
|
|
1891
|
-
|
|
1873
|
+
Log.d("StreamCallPlugin", "Cannot perform transEndCallRaw for call $callId. savedCapacitorActivity is null")
|
|
1892
1874
|
return
|
|
1893
1875
|
}
|
|
1894
|
-
|
|
1876
|
+
Log.d("StreamCallPlugin", "Performing a trans-instance call to end call with id $callId")
|
|
1895
1877
|
if (savedCapacitorActivity !is BridgeActivity) {
|
|
1896
|
-
|
|
1878
|
+
Log.e("StreamCallPlugin", "Saved activity is NOT a Capactor activity. Saved activity class: ${savedCapacitorActivity.javaClass.canonicalName}")
|
|
1897
1879
|
return
|
|
1898
1880
|
}
|
|
1899
1881
|
val plugin = savedCapacitorActivity.bridge.getPlugin("StreamCall")
|
|
1900
1882
|
if (plugin == null) {
|
|
1901
|
-
|
|
1883
|
+
Log.e("StreamCallPlugin", "Plugin with name StreamCall not found?????")
|
|
1902
1884
|
return
|
|
1903
1885
|
}
|
|
1904
1886
|
if (plugin.instance !is StreamCallPlugin) {
|
|
1905
|
-
|
|
1887
|
+
Log.e("StreamCallPlugin", "Plugin found, but invalid instance")
|
|
1906
1888
|
return
|
|
1907
1889
|
}
|
|
1908
1890
|
|
|
@@ -1910,7 +1892,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1910
1892
|
try {
|
|
1911
1893
|
(plugin.instance as StreamCallPlugin).endCallRaw(call)
|
|
1912
1894
|
} catch (e: Exception) {
|
|
1913
|
-
|
|
1895
|
+
Log.e("StreamCallPlugin", "Error ending call on remote instance", e)
|
|
1914
1896
|
}
|
|
1915
1897
|
}
|
|
1916
1898
|
}
|
|
@@ -1925,12 +1907,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1925
1907
|
val callToEnd = activeCall ?: ringingCall
|
|
1926
1908
|
|
|
1927
1909
|
if (callToEnd == null) {
|
|
1928
|
-
|
|
1910
|
+
Log.w("StreamCallPlugin", "Attempted to end call but no active or ringing call found")
|
|
1929
1911
|
call.reject("No active call to end")
|
|
1930
1912
|
return
|
|
1931
1913
|
}
|
|
1932
1914
|
|
|
1933
|
-
|
|
1915
|
+
Log.d("StreamCallPlugin", "Ending call: activeCall=${activeCall?.id}, ringingCall=${ringingCall?.id}, callToEnd=${callToEnd.id}")
|
|
1934
1916
|
|
|
1935
1917
|
kotlinx.coroutines.GlobalScope.launch {
|
|
1936
1918
|
try {
|
|
@@ -1939,11 +1921,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1939
1921
|
put("success", true)
|
|
1940
1922
|
})
|
|
1941
1923
|
} catch (e: Exception) {
|
|
1942
|
-
|
|
1924
|
+
Log.e("StreamCallPlugin", "Error ending call: ${e.message}")
|
|
1943
1925
|
call.reject("Failed to end call: ${e.message}")
|
|
1944
1926
|
}
|
|
1945
1927
|
}
|
|
1946
1928
|
} catch (e: Exception) {
|
|
1929
|
+
Log.e("StreamCallPlugin", "Error ending call: ${e.message}")
|
|
1947
1930
|
call.reject("StreamVideo not initialized")
|
|
1948
1931
|
}
|
|
1949
1932
|
}
|
|
@@ -1974,20 +1957,20 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1974
1957
|
val callType = call.getString("type") ?: "default"
|
|
1975
1958
|
val shouldRing = call.getBoolean("ring") ?: true
|
|
1976
1959
|
val callId = java.util.UUID.randomUUID().toString()
|
|
1977
|
-
val team = call.getString("team")
|
|
1960
|
+
val team = call.getString("team")
|
|
1978
1961
|
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1962
|
+
Log.d("StreamCallPlugin", "Creating call:")
|
|
1963
|
+
Log.d("StreamCallPlugin", "- Call ID: $callId")
|
|
1964
|
+
Log.d("StreamCallPlugin", "- Call Type: $callType")
|
|
1965
|
+
Log.d("StreamCallPlugin", "- Users: $userIds")
|
|
1966
|
+
Log.d("StreamCallPlugin", "- Should Ring: $shouldRing")
|
|
1984
1967
|
if (custom != null) {
|
|
1985
|
-
|
|
1968
|
+
Log.d("StreamCallPlugin", "- Custom data: $custom")
|
|
1986
1969
|
}
|
|
1987
1970
|
|
|
1988
1971
|
// Check permissions before creating the call
|
|
1989
1972
|
if (!checkPermissions()) {
|
|
1990
|
-
|
|
1973
|
+
Log.d("StreamCallPlugin", "Permissions not granted, storing call parameters and requesting permissions")
|
|
1991
1974
|
// Store call parameters for later execution
|
|
1992
1975
|
pendingCall = call
|
|
1993
1976
|
pendingCallUserIds = userIds
|
|
@@ -2026,7 +2009,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2026
2009
|
|
|
2027
2010
|
callStates[callCid] = callState
|
|
2028
2011
|
|
|
2029
|
-
|
|
2012
|
+
Log.d("StreamCallPlugin", "Started timeout monitor for call $callCid with ${memberIds.size} members")
|
|
2030
2013
|
}
|
|
2031
2014
|
|
|
2032
2015
|
private fun checkCallTimeout(callCid: String) {
|
|
@@ -2036,12 +2019,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2036
2019
|
val elapsedSeconds = (now - callState.createdAt) / 1000
|
|
2037
2020
|
|
|
2038
2021
|
if (elapsedSeconds >= 30) {
|
|
2039
|
-
|
|
2022
|
+
Log.d("StreamCallPlugin", "Call $callCid has timed out after $elapsedSeconds seconds")
|
|
2040
2023
|
|
|
2041
2024
|
val hasAccepted = callState.participantResponses.values.any { it == "accepted" }
|
|
2042
2025
|
|
|
2043
2026
|
if (!hasAccepted) {
|
|
2044
|
-
|
|
2027
|
+
Log.d("StreamCallPlugin", "No one accepted call $callCid, marking all non-responders as missed")
|
|
2045
2028
|
|
|
2046
2029
|
// First, remove the timer to prevent further callbacks
|
|
2047
2030
|
callState.timer?.removeCallbacksAndMessages(null)
|
|
@@ -2072,7 +2055,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2072
2055
|
// Notify that call has ended using helper
|
|
2073
2056
|
updateCallStatusAndNotify(callCid, "ended", null, "timeout")
|
|
2074
2057
|
} catch (e: Exception) {
|
|
2075
|
-
|
|
2058
|
+
Log.e("StreamCallPlugin", "Error ending timed out call", e)
|
|
2076
2059
|
}
|
|
2077
2060
|
}
|
|
2078
2061
|
}
|
|
@@ -2087,7 +2070,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2087
2070
|
|
|
2088
2071
|
if (callState != null) {
|
|
2089
2072
|
// Ensure timer is properly canceled
|
|
2090
|
-
|
|
2073
|
+
Log.d("StreamCallPlugin", "Stopping timer for call: $callCid")
|
|
2091
2074
|
callState.timer?.removeCallbacksAndMessages(null)
|
|
2092
2075
|
callState.timer = null
|
|
2093
2076
|
}
|
|
@@ -2097,13 +2080,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2097
2080
|
|
|
2098
2081
|
// Hide UI elements directly without setting content
|
|
2099
2082
|
runOnMainThread {
|
|
2100
|
-
|
|
2083
|
+
Log.d("StreamCallPlugin", "Hiding UI elements for call $callCid (one-time cleanup)")
|
|
2101
2084
|
overlayView?.isVisible = false
|
|
2102
2085
|
// here we will also make sure we don't show on lock screen
|
|
2103
2086
|
changeActivityAsVisibleOnLockScreen(this.activity, false)
|
|
2104
2087
|
}
|
|
2105
2088
|
|
|
2106
|
-
|
|
2089
|
+
Log.d("StreamCallPlugin", "Cleaned up resources for ended call: $callCid")
|
|
2107
2090
|
}
|
|
2108
2091
|
|
|
2109
2092
|
private fun checkAllParticipantsResponded(callCid: String) {
|
|
@@ -2112,14 +2095,14 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2112
2095
|
val totalParticipants = callState.members.size
|
|
2113
2096
|
val responseCount = callState.participantResponses.size
|
|
2114
2097
|
|
|
2115
|
-
|
|
2098
|
+
Log.d("StreamCallPlugin", "Checking responses for call $callCid: $responseCount / $totalParticipants")
|
|
2116
2099
|
|
|
2117
2100
|
val allResponded = responseCount >= totalParticipants
|
|
2118
2101
|
val allRejectedOrMissed = allResponded &&
|
|
2119
2102
|
callState.participantResponses.values.all { it == "rejected" || it == "missed" }
|
|
2120
2103
|
|
|
2121
2104
|
if (allResponded && allRejectedOrMissed) {
|
|
2122
|
-
|
|
2105
|
+
Log.d("StreamCallPlugin", "All participants have rejected or missed the call $callCid")
|
|
2123
2106
|
|
|
2124
2107
|
// Cancel the timer immediately to prevent further callbacks
|
|
2125
2108
|
callState.timer?.removeCallbacksAndMessages(null)
|
|
@@ -2143,7 +2126,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2143
2126
|
// Notify that call has ended using helper
|
|
2144
2127
|
updateCallStatusAndNotify(callCid, "ended", null, "all_rejected_or_missed")
|
|
2145
2128
|
} catch (e: Exception) {
|
|
2146
|
-
|
|
2129
|
+
Log.e("StreamCallPlugin", "Error ending call after all rejected/missed", e)
|
|
2147
2130
|
}
|
|
2148
2131
|
}
|
|
2149
2132
|
}
|
|
@@ -2153,10 +2136,10 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2153
2136
|
|
|
2154
2137
|
private suspend fun magicDeviceDelete(streamVideoClient: StreamVideo) {
|
|
2155
2138
|
try {
|
|
2156
|
-
|
|
2139
|
+
Log.d("StreamCallPlugin", "Starting magicDeviceDelete operation")
|
|
2157
2140
|
|
|
2158
2141
|
FirebaseMessaging.getInstance().token.await()?.let {
|
|
2159
|
-
|
|
2142
|
+
Log.d("StreamCallPlugin", "Found firebase token")
|
|
2160
2143
|
val device = Device(
|
|
2161
2144
|
id = it,
|
|
2162
2145
|
pushProvider = PushProvider.FIREBASE.key,
|
|
@@ -2166,7 +2149,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2166
2149
|
streamVideoClient.deleteDevice(device)
|
|
2167
2150
|
}
|
|
2168
2151
|
} catch (e: Exception) {
|
|
2169
|
-
|
|
2152
|
+
Log.e("StreamCallPlugin", "Error in magicDeviceDelete", e)
|
|
2170
2153
|
}
|
|
2171
2154
|
}
|
|
2172
2155
|
|
|
@@ -2231,12 +2214,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2231
2214
|
|
|
2232
2215
|
try {
|
|
2233
2216
|
saveDynamicApiKey(apiKey)
|
|
2234
|
-
|
|
2217
|
+
Log.d("StreamCallPlugin", "Dynamic API key saved successfully")
|
|
2235
2218
|
call.resolve(JSObject().apply {
|
|
2236
2219
|
put("success", true)
|
|
2237
2220
|
})
|
|
2238
2221
|
} catch (e: Exception) {
|
|
2239
|
-
|
|
2222
|
+
Log.e("StreamCallPlugin", "Error saving dynamic API key", e)
|
|
2240
2223
|
call.reject("Failed to save API key: ${e.message}")
|
|
2241
2224
|
}
|
|
2242
2225
|
}
|
|
@@ -2255,7 +2238,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2255
2238
|
}
|
|
2256
2239
|
})
|
|
2257
2240
|
} catch (e: Exception) {
|
|
2258
|
-
|
|
2241
|
+
Log.e("StreamCallPlugin", "Error getting dynamic API key", e)
|
|
2259
2242
|
call.reject("Failed to get API key: ${e.message}")
|
|
2260
2243
|
}
|
|
2261
2244
|
}
|
|
@@ -2263,17 +2246,9 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2263
2246
|
// Helper functions for managing dynamic API key in SharedPreferences
|
|
2264
2247
|
private fun saveDynamicApiKey(apiKey: String) {
|
|
2265
2248
|
val sharedPrefs = getApiKeyPreferences()
|
|
2266
|
-
sharedPrefs.edit
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
}
|
|
2270
|
-
|
|
2271
|
-
// Helper functions for managing dynamic API key in SharedPreferences
|
|
2272
|
-
private fun clearDynamicApiKey() {
|
|
2273
|
-
val sharedPrefs = getApiKeyPreferences()
|
|
2274
|
-
sharedPrefs.edit()
|
|
2275
|
-
.remove(DYNAMIC_API_KEY_PREF)
|
|
2276
|
-
.apply()
|
|
2249
|
+
sharedPrefs.edit {
|
|
2250
|
+
putString(DYNAMIC_API_KEY_PREF, apiKey)
|
|
2251
|
+
}
|
|
2277
2252
|
}
|
|
2278
2253
|
|
|
2279
2254
|
private fun getDynamicApiKey(): String? {
|
|
@@ -2298,18 +2273,18 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2298
2273
|
// A) Check if the key exists in the custom preference
|
|
2299
2274
|
val dynamicApiKey = getDynamicApiKey(context)
|
|
2300
2275
|
return if (!dynamicApiKey.isNullOrEmpty() && dynamicApiKey.trim().isNotEmpty()) {
|
|
2301
|
-
|
|
2276
|
+
Log.d("StreamCallPlugin", "Using dynamic API key")
|
|
2302
2277
|
dynamicApiKey
|
|
2303
2278
|
} else {
|
|
2304
2279
|
// B) If not, use R.string.CAPACITOR_STREAM_VIDEO_APIKEY
|
|
2305
|
-
|
|
2280
|
+
Log.d("StreamCallPlugin", "Using static API key from resources")
|
|
2306
2281
|
context.getString(R.string.CAPACITOR_STREAM_VIDEO_APIKEY)
|
|
2307
2282
|
}
|
|
2308
2283
|
}
|
|
2309
2284
|
|
|
2310
2285
|
// Helper method to update call status and notify listeners
|
|
2311
2286
|
private fun updateCallStatusAndNotify(callId: String, state: String, userId: String? = null, reason: String? = null, members: List<Map<String, Any>>? = null, caller: Map<String, Any>? = null) {
|
|
2312
|
-
|
|
2287
|
+
Log.d("StreamCallPlugin", "updateCallStatusAndNotify called: callId=$callId, state=$state, userId=$userId, reason=$reason")
|
|
2313
2288
|
// Update stored call info
|
|
2314
2289
|
currentCallId = callId
|
|
2315
2290
|
currentCallState = state
|
|
@@ -2351,6 +2326,13 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2351
2326
|
}
|
|
2352
2327
|
}
|
|
2353
2328
|
|
|
2329
|
+
val eventString = data.toString()
|
|
2330
|
+
if (lastEventSent == eventString) {
|
|
2331
|
+
Log.d("StreamCallPlugin", "Duplicate event detected, not sending: $eventString")
|
|
2332
|
+
return
|
|
2333
|
+
}
|
|
2334
|
+
lastEventSent = eventString
|
|
2335
|
+
|
|
2354
2336
|
// Notify listeners
|
|
2355
2337
|
notifyListeners("callEvent", data)
|
|
2356
2338
|
}
|
|
@@ -2396,19 +2378,19 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2396
2378
|
private val acceptCallReceiver = object : BroadcastReceiver() {
|
|
2397
2379
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
2398
2380
|
if (intent?.action == "io.getstream.video.android.action.ACCEPT_CALL") {
|
|
2399
|
-
|
|
2381
|
+
Log.d("StreamCallPlugin", "BroadcastReceiver: Received broadcast with action: ${intent.action}")
|
|
2400
2382
|
val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
|
|
2401
2383
|
if (cid != null) {
|
|
2402
|
-
|
|
2384
|
+
Log.d("StreamCallPlugin", "BroadcastReceiver: ACCEPT_CALL broadcast received with cid: $cid")
|
|
2403
2385
|
val call = streamVideoClient?.call(id = cid.id, type = cid.type)
|
|
2404
2386
|
if (call != null) {
|
|
2405
|
-
|
|
2387
|
+
Log.d("StreamCallPlugin", "BroadcastReceiver: Accepting call with cid: $cid")
|
|
2406
2388
|
kotlinx.coroutines.GlobalScope.launch {
|
|
2407
2389
|
internalAcceptCall(call, requestPermissionsAfter = !checkPermissions())
|
|
2408
2390
|
}
|
|
2409
2391
|
bringAppToForeground()
|
|
2410
2392
|
} else {
|
|
2411
|
-
|
|
2393
|
+
Log.e("StreamCallPlugin", "BroadcastReceiver: Call object is null for cid: $cid")
|
|
2412
2394
|
}
|
|
2413
2395
|
}
|
|
2414
2396
|
}
|
|
@@ -2422,12 +2404,12 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2422
2404
|
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
2423
2405
|
if (launchIntent != null) {
|
|
2424
2406
|
ctx.startActivity(launchIntent)
|
|
2425
|
-
|
|
2407
|
+
Log.d("StreamCallPlugin", "bringAppToForeground: Launch intent executed to foreground app")
|
|
2426
2408
|
} else {
|
|
2427
|
-
|
|
2409
|
+
Log.w("StreamCallPlugin", "bringAppToForeground: launchIntent is null")
|
|
2428
2410
|
}
|
|
2429
2411
|
} catch (e: Exception) {
|
|
2430
|
-
|
|
2412
|
+
Log.e("StreamCallPlugin", "bringAppToForeground error", e)
|
|
2431
2413
|
}
|
|
2432
2414
|
}
|
|
2433
2415
|
|