@capgo/capacitor-stream-call 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -0
- package/android/build.gradle +6 -6
- package/android/src/main/AndroidManifest.xml +29 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CustomNotificationHandler.kt +69 -38
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallBackgroundService.java +41 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallFragment.kt +56 -0
- package/android/src/main/java/ee/forgr/capacitor/streamcall/StreamCallPlugin.kt +506 -169
- package/android/src/main/java/ee/forgr/capacitor/streamcall/TouchInterceptWrapper.kt +8 -8
- package/dist/docs.json +125 -0
- package/dist/esm/definitions.d.ts +39 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +6 -0
- package/dist/esm/web.js +21 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +21 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +21 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/StreamCallPlugin/StreamCallPlugin.swift +77 -160
- package/package.json +1 -1
- package/android/src/main/java/ee/forgr/capacitor/streamcall/CallOverlayView.kt +0 -217
- package/android/src/main/java/ee/forgr/capacitor/streamcall/IncomingCallView.kt +0 -163
package/README.md
CHANGED
|
@@ -128,11 +128,14 @@ The SDK will automatically use the system language and these translations.
|
|
|
128
128
|
* [`setMicrophoneEnabled(...)`](#setmicrophoneenabled)
|
|
129
129
|
* [`setCameraEnabled(...)`](#setcameraenabled)
|
|
130
130
|
* [`addListener('callEvent', ...)`](#addlistenercallevent-)
|
|
131
|
+
* [`addListener('incomingCall', ...)`](#addlistenerincomingcall-)
|
|
131
132
|
* [`removeAllListeners()`](#removealllisteners)
|
|
132
133
|
* [`acceptCall()`](#acceptcall)
|
|
133
134
|
* [`rejectCall()`](#rejectcall)
|
|
134
135
|
* [`isCameraEnabled()`](#iscameraenabled)
|
|
135
136
|
* [`getCallStatus()`](#getcallstatus)
|
|
137
|
+
* [`setSpeaker(...)`](#setspeaker)
|
|
138
|
+
* [`switchCamera(...)`](#switchcamera)
|
|
136
139
|
* [Interfaces](#interfaces)
|
|
137
140
|
* [Type Aliases](#type-aliases)
|
|
138
141
|
* [Enums](#enums)
|
|
@@ -254,6 +257,25 @@ Add listener for call events
|
|
|
254
257
|
--------------------
|
|
255
258
|
|
|
256
259
|
|
|
260
|
+
### addListener('incomingCall', ...)
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
addListener(eventName: 'incomingCall', listenerFunc: (event: IncomingCallPayload) => void) => Promise<{ remove: () => Promise<void>; }>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Listen for lock-screen incoming call (Android only).
|
|
267
|
+
Fired when the app is shown by full-screen intent before user interaction.
|
|
268
|
+
|
|
269
|
+
| Param | Type |
|
|
270
|
+
| ------------------ | --------------------------------------------------------------------------------------- |
|
|
271
|
+
| **`eventName`** | <code>'incomingCall'</code> |
|
|
272
|
+
| **`listenerFunc`** | <code>(event: <a href="#incomingcallpayload">IncomingCallPayload</a>) => void</code> |
|
|
273
|
+
|
|
274
|
+
**Returns:** <code>Promise<{ remove: () => Promise<void>; }></code>
|
|
275
|
+
|
|
276
|
+
--------------------
|
|
277
|
+
|
|
278
|
+
|
|
257
279
|
### removeAllListeners()
|
|
258
280
|
|
|
259
281
|
```typescript
|
|
@@ -317,6 +339,40 @@ Get the current call status
|
|
|
317
339
|
--------------------
|
|
318
340
|
|
|
319
341
|
|
|
342
|
+
### setSpeaker(...)
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
setSpeaker(options: { name: string; }) => Promise<SuccessResponse>
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Set speakerphone on
|
|
349
|
+
|
|
350
|
+
| Param | Type | Description |
|
|
351
|
+
| ------------- | ------------------------------ | ------------------- |
|
|
352
|
+
| **`options`** | <code>{ name: string; }</code> | - Speakerphone name |
|
|
353
|
+
|
|
354
|
+
**Returns:** <code>Promise<<a href="#successresponse">SuccessResponse</a>></code>
|
|
355
|
+
|
|
356
|
+
--------------------
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
### switchCamera(...)
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
switchCamera(options: { camera: 'front' | 'back'; }) => Promise<SuccessResponse>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Switch camera
|
|
366
|
+
|
|
367
|
+
| Param | Type | Description |
|
|
368
|
+
| ------------- | ------------------------------------------- | --------------------- |
|
|
369
|
+
| **`options`** | <code>{ camera: 'front' \| 'back'; }</code> | - Camera to switch to |
|
|
370
|
+
|
|
371
|
+
**Returns:** <code>Promise<<a href="#successresponse">SuccessResponse</a>></code>
|
|
372
|
+
|
|
373
|
+
--------------------
|
|
374
|
+
|
|
375
|
+
|
|
320
376
|
### Interfaces
|
|
321
377
|
|
|
322
378
|
|
|
@@ -550,6 +606,14 @@ The JSON representation for <a href="#listvalue">`ListValue`</a> is JSON array.
|
|
|
550
606
|
| **`sessionId`** | <code>string</code> | the user sesion_id to pin, if not provided, applies to all sessions |
|
|
551
607
|
|
|
552
608
|
|
|
609
|
+
#### IncomingCallPayload
|
|
610
|
+
|
|
611
|
+
| Prop | Type | Description |
|
|
612
|
+
| ---------- | ----------------------- | ---------------------------------------- |
|
|
613
|
+
| **`cid`** | <code>string</code> | Full call CID (e.g. default:123) |
|
|
614
|
+
| **`type`** | <code>'incoming'</code> | Event type (currently always "incoming") |
|
|
615
|
+
|
|
616
|
+
|
|
553
617
|
#### CameraEnabledResponse
|
|
554
618
|
|
|
555
619
|
| Prop | Type |
|
package/android/build.gradle
CHANGED
|
@@ -63,7 +63,7 @@ dependencies {
|
|
|
63
63
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
64
64
|
implementation project(':capacitor-android')
|
|
65
65
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
66
|
-
implementation 'androidx.core:core-ktx:1.
|
|
66
|
+
implementation 'androidx.core:core-ktx:1.16.0'
|
|
67
67
|
|
|
68
68
|
// Compose dependencies with consistent versions
|
|
69
69
|
def compose_version = '1.6.8'
|
|
@@ -72,20 +72,20 @@ dependencies {
|
|
|
72
72
|
implementation "androidx.compose.ui:ui-tooling:$compose_version"
|
|
73
73
|
implementation "androidx.compose.foundation:foundation:$compose_version"
|
|
74
74
|
implementation "androidx.compose.runtime:runtime:$compose_version"
|
|
75
|
-
implementation "androidx.compose.material3:material3:1.3.
|
|
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.
|
|
78
|
+
implementation("io.getstream:stream-video-android-ui-compose:1.6.1")
|
|
79
|
+
implementation("io.getstream:stream-video-android-core:1.6.1")
|
|
80
80
|
implementation("io.getstream:stream-android-push:1.3.1")
|
|
81
81
|
implementation("io.getstream:stream-android-push-firebase:1.3.1")
|
|
82
82
|
|
|
83
83
|
// Firebase dependencies using BOM
|
|
84
|
-
implementation(platform('com.google.firebase:firebase-bom:
|
|
84
|
+
implementation(platform('com.google.firebase:firebase-bom:33.13.0'))
|
|
85
85
|
implementation('com.google.firebase:firebase-messaging-ktx')
|
|
86
86
|
|
|
87
87
|
implementation("androidx.coordinatorlayout:coordinatorlayout:1.3.0")
|
|
88
|
-
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.
|
|
88
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0'
|
|
89
89
|
testImplementation "junit:junit:$junitVersion"
|
|
90
90
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
91
91
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
@@ -1,2 +1,31 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
|
3
|
+
<application>
|
|
4
|
+
|
|
5
|
+
<service
|
|
6
|
+
android:name="io.getstream.android.push.firebase.ChatFirebaseMessagingService"
|
|
7
|
+
android:exported="false">
|
|
8
|
+
<intent-filter>
|
|
9
|
+
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
|
10
|
+
</intent-filter>
|
|
11
|
+
<intent-filter>
|
|
12
|
+
<action android:name="FCM_PLUGIN_ACTIVITY" />
|
|
13
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
14
|
+
</intent-filter>
|
|
15
|
+
</service>
|
|
16
|
+
|
|
17
|
+
<service
|
|
18
|
+
android:name="ee.forgr.capacitor.streamcall.StreamCallBackgroundService"
|
|
19
|
+
android:enabled="true"
|
|
20
|
+
android:exported="false" />
|
|
21
|
+
</application>
|
|
22
|
+
|
|
23
|
+
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
|
24
|
+
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
|
|
25
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
26
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
27
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
28
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
29
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
30
|
+
<uses-permission android:name="android.permission.BLUETOOTH" />
|
|
2
31
|
</manifest>
|
|
@@ -4,13 +4,11 @@ import android.app.Application
|
|
|
4
4
|
import android.app.Notification
|
|
5
5
|
import android.app.NotificationManager
|
|
6
6
|
import android.app.PendingIntent
|
|
7
|
-
import android.content.Context
|
|
8
7
|
import android.content.Intent
|
|
9
8
|
import android.media.RingtoneManager
|
|
10
9
|
import android.os.Build
|
|
11
10
|
import android.util.Log
|
|
12
11
|
import androidx.core.app.NotificationCompat
|
|
13
|
-
import io.getstream.log.taggedLogger
|
|
14
12
|
import io.getstream.video.android.core.RingingState
|
|
15
13
|
import io.getstream.video.android.core.notifications.DefaultNotificationHandler
|
|
16
14
|
import io.getstream.video.android.core.notifications.NotificationHandler
|
|
@@ -36,11 +34,66 @@ class CustomNotificationHandler(
|
|
|
36
34
|
callDisplayName: String?,
|
|
37
35
|
shouldHaveContentIntent: Boolean,
|
|
38
36
|
): Notification? {
|
|
37
|
+
Log.d("CustomNotificationHandler", "getRingingCallNotification called: ringingState=$ringingState, callId=$callId, callDisplayName=$callDisplayName, shouldHaveContentIntent=$shouldHaveContentIntent")
|
|
39
38
|
return if (ringingState is RingingState.Incoming) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
// Note: we create our own fullScreenPendingIntent later based on acceptCallPendingIntent
|
|
40
|
+
|
|
41
|
+
// Get the main launch intent for the application
|
|
42
|
+
val launchIntent = application.packageManager.getLaunchIntentForPackage(application.packageName)
|
|
43
|
+
var targetComponent: android.content.ComponentName? = null
|
|
44
|
+
if (launchIntent != null) {
|
|
45
|
+
targetComponent = launchIntent.component
|
|
46
|
+
Log.d("CustomNotificationHandler", "Derived launch component: ${targetComponent?.flattenToString()}")
|
|
47
|
+
} else {
|
|
48
|
+
Log.e("CustomNotificationHandler", "Could not get launch intent for package: ${application.packageName}. This is problematic for creating explicit intents.")
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Intent to simply bring the app to foreground and show incoming-call UI (no auto accept)
|
|
52
|
+
val incomingIntentAction = "io.getstream.video.android.action.INCOMING_CALL"
|
|
53
|
+
val incomingCallIntent = Intent(incomingIntentAction)
|
|
54
|
+
.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId)
|
|
55
|
+
.setPackage(application.packageName)
|
|
56
|
+
if (targetComponent != null) incomingCallIntent.component = targetComponent
|
|
57
|
+
incomingCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
58
|
+
|
|
59
|
+
// Use the app's MainActivity intent so webview loads; user sees app UI
|
|
60
|
+
val requestCodeFull = callId.cid.hashCode()
|
|
61
|
+
val fullScreenPendingIntent = PendingIntent.getActivity(
|
|
62
|
+
application,
|
|
63
|
+
requestCodeFull,
|
|
64
|
+
incomingCallIntent,
|
|
65
|
+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
val acceptCallAction = NotificationHandler.ACTION_ACCEPT_CALL
|
|
69
|
+
val acceptCallIntent = Intent(acceptCallAction)
|
|
70
|
+
// Pass full Parcelable so both new and old handlers succeed
|
|
71
|
+
.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId)
|
|
72
|
+
.setPackage(application.packageName)
|
|
73
|
+
|
|
74
|
+
if (targetComponent != null) {
|
|
75
|
+
acceptCallIntent.component = targetComponent
|
|
76
|
+
}
|
|
77
|
+
acceptCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
78
|
+
|
|
79
|
+
Log.d("CustomNotificationHandler", "Constructed Accept Call Intent for PI: action=${acceptCallIntent.action}, cid=${acceptCallIntent.getStringExtra(NotificationHandler.INTENT_EXTRA_CALL_CID)}, package=${acceptCallIntent.getPackage()}, component=${acceptCallIntent.component?.flattenToString()}, flags=${acceptCallIntent.flags}")
|
|
80
|
+
|
|
81
|
+
// Create PendingIntent for Accept action using getActivity to launch the app
|
|
82
|
+
val requestCodeAccept = callId.cid.hashCode() + 1 // Unique request code for the PendingIntent with offset to avoid collisions
|
|
83
|
+
val acceptCallPendingIntent = PendingIntent.getActivity(
|
|
84
|
+
application,
|
|
85
|
+
requestCodeAccept,
|
|
86
|
+
acceptCallIntent,
|
|
87
|
+
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
88
|
+
)
|
|
89
|
+
Log.d("CustomNotificationHandler", "Created Accept Call PendingIntent with requestCode: $requestCodeAccept")
|
|
90
|
+
|
|
91
|
+
val rejectCallPendingIntent = intentResolver.searchRejectCallPendingIntent(callId) // Keep using resolver for reject for now, or change it too if needed
|
|
92
|
+
|
|
93
|
+
Log.d("CustomNotificationHandler", "Full Screen PI: $fullScreenPendingIntent")
|
|
94
|
+
Log.d("CustomNotificationHandler", "Custom Accept Call PI: $acceptCallPendingIntent")
|
|
95
|
+
Log.d("CustomNotificationHandler", "Resolver Reject Call PI: $rejectCallPendingIntent")
|
|
96
|
+
|
|
44
97
|
if (fullScreenPendingIntent != null && acceptCallPendingIntent != null && rejectCallPendingIntent != null) {
|
|
45
98
|
customGetIncomingCallNotification(
|
|
46
99
|
fullScreenPendingIntent,
|
|
@@ -81,37 +134,11 @@ class CustomNotificationHandler(
|
|
|
81
134
|
shouldHaveContentIntent: Boolean,
|
|
82
135
|
callId: StreamCallId
|
|
83
136
|
): Notification {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
// serviceIntent.action = CallForegroundService.ACTION_START_FOREGROUND_SERVICE
|
|
90
|
-
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
91
|
-
// application.startForegroundService(serviceIntent)
|
|
92
|
-
// } else {
|
|
93
|
-
// application.startService(serviceIntent)
|
|
94
|
-
// }
|
|
95
|
-
// Adjust PendingIntent for Xiaomi to avoid permission denial
|
|
96
|
-
val xiaomiAcceptIntent = PendingIntent.getActivity(
|
|
97
|
-
application,
|
|
98
|
-
0,
|
|
99
|
-
Intent("io.getstream.video.android.action.ACCEPT_CALL")
|
|
100
|
-
.setPackage(application.packageName)
|
|
101
|
-
.putExtra(NotificationHandler.INTENT_EXTRA_CALL_CID, callId),
|
|
102
|
-
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
103
|
-
)
|
|
104
|
-
return buildNotification(
|
|
105
|
-
fullScreenPendingIntent,
|
|
106
|
-
xiaomiAcceptIntent,
|
|
107
|
-
rejectCallPendingIntent,
|
|
108
|
-
callerName,
|
|
109
|
-
shouldHaveContentIntent,
|
|
110
|
-
INCOMING_CALLS_CUSTOM,
|
|
111
|
-
true // Include sound
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
137
|
+
Log.d("CustomNotificationHandler", "customGetIncomingCallNotification called: callerName=$callerName, callId=$callId")
|
|
138
|
+
customCreateIncomingCallChannel()
|
|
139
|
+
// Always use the provided acceptCallPendingIntent (created with getActivity) so that
|
|
140
|
+
// the app process is started and MainActivity receives the ACCEPT_CALL action even
|
|
141
|
+
// when the app has been killed.
|
|
115
142
|
return buildNotification(
|
|
116
143
|
fullScreenPendingIntent,
|
|
117
144
|
acceptCallPendingIntent,
|
|
@@ -132,10 +159,11 @@ class CustomNotificationHandler(
|
|
|
132
159
|
channelId: String,
|
|
133
160
|
includeSound: Boolean
|
|
134
161
|
): Notification {
|
|
162
|
+
Log.d("CustomNotificationHandler", "buildNotification called: callerName=$callerName, channelId=$channelId, includeSound=$includeSound")
|
|
135
163
|
return getNotification {
|
|
136
164
|
priority = NotificationCompat.PRIORITY_HIGH
|
|
137
165
|
setContentTitle(callerName)
|
|
138
|
-
setContentText("Incoming call")
|
|
166
|
+
setContentText("Incoming call toto")
|
|
139
167
|
setChannelId(channelId)
|
|
140
168
|
setOngoing(true)
|
|
141
169
|
setAutoCancel(false)
|
|
@@ -173,11 +201,13 @@ class CustomNotificationHandler(
|
|
|
173
201
|
}
|
|
174
202
|
|
|
175
203
|
override fun onMissedCall(callId: StreamCallId, callDisplayName: String) {
|
|
204
|
+
Log.d("CustomNotificationHandler", "onMissedCall called: callId=$callId, callDisplayName=$callDisplayName")
|
|
176
205
|
endCall(callId)
|
|
177
206
|
super.onMissedCall(callId, callDisplayName)
|
|
178
207
|
}
|
|
179
208
|
|
|
180
209
|
private fun customCreateIncomingCallChannel() {
|
|
210
|
+
Log.d("CustomNotificationHandler", "customCreateIncomingCallChannel called")
|
|
181
211
|
maybeCreateChannel(
|
|
182
212
|
channelId = INCOMING_CALLS_CUSTOM,
|
|
183
213
|
context = application,
|
|
@@ -204,6 +234,7 @@ class CustomNotificationHandler(
|
|
|
204
234
|
}
|
|
205
235
|
|
|
206
236
|
public fun clone(): CustomNotificationHandler {
|
|
237
|
+
Log.d("CustomNotificationHandler", "clone called")
|
|
207
238
|
return CustomNotificationHandler(this.application, this.endCall, this.incomingCall)
|
|
208
239
|
}
|
|
209
240
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
package ee.forgr.capacitor.streamcall;
|
|
2
|
+
|
|
3
|
+
import android.app.Service;
|
|
4
|
+
import android.content.Intent;
|
|
5
|
+
import android.os.IBinder;
|
|
6
|
+
import android.util.Log;
|
|
7
|
+
import androidx.annotation.Nullable;
|
|
8
|
+
|
|
9
|
+
public class StreamCallBackgroundService extends Service {
|
|
10
|
+
|
|
11
|
+
private static final String TAG = "StreamCallBackgroundService";
|
|
12
|
+
|
|
13
|
+
@Override
|
|
14
|
+
public void onCreate() {
|
|
15
|
+
super.onCreate();
|
|
16
|
+
Log.d(TAG, "Service created");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@Override
|
|
20
|
+
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
21
|
+
Log.d(TAG, "Service started");
|
|
22
|
+
// Keep the service running even if the app is killed
|
|
23
|
+
return START_STICKY;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Override
|
|
27
|
+
public void onDestroy() {
|
|
28
|
+
super.onDestroy();
|
|
29
|
+
Log.d(TAG, "Service destroyed");
|
|
30
|
+
// Restart the service if it's killed by the system
|
|
31
|
+
Intent restartServiceIntent = new Intent(getApplicationContext(), StreamCallBackgroundService.class);
|
|
32
|
+
restartServiceIntent.setPackage(getPackageName());
|
|
33
|
+
startService(restartServiceIntent);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Nullable
|
|
37
|
+
@Override
|
|
38
|
+
public IBinder onBind(Intent intent) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
package ee.forgr.capacitor.streamcall;
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle;
|
|
4
|
+
import android.view.LayoutInflater;
|
|
5
|
+
import android.view.View;
|
|
6
|
+
import android.view.ViewGroup;
|
|
7
|
+
import androidx.fragment.app.Fragment;
|
|
8
|
+
import io.getstream.video.android.core.Call;
|
|
9
|
+
import kotlinx.coroutines.CoroutineScope;
|
|
10
|
+
import kotlinx.coroutines.Dispatchers;
|
|
11
|
+
import kotlinx.coroutines.flow.launchIn;
|
|
12
|
+
import kotlinx.coroutines.flow.onEach;
|
|
13
|
+
import kotlinx.coroutines.launch;
|
|
14
|
+
import io.getstream.webrtc.android.ui.VideoTextureViewRenderer;
|
|
15
|
+
import stream.video.sfu.models.TrackType;
|
|
16
|
+
|
|
17
|
+
class StreamCallFragment : Fragment() {
|
|
18
|
+
private var call: Call? = null
|
|
19
|
+
private var videoRenderer: VideoTextureViewRenderer? = null
|
|
20
|
+
|
|
21
|
+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
|
22
|
+
videoRenderer = VideoTextureViewRenderer(requireContext())
|
|
23
|
+
return videoRenderer!!
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fun setCall(call: Call) {
|
|
27
|
+
this.call = call
|
|
28
|
+
videoRenderer?.let { renderer ->
|
|
29
|
+
// Setup video rendering for local video
|
|
30
|
+
call.initRenderer(renderer, call.sessionId, TrackType.TRACK_TYPE_VIDEO)
|
|
31
|
+
// Setup listener for remote participants' video
|
|
32
|
+
val scope = CoroutineScope(Dispatchers.Main)
|
|
33
|
+
scope.launch {
|
|
34
|
+
call.state.participants.onEach { participantStates ->
|
|
35
|
+
participantStates.forEach { participantState ->
|
|
36
|
+
participantState.videoTrack.onEach { videoTrack ->
|
|
37
|
+
videoTrack?.let { track ->
|
|
38
|
+
// Additional renderers might be needed for multiple participants
|
|
39
|
+
track.video.addSink(renderer)
|
|
40
|
+
}
|
|
41
|
+
}.launchIn(scope)
|
|
42
|
+
}
|
|
43
|
+
}.launchIn(scope)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fun getCall(): Call? {
|
|
49
|
+
return call
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override fun onDestroyView() {
|
|
53
|
+
super.onDestroyView()
|
|
54
|
+
videoRenderer = null
|
|
55
|
+
}
|
|
56
|
+
}
|