@capgo/capacitor-stream-call 0.0.6 → 0.0.19

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.
@@ -6,6 +6,8 @@ import android.app.Application
6
6
  import android.app.KeyguardManager
7
7
  import android.content.Context
8
8
  import android.graphics.Color
9
+ import android.media.RingtoneManager
10
+ import android.net.Uri
9
11
  import android.os.Bundle
10
12
  import android.os.Handler
11
13
  import android.os.Looper
@@ -20,31 +22,34 @@ import com.getcapacitor.Plugin
20
22
  import com.getcapacitor.PluginCall
21
23
  import com.getcapacitor.PluginMethod
22
24
  import com.getcapacitor.annotation.CapacitorPlugin
23
- import io.getstream.android.push.firebase.FirebasePushDeviceGenerator
24
25
  import io.getstream.android.push.permissions.ActivityLifecycleCallbacks
25
26
  import io.getstream.video.android.core.Call
26
27
  import io.getstream.video.android.core.GEO
27
28
  import io.getstream.video.android.core.StreamVideo
28
29
  import io.getstream.video.android.core.StreamVideoBuilder
29
- import io.getstream.video.android.core.model.RejectReason
30
30
  import io.getstream.video.android.core.notifications.NotificationConfig
31
31
  import io.getstream.video.android.core.notifications.NotificationHandler
32
- import io.getstream.video.android.core.sounds.emptyRingingConfig
33
32
  import io.getstream.video.android.core.sounds.toSounds
34
33
  import io.getstream.video.android.model.StreamCallId
35
34
  import io.getstream.video.android.model.User
36
35
  import io.getstream.video.android.model.streamCallId
37
36
  import kotlinx.coroutines.DelicateCoroutinesApi
38
- import kotlinx.coroutines.flow.Flow
39
37
  import kotlinx.coroutines.launch
40
- import org.openapitools.client.models.CallAcceptedEvent
41
- import org.openapitools.client.models.CallEndedEvent
42
- import org.openapitools.client.models.CallMissedEvent
43
- import org.openapitools.client.models.CallRejectedEvent
44
- import org.openapitools.client.models.CallSessionEndedEvent
45
- import org.openapitools.client.models.VideoEvent
46
38
  import io.getstream.video.android.model.Device
47
- import kotlinx.coroutines.flow.first
39
+ import kotlinx.coroutines.tasks.await
40
+ import com.google.firebase.messaging.FirebaseMessaging
41
+ import io.getstream.android.push.PushProvider
42
+ import io.getstream.android.push.firebase.FirebasePushDeviceGenerator
43
+ import io.getstream.android.video.generated.models.CallAcceptedEvent
44
+ import io.getstream.android.video.generated.models.CallCreatedEvent
45
+ import io.getstream.android.video.generated.models.CallEndedEvent
46
+ import io.getstream.android.video.generated.models.CallMissedEvent
47
+ import io.getstream.android.video.generated.models.CallRejectedEvent
48
+ import io.getstream.android.video.generated.models.CallRingEvent
49
+ import io.getstream.android.video.generated.models.CallSessionEndedEvent
50
+ import io.getstream.android.video.generated.models.CallSessionStartedEvent
51
+ import io.getstream.android.video.generated.models.VideoEvent
52
+ import io.getstream.video.android.core.sounds.RingingConfig
48
53
 
49
54
  // I am not a religious pearson, but at this point, I am not sure even god himself would understand this code
50
55
  // It's a spaghetti-like, tangled, unreadable mess and frankly, I am deeply sorry for the code crimes commited in the Android impl
@@ -63,7 +68,12 @@ public class StreamCallPlugin : Plugin() {
63
68
  private var savedActivity: Activity? = null
64
69
  private var savedActivityPaused = false
65
70
  private var savedCallsToEndOnResume = mutableListOf<Call>()
66
- private val callStates: MutableMap<String, CallState> = mutableMapOf()
71
+ private val callStates: MutableMap<String, LocalCallState> = mutableMapOf()
72
+
73
+ // Store current call info
74
+ private var currentCallId: String = ""
75
+ private var currentCallType: String = ""
76
+ private var currentCallState: String = ""
67
77
 
68
78
  private enum class State {
69
79
  NOT_INITIALIZED,
@@ -71,6 +81,11 @@ public class StreamCallPlugin : Plugin() {
71
81
  INITIALIZED
72
82
  }
73
83
 
84
+ public fun incomingOnlyRingingConfig(): RingingConfig = object : RingingConfig {
85
+ override val incomingCallSoundUri: Uri? = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
86
+ override val outgoingCallSoundUri: Uri? = null
87
+ }
88
+
74
89
  private fun runOnMainThread(action: () -> Unit) {
75
90
  mainHandler.post { action() }
76
91
  }
@@ -170,7 +185,7 @@ public class StreamCallPlugin : Plugin() {
170
185
  declineCall(declinedCall)
171
186
  },
172
187
  onAcceptCall = { acceptedCall ->
173
- acceptCall(acceptedCall)
188
+ internalAcceptCall(acceptedCall)
174
189
  },
175
190
  onHideIncomingCall = {
176
191
  hideIncomingCall()
@@ -192,7 +207,7 @@ public class StreamCallPlugin : Plugin() {
192
207
  val call = streamVideoClient?.call(id = cid.id, type = cid.type)
193
208
  kotlinx.coroutines.GlobalScope.launch {
194
209
  call?.get()
195
- call?.let { acceptCall(it) }
210
+ call?.let { internalAcceptCall(it) }
196
211
  }
197
212
  }
198
213
  }
@@ -211,12 +226,8 @@ public class StreamCallPlugin : Plugin() {
211
226
  // Stop ringtone
212
227
  ringtonePlayer?.stopRinging()
213
228
 
214
- // Notify that call has ended
215
- val data = JSObject().apply {
216
- put("callId", call.id)
217
- put("state", "rejected")
218
- }
219
- notifyListeners("callEvent", data)
229
+ // Notify that call has ended using our helper
230
+ updateCallStatusAndNotify(call.id, "rejected")
220
231
 
221
232
  hideIncomingCall()
222
233
  } catch (e: Exception) {
@@ -250,14 +261,6 @@ public class StreamCallPlugin : Plugin() {
250
261
  }
251
262
  }
252
263
 
253
- // private fun remoteIncomingCallNotif() {
254
- // CallService.removeIncomingCall(
255
- // context,
256
- // StreamCallId.fromCallCid(call.cid),
257
- // StreamVideo.instance().state.callConfigRegistry.get(call.type),
258
- // )
259
- // }
260
-
261
264
  private fun setupViews() {
262
265
  val context = context
263
266
  val parent = bridge?.webView?.parent as? ViewGroup ?: return
@@ -468,16 +471,17 @@ public class StreamCallPlugin : Plugin() {
468
471
  )
469
472
 
470
473
  val notificationConfig = NotificationConfig(
471
- pushDeviceGenerators = listOf(FirebasePushDeviceGenerator(
474
+ pushDeviceGenerators = listOf(
475
+ FirebasePushDeviceGenerator(
472
476
  providerName = "firebase",
473
477
  context = contextToUse
474
- )),
478
+ )
479
+ ),
475
480
  requestPermissionOnAppLaunch = { true },
476
481
  notificationHandler = notificationHandler,
477
482
  )
478
483
 
479
- val soundsConfig = emptyRingingConfig()
480
- soundsConfig.incomingCallSoundUri
484
+ val soundsConfig = incomingOnlyRingingConfig()
481
485
  // Initialize StreamVideo client
482
486
  streamVideoClient = StreamVideoBuilder(
483
487
  context = contextToUse,
@@ -561,62 +565,41 @@ public class StreamCallPlugin : Plugin() {
561
565
  client.subscribe { event: VideoEvent ->
562
566
  android.util.Log.v("StreamCallPlugin", "Received an event ${event.getEventType()} $event")
563
567
  when (event) {
568
+ is CallRingEvent -> {
569
+ updateCallStatusAndNotify(event.callCid, "ringing")
570
+ }
564
571
  // Handle CallCreatedEvent differently - only log it but don't try to access members yet
565
- is org.openapitools.client.models.CallCreatedEvent -> {
566
- val callCid = event.call.cid
572
+ is CallCreatedEvent -> {
573
+ val callCid = event.callCid
567
574
  android.util.Log.d("StreamCallPlugin", "Call created: $callCid")
568
575
 
569
576
  // let's get the members
570
- val callParticipants = event.members.filter{ it.user.id != this@StreamCallPlugin.streamVideoClient?.userId } .map { it.user.id }
577
+ val callParticipants = event.members.filter{ it.user.id != this@StreamCallPlugin.streamVideoClient?.userId }.map { it.user.id }
571
578
  android.util.Log.d("StreamCallPlugin", "Call created for $callCid with ${callParticipants.size} participants")
572
579
 
573
580
  // Start tracking this call now that we have the member list
574
581
  startCallTimeoutMonitor(callCid, callParticipants)
575
582
 
576
- val data = JSObject().apply {
577
- put("callId", callCid)
578
- put("state", "created")
579
- }
580
- notifyListeners("callEvent", data)
583
+ // Use direction from event if available
584
+ val callType = callCid.split(":").firstOrNull() ?: "default"
585
+ updateCallStatusAndNotify(callCid, "created")
581
586
  }
582
587
  // Add handler for CallSessionStartedEvent which contains participant information
583
- is org.openapitools.client.models.CallSessionStartedEvent -> {
584
- val callCid = event.call.cid
585
-
586
- val data = JSObject().apply {
587
- put("callId", callCid)
588
- put("state", "session_started")
589
- }
590
- notifyListeners("callEvent", data)
588
+ is CallSessionStartedEvent -> {
589
+ val callCid = event.callCid
590
+ updateCallStatusAndNotify(callCid, "session_started")
591
591
  }
592
592
 
593
593
  is CallRejectedEvent -> {
594
594
  val userId = event.user.id
595
- val callCid = event.call.cid
595
+ val callCid = event.callCid
596
596
 
597
597
  // Update call state
598
598
  callStates[callCid]?.let { callState ->
599
599
  callState.participantResponses[userId] = "rejected"
600
600
  }
601
601
 
602
- // runOnMainThread {
603
- // android.util.Log.d("StreamCallPlugin", "Setting overlay invisible due to CallRejectedEvent for call ${event.call.cid}")
604
- // overlayView?.setContent {
605
- // CallOverlayView(
606
- // context = context,
607
- // streamVideo = streamVideoClient,
608
- // call = null
609
- // )
610
- // }
611
- // overlayView?.isVisible = false
612
- // }
613
-
614
- val data = JSObject().apply {
615
- put("callId", event.call.cid)
616
- put("state", "rejected")
617
- put("userId", userId)
618
- }
619
- notifyListeners("callEvent", data)
602
+ updateCallStatusAndNotify(callCid, "rejected", userId)
620
603
 
621
604
  // Check if all participants have responded
622
605
  checkAllParticipantsResponded(callCid)
@@ -624,7 +607,7 @@ public class StreamCallPlugin : Plugin() {
624
607
 
625
608
  is CallMissedEvent -> {
626
609
  val userId = event.user.id
627
- val callCid = event.call.cid
610
+ val callCid = event.callCid
628
611
 
629
612
  // Update call state
630
613
  callStates[callCid]?.let { callState ->
@@ -638,12 +621,7 @@ public class StreamCallPlugin : Plugin() {
638
621
  moveAllActivitiesToBackgroundOrKill(context)
639
622
  }
640
623
 
641
- val data = JSObject().apply {
642
- put("callId", callCid)
643
- put("state", "missed")
644
- put("userId", userId)
645
- }
646
- notifyListeners("callEvent", data)
624
+ updateCallStatusAndNotify(callCid, "missed", userId)
647
625
 
648
626
  // Check if all participants have responded
649
627
  checkAllParticipantsResponded(callCid)
@@ -651,7 +629,7 @@ public class StreamCallPlugin : Plugin() {
651
629
 
652
630
  is CallAcceptedEvent -> {
653
631
  val userId = event.user.id
654
- val callCid = event.call.cid
632
+ val callCid = event.callCid
655
633
 
656
634
  // Update call state
657
635
  callStates[callCid]?.let { callState ->
@@ -663,48 +641,34 @@ public class StreamCallPlugin : Plugin() {
663
641
  callState.timer = null
664
642
  }
665
643
 
666
- val data = JSObject().apply {
667
- put("callId", callCid)
668
- put("state", "accepted")
669
- put("userId", userId)
670
- }
671
- notifyListeners("callEvent", data)
644
+ updateCallStatusAndNotify(callCid, "accepted", userId)
672
645
  }
673
646
 
674
647
  is CallEndedEvent -> {
675
648
  runOnMainThread {
676
- android.util.Log.d("StreamCallPlugin", "Setting overlay invisible due to CallEndedEvent for call ${event.call.cid}")
649
+ android.util.Log.d("StreamCallPlugin", "Setting overlay invisible due to CallEndedEvent for call ${event.callCid}")
677
650
  // Clean up call resources
678
- val callCid = event.call.cid
651
+ val callCid = event.callCid
679
652
  cleanupCall(callCid)
680
653
  }
681
- val data = JSObject().apply {
682
- put("callId", event.call.cid)
683
- put("state", "left")
684
- }
685
- notifyListeners("callEvent", data)
654
+ updateCallStatusAndNotify(event.callCid, "left")
686
655
  }
687
656
 
688
657
  is CallSessionEndedEvent -> {
689
658
  runOnMainThread {
690
- android.util.Log.d("StreamCallPlugin", "Setting overlay invisible due to CallSessionEndedEvent for call ${event.call.cid}")
659
+ android.util.Log.d("StreamCallPlugin", "Setting overlay invisible due to CallSessionEndedEvent for call ${event.callCid}. Test session: ${event.call.session?.endedAt}")
691
660
  // Clean up call resources
692
- val callCid = event.call.cid
661
+ val callCid = event.callCid
693
662
  cleanupCall(callCid)
694
663
  }
695
- val data = JSObject().apply {
696
- put("callId", event.call.cid)
697
- put("state", "left")
698
- }
699
- notifyListeners("callEvent", data)
664
+ updateCallStatusAndNotify(event.callCid, "left")
700
665
  }
701
666
 
702
667
  else -> {
703
- val data = JSObject().apply {
704
- put("callId", streamVideoClient?.state?.activeCall?.value?.cid)
705
- put("state", event.getEventType())
706
- }
707
- notifyListeners("callEvent", data)
668
+ updateCallStatusAndNotify(
669
+ streamVideoClient?.state?.activeCall?.value?.cid ?: "",
670
+ event.getEventType()
671
+ )
708
672
  }
709
673
  }
710
674
  }
@@ -721,19 +685,11 @@ public class StreamCallPlugin : Plugin() {
721
685
  android.util.Log.d("StreamCallPlugin", "- All participants: ${state.participants}")
722
686
  android.util.Log.d("StreamCallPlugin", "- Remote participants: ${state.remoteParticipants}")
723
687
 
724
- // Notify that a call has started
725
- val data = JSObject().apply {
726
- put("callId", call.cid)
727
- put("state", "joined")
728
- }
729
- notifyListeners("callEvent", data)
688
+ // Notify that a call has started using our helper
689
+ updateCallStatusAndNotify(call.cid, "joined")
730
690
  } ?: run {
731
- // Notify that call has ended
732
- val data = JSObject().apply {
733
- put("callId", "")
734
- put("state", "left")
735
- }
736
- notifyListeners("callEvent", data)
691
+ // Notify that call has ended using our helper
692
+ updateCallStatusAndNotify("", "left")
737
693
  }
738
694
  }
739
695
  }
@@ -791,8 +747,42 @@ public class StreamCallPlugin : Plugin() {
791
747
  })
792
748
  }
793
749
 
750
+ @PluginMethod
751
+ public fun acceptCall(call: PluginCall) {
752
+ try {
753
+ val streamVideoCall = streamVideoClient?.state?.ringingCall?.value
754
+ if (streamVideoCall == null) {
755
+ call.reject("Ringing call is null")
756
+ return
757
+ }
758
+ kotlinx.coroutines.GlobalScope.launch {
759
+ internalAcceptCall(streamVideoCall)
760
+ }
761
+ } catch (t: Throwable) {
762
+ android.util.Log.d("StreamCallPlugin", "JS -> acceptCall fail", t);
763
+ call.reject("Cannot acceptCall")
764
+ }
765
+ }
766
+
767
+ @PluginMethod
768
+ public fun rejectCall(call: PluginCall) {
769
+ try {
770
+ val streamVideoCall = streamVideoClient?.state?.ringingCall?.value
771
+ if (streamVideoCall == null) {
772
+ call.reject("Ringing call is null")
773
+ return
774
+ }
775
+ kotlinx.coroutines.GlobalScope.launch {
776
+ declineCall(streamVideoCall)
777
+ }
778
+ } catch (t: Throwable) {
779
+ android.util.Log.d("StreamCallPlugin", "JS -> rejectCall fail", t);
780
+ call.reject("Cannot rejectCall")
781
+ }
782
+ }
783
+
794
784
  @OptIn(DelicateCoroutinesApi::class)
795
- private fun acceptCall(call: Call) {
785
+ private fun internalAcceptCall(call: Call) {
796
786
  kotlinx.coroutines.GlobalScope.launch {
797
787
  try {
798
788
  // Stop ringtone
@@ -807,12 +797,8 @@ public class StreamCallPlugin : Plugin() {
807
797
  // Join the call without affecting others
808
798
  call.accept()
809
799
 
810
- // Notify that call has started
811
- val data = JSObject().apply {
812
- put("callId", call.id)
813
- put("state", "joined")
814
- }
815
- notifyListeners("callEvent", data)
800
+ // Notify that call has started using helper
801
+ updateCallStatusAndNotify(call.id, "joined")
816
802
 
817
803
  // Show overlay view with the active call
818
804
  runOnMainThread {
@@ -935,7 +921,6 @@ public class StreamCallPlugin : Plugin() {
935
921
  val callId = call.id
936
922
  android.util.Log.d("StreamCallPlugin", "Attempting to end call $callId")
937
923
  call.leave()
938
- call.reject(reason = RejectReason.Cancel)
939
924
 
940
925
  // Capture context from the overlayView
941
926
  val currentContext = overlayView?.context ?: this.savedContext
@@ -988,12 +973,8 @@ public class StreamCallPlugin : Plugin() {
988
973
  incomingCallView?.isVisible = false
989
974
  }
990
975
 
991
- // Notify that call has ended
992
- val data = JSObject().apply {
993
- put("callId", callId)
994
- put("state", "left")
995
- }
996
- notifyListeners("callEvent", data)
976
+ // Notify that call has ended using helper
977
+ updateCallStatusAndNotify(callId, "left")
997
978
  }
998
979
 
999
980
  @OptIn(DelicateCoroutinesApi::class)
@@ -1079,7 +1060,7 @@ public class StreamCallPlugin : Plugin() {
1079
1060
  val callType = call.getString("type") ?: "default"
1080
1061
  val shouldRing = call.getBoolean("ring") ?: true
1081
1062
  val callId = java.util.UUID.randomUUID().toString()
1082
- val callCid = "$callType:$callId"
1063
+ val team = call.getString("team");
1083
1064
 
1084
1065
  android.util.Log.d("StreamCallPlugin", "Creating call:")
1085
1066
  android.util.Log.d("StreamCallPlugin", "- Call ID: $callId")
@@ -1098,15 +1079,23 @@ public class StreamCallPlugin : Plugin() {
1098
1079
 
1099
1080
  android.util.Log.d("StreamCallPlugin", "Creating call with members...")
1100
1081
  // Create the call with all members
1101
- streamCall?.create(
1082
+ val createResult = streamCall?.create(
1102
1083
  memberIds = userIds + selfUserId,
1103
1084
  custom = emptyMap(),
1104
- ring = shouldRing
1085
+ ring = shouldRing,
1086
+ team = team,
1105
1087
  )
1088
+
1089
+ if (createResult?.isFailure == true) {
1090
+ throw (createResult.errorOrNull() ?: RuntimeException("Unknown error creating call")) as Throwable
1091
+ }
1106
1092
 
1107
1093
  android.util.Log.d("StreamCallPlugin", "Setting overlay visible for outgoing call $callId")
1108
1094
  // Show overlay view
1109
1095
  activity?.runOnUiThread {
1096
+ streamCall?.microphone?.setEnabled(true)
1097
+ streamCall?.camera?.setEnabled(true)
1098
+
1110
1099
  overlayView?.setContent {
1111
1100
  CallOverlayView(
1112
1101
  context = context,
@@ -1132,7 +1121,7 @@ public class StreamCallPlugin : Plugin() {
1132
1121
  }
1133
1122
 
1134
1123
  private fun startCallTimeoutMonitor(callCid: String, memberIds: List<String>) {
1135
- val callState = CallState(members = memberIds)
1124
+ val callState = LocalCallState(members = memberIds)
1136
1125
 
1137
1126
  val handler = Handler(Looper.getMainLooper())
1138
1127
  val timeoutRunnable = object : Runnable {
@@ -1172,12 +1161,7 @@ public class StreamCallPlugin : Plugin() {
1172
1161
  if (memberId !in callState.participantResponses) {
1173
1162
  callState.participantResponses[memberId] = "missed"
1174
1163
 
1175
- val data = JSObject().apply {
1176
- put("callId", callCid)
1177
- put("state", "missed")
1178
- put("userId", memberId)
1179
- }
1180
- notifyListeners("callEvent", data)
1164
+ updateCallStatusAndNotify(callCid, "missed", memberId)
1181
1165
  }
1182
1166
  }
1183
1167
 
@@ -1195,13 +1179,8 @@ public class StreamCallPlugin : Plugin() {
1195
1179
  // Clean up state - we don't need to do this in endCallRaw because we already did it here
1196
1180
  callStates.remove(callCid)
1197
1181
 
1198
- // Notify that call has ended
1199
- val data = JSObject().apply {
1200
- put("callId", callCid)
1201
- put("state", "ended")
1202
- put("reason", "timeout")
1203
- }
1204
- notifyListeners("callEvent", data)
1182
+ // Notify that call has ended using helper
1183
+ updateCallStatusAndNotify(callCid, "ended", null, "timeout")
1205
1184
  } catch (e: Exception) {
1206
1185
  android.util.Log.e("StreamCallPlugin", "Error ending timed out call", e)
1207
1186
  }
@@ -1271,13 +1250,8 @@ public class StreamCallPlugin : Plugin() {
1271
1250
  // Clean up state - we don't need to do this in endCallRaw because we already did it here
1272
1251
  callStates.remove(callCid)
1273
1252
 
1274
- // Notify that call has ended
1275
- val data = JSObject().apply {
1276
- put("callId", callCid)
1277
- put("state", "ended")
1278
- put("reason", "all_rejected_or_missed")
1279
- }
1280
- notifyListeners("callEvent", data)
1253
+ // Notify that call has ended using helper
1254
+ updateCallStatusAndNotify(callCid, "ended", null, "all_rejected_or_missed")
1281
1255
  } catch (e: Exception) {
1282
1256
  android.util.Log.e("StreamCallPlugin", "Error ending call after all rejected/missed", e)
1283
1257
  }
@@ -1289,68 +1263,68 @@ public class StreamCallPlugin : Plugin() {
1289
1263
 
1290
1264
  private suspend fun magicDeviceDelete(streamVideoClient: StreamVideo) {
1291
1265
  try {
1292
- android.util.Log.d("StreamCallPlugin", "Starting magicDeviceDelete reflection operation")
1293
-
1294
- // Get the streamNotificationManager field from StreamVideo
1295
- val streamVideoClass = streamVideoClient.javaClass
1296
- val notificationManagerField = streamVideoClass.getDeclaredField("streamNotificationManager")
1297
- notificationManagerField.isAccessible = true
1298
- val notificationManager = notificationManagerField.get(streamVideoClient)
1299
-
1300
- if (notificationManager == null) {
1301
- android.util.Log.e("StreamCallPlugin", "streamNotificationManager is null")
1302
- return
1303
- }
1304
-
1305
- android.util.Log.d("StreamCallPlugin", "Successfully accessed streamNotificationManager")
1306
-
1307
- // Get deviceTokenStorage from notification manager
1308
- val notificationManagerClass = notificationManager.javaClass
1309
- val deviceTokenStorageField = notificationManagerClass.getDeclaredField("deviceTokenStorage")
1310
- deviceTokenStorageField.isAccessible = true
1311
- val deviceTokenStorage = deviceTokenStorageField.get(notificationManager)
1312
-
1313
- if (deviceTokenStorage == null) {
1314
- android.util.Log.e("StreamCallPlugin", "deviceTokenStorage is null")
1315
- return
1316
- }
1317
-
1318
- android.util.Log.d("StreamCallPlugin", "Successfully accessed deviceTokenStorage")
1319
-
1320
- // Access the DeviceTokenStorage object dynamically without hardcoding class
1321
- val deviceTokenStorageClass = deviceTokenStorage.javaClass
1322
-
1323
- // Get the userDevice Flow from deviceTokenStorage
1324
- val userDeviceField = deviceTokenStorageClass.getDeclaredField("userDevice")
1325
- userDeviceField.isAccessible = true
1326
- val userDeviceFlow = userDeviceField.get(deviceTokenStorage)
1327
-
1328
- if (userDeviceFlow == null) {
1329
- android.util.Log.e("StreamCallPlugin", "userDevice Flow is null")
1330
- return
1331
- }
1332
-
1333
- android.util.Log.d("StreamCallPlugin", "Successfully accessed userDevice Flow: $userDeviceFlow")
1266
+ android.util.Log.d("StreamCallPlugin", "Starting magicDeviceDelete operation")
1267
+
1268
+ FirebaseMessaging.getInstance().token.await()?.let {
1269
+ android.util.Log.d("StreamCallPlugin", "Found firebase token")
1270
+ val device = Device(
1271
+ id = it,
1272
+ pushProvider = PushProvider.FIREBASE.key,
1273
+ pushProviderName = "firebase",
1274
+ )
1334
1275
 
1335
- val castedUserDeviceFlow = userDeviceFlow as Flow<Device?>
1336
- try {
1337
- castedUserDeviceFlow.first {
1338
- if (it == null) {
1339
- android.util.Log.d("StreamCallPlugin", "Device is null. Nothing to remove")
1340
- return@first true;
1341
- }
1342
- streamVideoClient.deleteDevice(it)
1343
- return@first true;
1344
- }
1345
- } catch (e: Throwable) {
1346
- android.util.Log.e("StreamCallPlugin", "Cannot collect flow in magicDeviceDelete", e)
1276
+ streamVideoClient.deleteDevice(device)
1347
1277
  }
1348
1278
  } catch (e: Exception) {
1349
1279
  android.util.Log.e("StreamCallPlugin", "Error in magicDeviceDelete", e)
1350
1280
  }
1351
1281
  }
1352
1282
 
1353
- data class CallState(
1283
+ @PluginMethod
1284
+ fun getCallStatus(call: PluginCall) {
1285
+ // If not in a call, reject
1286
+ if (currentCallId.isEmpty() || currentCallState == "left") {
1287
+ call.reject("Not in a call")
1288
+ return
1289
+ }
1290
+
1291
+ val result = JSObject()
1292
+ result.put("callId", currentCallId)
1293
+ result.put("state", currentCallState)
1294
+
1295
+ // No additional fields to ensure compatibility with CallEvent interface
1296
+
1297
+ call.resolve(result)
1298
+ }
1299
+
1300
+ // Helper method to update call status and notify listeners
1301
+ private fun updateCallStatusAndNotify(callId: String, state: String, userId: String? = null, reason: String? = null) {
1302
+ // Update stored call info
1303
+ currentCallId = callId
1304
+ currentCallState = state
1305
+
1306
+ // Get call type from call ID if available
1307
+ if (callId.contains(":")) {
1308
+ currentCallType = callId.split(":").firstOrNull() ?: ""
1309
+ }
1310
+
1311
+ // Create data object with only the fields in the CallEvent interface
1312
+ val data = JSObject().apply {
1313
+ put("callId", callId)
1314
+ put("state", state)
1315
+ userId?.let {
1316
+ put("userId", it)
1317
+ }
1318
+ reason?.let {
1319
+ put("reason", it)
1320
+ }
1321
+ }
1322
+
1323
+ // Notify listeners
1324
+ notifyListeners("callEvent", data)
1325
+ }
1326
+
1327
+ data class LocalCallState(
1354
1328
  val members: List<String>,
1355
1329
  val participantResponses: MutableMap<String, String> = mutableMapOf(),
1356
1330
  val createdAt: Long = System.currentTimeMillis(),