@capgo/capacitor-stream-call 0.0.77 → 0.0.79
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/build.gradle +4 -4
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/AcceptCallReceiver.kt +35 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CustomNotificationHandler.kt +97 -16
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +26 -9
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -75,10 +75,10 @@ dependencies {
|
|
|
75
75
|
implementation "androidx.compose.material3:material3:1.3.2"
|
|
76
76
|
|
|
77
77
|
// Stream dependencies
|
|
78
|
-
implementation("io.getstream:stream-video-android-ui-compose:1.
|
|
79
|
-
implementation("io.getstream:stream-video-android-core:1.
|
|
80
|
-
implementation("io.getstream:stream-android-push:1.3.
|
|
81
|
-
implementation("io.getstream:stream-android-push-firebase:1.3.
|
|
78
|
+
implementation("io.getstream:stream-video-android-ui-compose:1.9.1")
|
|
79
|
+
implementation("io.getstream:stream-video-android-core:1.9.1")
|
|
80
|
+
implementation("io.getstream:stream-android-push:1.3.2")
|
|
81
|
+
implementation("io.getstream:stream-android-push-firebase:1.3.2")
|
|
82
82
|
|
|
83
83
|
// Firebase dependencies using BOM
|
|
84
84
|
implementation(platform('com.google.firebase:firebase-bom:33.13.0'))
|
|
@@ -18,6 +18,15 @@
|
|
|
18
18
|
android:name="ee.forgr.capacitor.streamcall.StreamCallBackgroundService"
|
|
19
19
|
android:enabled="true"
|
|
20
20
|
android:exported="false" />
|
|
21
|
+
|
|
22
|
+
<receiver
|
|
23
|
+
android:name=".AcceptCallReceiver"
|
|
24
|
+
android:enabled="true"
|
|
25
|
+
android:exported="true">
|
|
26
|
+
<intent-filter>
|
|
27
|
+
<action android:name="io.getstream.video.android.action.ACCEPT_CALL" />
|
|
28
|
+
</intent-filter>
|
|
29
|
+
</receiver>
|
|
21
30
|
</application>
|
|
22
31
|
|
|
23
32
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package ee.forgr.capacitor.streamcall
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import io.getstream.video.android.core.notifications.NotificationHandler
|
|
8
|
+
import io.getstream.video.android.model.streamCallId
|
|
9
|
+
|
|
10
|
+
class AcceptCallReceiver : BroadcastReceiver() {
|
|
11
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
12
|
+
Log.d("AcceptCallReceiver", "onReceive called with action: ${intent?.action}")
|
|
13
|
+
if (intent?.action == NotificationHandler.ACTION_ACCEPT_CALL) {
|
|
14
|
+
val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
|
|
15
|
+
if (cid == null) {
|
|
16
|
+
Log.e("AcceptCallReceiver", "Call CID is null, cannot accept call.")
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Log.d("AcceptCallReceiver", "Accepting call with CID: $cid")
|
|
21
|
+
|
|
22
|
+
// Create an intent to launch the main activity
|
|
23
|
+
val launchIntent = context?.packageManager?.getLaunchIntentForPackage(context.packageName)
|
|
24
|
+
if (launchIntent != null) {
|
|
25
|
+
launchIntent.action = NotificationHandler.ACTION_ACCEPT_CALL
|
|
26
|
+
launchIntent.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, cid)
|
|
27
|
+
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
28
|
+
context.startActivity(launchIntent)
|
|
29
|
+
Log.d("AcceptCallReceiver", "Started MainActivity to handle ACCEPT_CALL action.")
|
|
30
|
+
} else {
|
|
31
|
+
Log.e("AcceptCallReceiver", "Could not get launch intent for package.")
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -14,6 +14,7 @@ import io.getstream.video.android.core.notifications.DefaultNotificationHandler
|
|
|
14
14
|
import io.getstream.video.android.core.notifications.NotificationHandler
|
|
15
15
|
import io.getstream.video.android.model.StreamCallId
|
|
16
16
|
import io.getstream.video.android.model.streamCallId
|
|
17
|
+
import io.getstream.video.android.core.R
|
|
17
18
|
|
|
18
19
|
// declare "incoming_calls_custom" as a constant
|
|
19
20
|
const val INCOMING_CALLS_CUSTOM = "incoming_calls_custom"
|
|
@@ -67,26 +68,16 @@ class CustomNotificationHandler(
|
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
val acceptCallAction = NotificationHandler.ACTION_ACCEPT_CALL
|
|
70
|
-
val acceptCallIntent = Intent(acceptCallAction)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (targetComponent != null) {
|
|
76
|
-
acceptCallIntent.component = targetComponent
|
|
71
|
+
val acceptCallIntent = Intent(acceptCallAction).apply {
|
|
72
|
+
putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId)
|
|
73
|
+
// Explicitly target our manifest-declared receiver
|
|
74
|
+
setClass(application, AcceptCallReceiver::class.java)
|
|
77
75
|
}
|
|
78
|
-
acceptCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
79
76
|
|
|
80
|
-
Log.d("CustomNotificationHandler", "Constructed Accept Call Intent for PI: action=${acceptCallIntent.action}, cid=${acceptCallIntent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)},
|
|
77
|
+
Log.d("CustomNotificationHandler", "Constructed Accept Call Intent for PI: action=${acceptCallIntent.action}, cid=${acceptCallIntent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)}, component=${acceptCallIntent.component}")
|
|
81
78
|
|
|
82
|
-
// Create PendingIntent for Accept action using getActivity to launch the app
|
|
83
79
|
val requestCodeAccept = callId.cid.hashCode() + 1 // Unique request code for the PendingIntent with offset to avoid collisions
|
|
84
|
-
val acceptCallPendingIntent =
|
|
85
|
-
application,
|
|
86
|
-
requestCodeAccept,
|
|
87
|
-
acceptCallIntent,
|
|
88
|
-
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
89
|
-
)
|
|
80
|
+
val acceptCallPendingIntent = createAcceptCallPendingIntent(callId, requestCodeAccept, acceptCallIntent)
|
|
90
81
|
Log.d("CustomNotificationHandler", "Created Accept Call PendingIntent with requestCode: $requestCodeAccept")
|
|
91
82
|
|
|
92
83
|
val rejectCallPendingIntent = intentResolver.searchRejectCallPendingIntent(callId) // Keep using resolver for reject for now, or change it too if needed
|
|
@@ -126,6 +117,37 @@ class CustomNotificationHandler(
|
|
|
126
117
|
null
|
|
127
118
|
}
|
|
128
119
|
}
|
|
120
|
+
|
|
121
|
+
private fun createAcceptCallPendingIntent(
|
|
122
|
+
callId: StreamCallId,
|
|
123
|
+
requestCode: Int,
|
|
124
|
+
acceptCallIntent: Intent
|
|
125
|
+
): PendingIntent? {
|
|
126
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
127
|
+
val launchIntent = application.packageManager.getLaunchIntentForPackage(application.packageName)
|
|
128
|
+
if (launchIntent != null) {
|
|
129
|
+
launchIntent.action = NotificationHandler.ACTION_ACCEPT_CALL
|
|
130
|
+
launchIntent.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId)
|
|
131
|
+
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
132
|
+
PendingIntent.getActivity(
|
|
133
|
+
application,
|
|
134
|
+
requestCode,
|
|
135
|
+
launchIntent,
|
|
136
|
+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
137
|
+
)
|
|
138
|
+
} else {
|
|
139
|
+
Log.e("CustomNotificationHandler", "Could not get launch intent for package to create Accept PI.")
|
|
140
|
+
null
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
PendingIntent.getBroadcast(
|
|
144
|
+
application,
|
|
145
|
+
requestCode,
|
|
146
|
+
acceptCallIntent,
|
|
147
|
+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
129
151
|
|
|
130
152
|
fun customGetIncomingCallNotification(
|
|
131
153
|
fullScreenPendingIntent: PendingIntent,
|
|
@@ -206,6 +228,45 @@ class CustomNotificationHandler(
|
|
|
206
228
|
endCall(callId)
|
|
207
229
|
super.onMissedCall(callId, callDisplayName)
|
|
208
230
|
}
|
|
231
|
+
|
|
232
|
+
override fun getOngoingCallNotification(
|
|
233
|
+
callId: StreamCallId,
|
|
234
|
+
callDisplayName: String?,
|
|
235
|
+
isOutgoingCall: Boolean,
|
|
236
|
+
remoteParticipantCount: Int
|
|
237
|
+
): Notification? {
|
|
238
|
+
Log.d("CustomNotificationHandler", "getOngoingCallNotification called: callId=$callId, isOutgoing=$isOutgoingCall, participants=$remoteParticipantCount")
|
|
239
|
+
createOngoingCallChannel()
|
|
240
|
+
|
|
241
|
+
val launchIntent = application.packageManager.getLaunchIntentForPackage(application.packageName)
|
|
242
|
+
val contentIntent = if (launchIntent != null) {
|
|
243
|
+
launchIntent.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId)
|
|
244
|
+
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
245
|
+
PendingIntent.getActivity(
|
|
246
|
+
application,
|
|
247
|
+
callId.cid.hashCode(),
|
|
248
|
+
launchIntent,
|
|
249
|
+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
250
|
+
)
|
|
251
|
+
} else {
|
|
252
|
+
Log.e("CustomNotificationHandler", "Could not get launch intent for package: ${application.packageName}. Ongoing call notification will not open the app.")
|
|
253
|
+
null
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
return getNotification {
|
|
257
|
+
setContentTitle(callDisplayName ?: "Ongoing Call")
|
|
258
|
+
setContentText("Tap to return to the call")
|
|
259
|
+
setSmallIcon(R.drawable.stream_video_ic_call)
|
|
260
|
+
setChannelId("ongoing_calls")
|
|
261
|
+
setOngoing(true)
|
|
262
|
+
setAutoCancel(false)
|
|
263
|
+
setCategory(NotificationCompat.CATEGORY_CALL)
|
|
264
|
+
setDefaults(0)
|
|
265
|
+
if (contentIntent != null) {
|
|
266
|
+
setContentIntent(contentIntent)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
209
270
|
|
|
210
271
|
private fun customCreateIncomingCallChannel() {
|
|
211
272
|
Log.d("CustomNotificationHandler", "customCreateIncomingCallChannel called")
|
|
@@ -233,6 +294,26 @@ class CustomNotificationHandler(
|
|
|
233
294
|
},
|
|
234
295
|
)
|
|
235
296
|
}
|
|
297
|
+
|
|
298
|
+
private fun createOngoingCallChannel() {
|
|
299
|
+
Log.d("CustomNotificationHandler", "createOngoingCallChannel called")
|
|
300
|
+
maybeCreateChannel(
|
|
301
|
+
channelId = "ongoing_calls",
|
|
302
|
+
context = application,
|
|
303
|
+
configure = {
|
|
304
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
305
|
+
name = "Ongoing calls"
|
|
306
|
+
description = "Notifications for ongoing calls"
|
|
307
|
+
importance = NotificationManager.IMPORTANCE_LOW
|
|
308
|
+
this.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
|
309
|
+
this.setShowBadge(false)
|
|
310
|
+
setSound(null, null)
|
|
311
|
+
enableVibration(false)
|
|
312
|
+
enableLights(false)
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
)
|
|
316
|
+
}
|
|
236
317
|
|
|
237
318
|
public fun clone(): CustomNotificationHandler {
|
|
238
319
|
Log.d("CustomNotificationHandler", "clone called")
|
|
@@ -410,6 +410,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
410
410
|
|
|
411
411
|
CallContent(
|
|
412
412
|
call = activeCall,
|
|
413
|
+
enableInPictureInPicture = false,
|
|
413
414
|
onBackPressed = { /* Handle back press if needed */ },
|
|
414
415
|
controlsContent = { /* Empty to disable native controls */ },
|
|
415
416
|
appBarContent = { /* Empty to disable app bar with stop call button */ },
|
|
@@ -510,6 +511,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
510
511
|
|
|
511
512
|
streamVideoClient = null
|
|
512
513
|
state = State.NOT_INITIALIZED
|
|
514
|
+
eventHandlersRegistered = false
|
|
513
515
|
|
|
514
516
|
val ret = JSObject()
|
|
515
517
|
ret.put("success", true)
|
|
@@ -550,7 +552,10 @@ public class StreamCallPlugin : Plugin() {
|
|
|
550
552
|
android.util.Log.v("StreamCallPlugin", "Plugin's streamVideoClient is null, reusing singleton and registering event handlers")
|
|
551
553
|
streamVideoClient = StreamVideo.instance()
|
|
552
554
|
// Register event handlers since streamVideoClient was null
|
|
553
|
-
|
|
555
|
+
if (!eventHandlersRegistered) {
|
|
556
|
+
registerEventHandlers()
|
|
557
|
+
eventHandlersRegistered = true
|
|
558
|
+
}
|
|
554
559
|
} else {
|
|
555
560
|
android.util.Log.v("StreamCallPlugin", "Plugin already has streamVideoClient, skipping event handler registration")
|
|
556
561
|
}
|
|
@@ -641,8 +646,10 @@ public class StreamCallPlugin : Plugin() {
|
|
|
641
646
|
this.state = State.INITIALIZED
|
|
642
647
|
return
|
|
643
648
|
}
|
|
644
|
-
|
|
645
|
-
|
|
649
|
+
if (!eventHandlersRegistered) {
|
|
650
|
+
registerEventHandlers()
|
|
651
|
+
eventHandlersRegistered = true
|
|
652
|
+
}
|
|
646
653
|
|
|
647
654
|
android.util.Log.v("StreamCallPlugin", "Initialization finished")
|
|
648
655
|
initializationTime = System.currentTimeMillis()
|
|
@@ -900,7 +907,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
900
907
|
if (connectionState != RealtimeConnection.Disconnected) {
|
|
901
908
|
val total = activeCall.state.participantCounts.value?.total
|
|
902
909
|
android.util.Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: Participant left, remaining: $total");
|
|
903
|
-
if (total != null && total
|
|
910
|
+
if (total != null && total < 2) {
|
|
904
911
|
android.util.Log.d("StreamCallPlugin", "CallSessionParticipantLeftEvent: All remote participants have left call ${activeCall.cid}. Ending call.")
|
|
905
912
|
kotlinx.coroutines.GlobalScope.launch(Dispatchers.IO) {
|
|
906
913
|
endCallRaw(activeCall)
|
|
@@ -1766,7 +1773,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
1766
1773
|
|
|
1767
1774
|
// Use call.state.totalParticipants to get participant count (as per StreamVideo Android SDK docs)
|
|
1768
1775
|
val totalParticipants = call.state.totalParticipants.value ?: 0
|
|
1769
|
-
val shouldEndCall = isCreator || totalParticipants <=
|
|
1776
|
+
val shouldEndCall = isCreator || totalParticipants <= 1
|
|
1770
1777
|
|
|
1771
1778
|
android.util.Log.d("StreamCallPlugin", "Call $callId - Creator: $createdBy, CurrentUser: $currentUserId, IsCreator: $isCreator, TotalParticipants: $totalParticipants, ShouldEnd: $shouldEndCall")
|
|
1772
1779
|
|
|
@@ -2261,13 +2268,22 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2261
2268
|
return sharedPrefs.getString(DYNAMIC_API_KEY_PREF, null)
|
|
2262
2269
|
}
|
|
2263
2270
|
|
|
2271
|
+
private fun getDynamicApiKey(context: Context): String? {
|
|
2272
|
+
val sharedPrefs = getApiKeyPreferences(context)
|
|
2273
|
+
return sharedPrefs.getString(DYNAMIC_API_KEY_PREF, null)
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2264
2276
|
private fun getApiKeyPreferences(): SharedPreferences {
|
|
2265
2277
|
return context.getSharedPreferences(API_KEY_PREFS_NAME, Context.MODE_PRIVATE)
|
|
2266
2278
|
}
|
|
2267
2279
|
|
|
2280
|
+
private fun getApiKeyPreferences(context: Context): SharedPreferences {
|
|
2281
|
+
return context.getSharedPreferences(API_KEY_PREFS_NAME, Context.MODE_PRIVATE)
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2268
2284
|
private fun getEffectiveApiKey(context: Context): String {
|
|
2269
2285
|
// A) Check if the key exists in the custom preference
|
|
2270
|
-
val dynamicApiKey = getDynamicApiKey()
|
|
2286
|
+
val dynamicApiKey = getDynamicApiKey(context)
|
|
2271
2287
|
return if (!dynamicApiKey.isNullOrEmpty() && dynamicApiKey.trim().isNotEmpty()) {
|
|
2272
2288
|
android.util.Log.d("StreamCallPlugin", "Using dynamic API key")
|
|
2273
2289
|
dynamicApiKey
|
|
@@ -2366,14 +2382,14 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2366
2382
|
|
|
2367
2383
|
private val acceptCallReceiver = object : BroadcastReceiver() {
|
|
2368
2384
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
2369
|
-
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver: Received broadcast with action: ${intent?.action}")
|
|
2370
2385
|
if (intent?.action == "io.getstream.video.android.action.ACCEPT_CALL") {
|
|
2386
|
+
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver: Received broadcast with action: ${intent.action}")
|
|
2371
2387
|
val cid = intent.streamCallId(NotificationHandler.INTENT_EXTRA_CALL_CID)
|
|
2372
|
-
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver: ACCEPT_CALL broadcast received with cid: $cid")
|
|
2373
2388
|
if (cid != null) {
|
|
2374
|
-
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver:
|
|
2389
|
+
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver: ACCEPT_CALL broadcast received with cid: $cid")
|
|
2375
2390
|
val call = streamVideoClient?.call(id = cid.id, type = cid.type)
|
|
2376
2391
|
if (call != null) {
|
|
2392
|
+
android.util.Log.d("StreamCallPlugin", "BroadcastReceiver: Accepting call with cid: $cid")
|
|
2377
2393
|
kotlinx.coroutines.GlobalScope.launch {
|
|
2378
2394
|
internalAcceptCall(call, requestPermissionsAfter = !checkPermissions())
|
|
2379
2395
|
}
|
|
@@ -2412,6 +2428,7 @@ public class StreamCallPlugin : Plugin() {
|
|
|
2412
2428
|
}
|
|
2413
2429
|
}
|
|
2414
2430
|
private var holder: StreamCallPlugin? = null
|
|
2431
|
+
private var eventHandlersRegistered = false
|
|
2415
2432
|
|
|
2416
2433
|
// Constants for SharedPreferences
|
|
2417
2434
|
private const val API_KEY_PREFS_NAME = "stream_video_api_key_prefs"
|
package/package.json
CHANGED