@fishjam-cloud/react-native-client 0.2.0 → 0.2.2

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.
Files changed (28) hide show
  1. package/README.md +21 -8
  2. package/android/build.gradle +5 -3
  3. package/android/src/main/AndroidManifest.xml +2 -0
  4. package/android/src/main/java/io/fishjam/reactnative/AudioDeviceKind.kt +34 -0
  5. package/android/src/main/java/io/fishjam/reactnative/AudioSwitchManager.kt +61 -0
  6. package/android/src/main/java/io/fishjam/reactnative/EmitableEvents.kt +14 -0
  7. package/android/src/main/java/io/fishjam/reactnative/Errors.kt +31 -0
  8. package/android/src/main/java/io/fishjam/reactnative/FishjamForegroundService.kt +90 -0
  9. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClient.kt +870 -0
  10. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClientModule.kt +370 -0
  11. package/android/src/main/java/io/fishjam/reactnative/Utils.kt +11 -0
  12. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewView.kt +40 -0
  13. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewViewModule.kt +17 -0
  14. package/android/src/main/java/io/fishjam/reactnative/VideoRendererView.kt +58 -0
  15. package/android/src/main/java/io/fishjam/reactnative/VideoRendererViewModule.kt +25 -0
  16. package/android/src/main/java/io/fishjam/reactnative/VideoView.kt +77 -0
  17. package/expo-module.config.json +17 -0
  18. package/ios/RNFishjamClient.podspec +30 -0
  19. package/ios/RNFishjamClientModule.swift +2 -2
  20. package/package.json +10 -7
  21. package/plugin/build/types.d.ts +9 -0
  22. package/plugin/build/types.js +2 -0
  23. package/plugin/build/withFishjam.d.ts +4 -0
  24. package/plugin/build/withFishjam.js +13 -0
  25. package/plugin/build/withFishjamAndroid.d.ts +3 -0
  26. package/plugin/build/withFishjamAndroid.js +32 -0
  27. package/plugin/build/withFishjamIos.d.ts +9 -0
  28. package/plugin/build/withFishjamIos.js +222 -0
package/README.md CHANGED
@@ -48,7 +48,7 @@ Add plugin to your `app.json` if it's not already added:
48
48
  }
49
49
  ```
50
50
 
51
- If you want to use screensharing feature, enable the following flag:
51
+ If you want to use screensharing feature, enable the following flags:
52
52
 
53
53
  ```json
54
54
  {
@@ -59,7 +59,12 @@ If you want to use screensharing feature, enable the following flag:
59
59
  [
60
60
  "@fishjam-cloud/react-native-client",
61
61
  {
62
- "setUpScreensharing": true,
62
+ "android": {
63
+ "enableForegroundService": true
64
+ },
65
+ "ios": {
66
+ "enableScreensharing": true
67
+ }
63
68
  }
64
69
  ]
65
70
  ]
@@ -72,11 +77,14 @@ On bare workflow run `expo prebuild` to configure the app, then run
72
77
 
73
78
  ### Android
74
79
 
75
- 1. Add camera and microphone permissions to your `AndroidManifest.xml`.
80
+ 1. Add camera and microphone permissions to your `AndroidManifest.xml` or you app.json permissions.
76
81
  2. Rebuild the app. That's it!
77
82
 
78
83
  ### iOS
79
84
 
85
+ > [!NOTE]
86
+ > If you're using our plugin with `enableScreensharing` set to `true`. Not further steps are required.
87
+
80
88
  On iOS installation is a bit more complicated, because you need to setup a
81
89
  screen broadcast app extension for screensharing.
82
90
 
@@ -102,7 +110,7 @@ screen broadcast app extension for screensharing.
102
110
  Broadcast Upload Extension → Next. Choose the name for the new target, select
103
111
  Swift language and deselect "Include UI Extension".
104
112
 
105
- ![New target config](../../.github/images/xcode1.png)
113
+ ![New target config](https://github.com/fishjam-cloud/mobile-client-sdk/blob/main/.github/images/xcode1.png)
106
114
 
107
115
  Press Finish. In the next alert xcode will ask you if you want to activate
108
116
  the new scheme - press Cancel.
@@ -110,7 +118,7 @@ screen broadcast app extension for screensharing.
110
118
  4. Configure app group. Go to "Signing & Capabilities" tab, click "+ Capability"
111
119
  button in upper left corner and select "App Groups".
112
120
 
113
- ![App groups config](../../.github/images/xcode2.png)
121
+ ![App groups config](https://github.com/fishjam-cloud/mobile-client-sdk/blob/main/.github/images/xcode2.png)
114
122
 
115
123
  Then in the "App Groups" add a new group or select existing. Usually group
116
124
  name has format `group.<your-bundle-identifier>`. Verify that both app and
@@ -119,7 +127,7 @@ screen broadcast app extension for screensharing.
119
127
  5. A new folder with app extension should appear on the left with contents like
120
128
  this:
121
129
 
122
- ![App extension files](../../.github/images/xcode3.png)
130
+ ![App extension files](https://github.com/fishjam-cloud/mobile-client-sdk/blob/main/.github/images/xcode3.png)
123
131
 
124
132
  Replace `SampleHandler.swift` with `MembraneBroadcastSampleHandler.swift` and
125
133
  this code:
@@ -221,10 +229,12 @@ room client. To run the app:
221
229
  instructions there to setup and run demo server.
222
230
  2. Clone the repo
223
231
  3. ```
224
- cd `examples/fishjam-chat`
225
232
  yarn
233
+ yarn build
234
+ cd `examples/fishjam-chat`
235
+ npx expo prebuild --clean
226
236
  ```
227
- 4. In App.ts replace server url with your server's url.
237
+ 4. Copy `.env.template` and create `.env` with proper secrets.
228
238
  5. `yarn run android` or `yarn run ios` or run project from Android Studio /
229
239
  Xcode just like every RN project. Note that simulators won't work, you have
230
240
  to test on real device for camera and screensharing to run.
@@ -323,6 +333,9 @@ Use track metadata to differentiate between video and screencast tracks.
323
333
 
324
334
  ### Android foreground service
325
335
 
336
+ > [!NOTE]
337
+ > If you're using our plugin with `enableForegroundService` set to `true`. Not further steps are required.
338
+
326
339
  In order for the call to continue running when app is in background, you need to
327
340
  set up and start a foreground service. You can use a 3rd party library for this,
328
341
  for example [notifee](https://notifee.app/).
@@ -2,8 +2,10 @@ apply plugin: 'com.android.library'
2
2
  apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
+ def packageVersion = '0.2.2'
6
+
5
7
  group = 'io.fishjam.reactnative'
6
- version = '0.2.0'
8
+ version = "${packageVersion}"
7
9
 
8
10
  buildscript {
9
11
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -59,7 +61,7 @@ android {
59
61
  minSdkVersion safeExtGet("minSdkVersion", 21)
60
62
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
61
63
  versionCode 1
62
- versionName "0.2.0"
64
+ versionName "${packageVersion}"
63
65
  }
64
66
  lintOptions {
65
67
  abortOnError false
@@ -82,7 +84,7 @@ dependencies {
82
84
  implementation project(':fishjam-cloud-android-client')
83
85
  } else {
84
86
  // update version number when releasing
85
- implementation 'com.github.fishjam-cloud:mobile-client-sdk:0.2.0'
87
+ implementation "com.github.fishjam-cloud:mobile-client-sdk:${packageVersion}"
86
88
  }
87
89
  api 'com.github.davidliu:audioswitch:8edf84ee46cdbc84c2b7725aa8f8d66363e19876'
88
90
  implementation 'androidx.appcompat:appcompat:1.6.1'
@@ -0,0 +1,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -0,0 +1,34 @@
1
+ package io.fishjam.reactnative
2
+
3
+ import com.twilio.audioswitch.AudioDevice
4
+
5
+ enum class AudioDeviceKind(
6
+ val typeName: String,
7
+ val audioDeviceClass: Class<out AudioDevice>
8
+ ) {
9
+ BLUETOOTH("bluetooth", AudioDevice.BluetoothHeadset::class.java),
10
+ WIRED_HEADSET("headset", AudioDevice.WiredHeadset::class.java),
11
+ SPEAKER("speaker", AudioDevice.Speakerphone::class.java),
12
+ EARPIECE("earpiece", AudioDevice.Earpiece::class.java)
13
+ ;
14
+
15
+ companion object {
16
+ fun fromAudioDevice(audioDevice: AudioDevice): AudioDeviceKind? {
17
+ for (kind in values()) {
18
+ if (kind.audioDeviceClass == audioDevice.javaClass) {
19
+ return kind
20
+ }
21
+ }
22
+ return null
23
+ }
24
+
25
+ fun fromTypeName(typeName: String): AudioDeviceKind? {
26
+ for (kind in values()) {
27
+ if (kind.typeName == typeName) {
28
+ return kind
29
+ }
30
+ }
31
+ return null
32
+ }
33
+ }
34
+ }
@@ -0,0 +1,61 @@
1
+ package io.fishjam.reactnative
2
+
3
+ import android.content.Context
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import com.twilio.audioswitch.AudioDevice
7
+ import com.twilio.audioswitch.AudioDeviceChangeListener
8
+ import com.twilio.audioswitch.AudioSwitch
9
+
10
+ class AudioSwitchManager(
11
+ private val context: Context
12
+ ) {
13
+ private var preferredDeviceList =
14
+ listOf(
15
+ AudioDevice.BluetoothHeadset::class.java,
16
+ AudioDevice.WiredHeadset::class.java,
17
+ AudioDevice.Speakerphone::class.java,
18
+ AudioDevice.Earpiece::class.java
19
+ )
20
+
21
+ // AudioSwitch is not threadsafe, so all calls should be done on the main thread.
22
+ private val handler: Handler = Handler(Looper.getMainLooper())
23
+
24
+ private var audioSwitch: AudioSwitch =
25
+ AudioSwitch(context, loggingEnabled = true, preferredDeviceList = preferredDeviceList)
26
+
27
+ fun start(listener: AudioDeviceChangeListener) {
28
+ handler.removeCallbacksAndMessages(null)
29
+ handler.postAtFrontOfQueue {
30
+ audioSwitch.start(listener)
31
+ audioSwitch.activate()
32
+ }
33
+ }
34
+
35
+ fun stop() {
36
+ handler.removeCallbacksAndMessages(null)
37
+ handler.postAtFrontOfQueue {
38
+ audioSwitch.stop()
39
+ }
40
+ }
41
+
42
+ fun selectedAudioDevice(): AudioDevice? = audioSwitch.selectedAudioDevice
43
+
44
+ fun availableAudioDevices(): List<AudioDevice> = audioSwitch.availableAudioDevices
45
+
46
+ fun selectAudioOutput(audioDeviceClass: Class<out AudioDevice?>) {
47
+ handler.post {
48
+ availableAudioDevices()
49
+ .find { it.javaClass == audioDeviceClass }
50
+ ?.let {
51
+ audioSwitch.selectDevice(it)
52
+ }
53
+ }
54
+ }
55
+
56
+ fun selectAudioOutput(kind: AudioDeviceKind?) {
57
+ if (kind != null) {
58
+ selectAudioOutput(kind.audioDeviceClass)
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,14 @@
1
+ package io.fishjam.reactnative
2
+
3
+ object EmitableEvents {
4
+ const val IsCameraOn = "IsCameraOn"
5
+ const val IsMicrophoneOn = "IsMicrophoneOn"
6
+ const val IsScreencastOn = "IsScreencastOn"
7
+ const val SimulcastConfigUpdate = "SimulcastConfigUpdate"
8
+ const val PeersUpdate = "PeersUpdate"
9
+ const val AudioDeviceUpdate = "AudioDeviceUpdate"
10
+ const val BandwidthEstimation = "BandwidthEstimation"
11
+ const val ReconnectionRetriesLimitReached = "ReconnectionRetriesLimitReached"
12
+ const val ReconnectionStarted = "ReconnectionStarted"
13
+ const val Reconnected = "Reconnected"
14
+ }
@@ -0,0 +1,31 @@
1
+ package io.fishjam.reactnative
2
+
3
+ import com.fishjamcloud.client.models.AuthError
4
+ import expo.modules.kotlin.exception.CodedException
5
+
6
+ class JoinError(
7
+ metadata: Any
8
+ ) : CodedException(message = "Join error: $metadata")
9
+
10
+ class ConnectionError(
11
+ reason: AuthError
12
+ ) : CodedException(message = "Connection error: ${reason.error}")
13
+
14
+ class MissingScreencastPermission : CodedException(message = "No permission to start screencast, call handleScreencastPermission first.")
15
+
16
+ class ClientNotConnectedError : CodedException(message = "Client not connected to server yet. Make sure to call connect() first!")
17
+
18
+ class NoLocalVideoTrackError : CodedException(message = "No local video track. Make sure to call connect() first!")
19
+
20
+ class NoLocalAudioTrackError : CodedException(message = "No local audio track. Make sure to call connect() first!")
21
+
22
+ class NoScreencastTrackError : CodedException(message = "No local screencast track. Make sure to toggle screencast on first!")
23
+
24
+ class SocketClosedError(
25
+ code: Int,
26
+ reason: String
27
+ ) : CodedException(message = "Socket was closed with code = $code and reason = $reason")
28
+
29
+ class SocketError(
30
+ message: String
31
+ ) : CodedException(message = "Socket error: $message")
@@ -0,0 +1,90 @@
1
+ package io.fishjam.reactnative
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.PendingIntent
7
+ import android.app.Service
8
+ import android.content.Intent
9
+ import android.os.Build
10
+ import android.os.IBinder
11
+ import androidx.core.app.NotificationCompat
12
+
13
+ class FishjamForegroundService : Service() {
14
+ companion object {
15
+ private const val FOREGROUND_SERVICE_ID = 1668
16
+ }
17
+
18
+ override fun onBind(p0: Intent?): IBinder? = null
19
+
20
+ override fun onStartCommand(
21
+ intent: Intent?,
22
+ flags: Int,
23
+ startId: Int
24
+ ): Int {
25
+ val channelId = intent!!.getStringExtra("channelId")!!
26
+ val channelName = intent.getStringExtra("channelName")!!
27
+ val notificationTitle = intent.getStringExtra("notificationTitle")!!
28
+ val notificationContent = intent.getStringExtra("notificationContent")!!
29
+ val foregroundServiceTypesArray = intent.getIntArrayExtra("foregroundServiceTypes")!!
30
+ // Create "bitwise or" of foregroundServiceTypesArray
31
+ val foregroundServiceType = foregroundServiceTypesArray.reduce { acc, value -> acc or value }
32
+
33
+ val pendingIntent =
34
+ PendingIntent.getActivity(
35
+ this,
36
+ 0,
37
+ intent,
38
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
39
+ )
40
+
41
+ val notification: Notification =
42
+ NotificationCompat
43
+ .Builder(this, channelId)
44
+ .setContentTitle(notificationTitle)
45
+ .setContentText(notificationContent)
46
+ .setContentIntent(pendingIntent)
47
+ .build()
48
+
49
+ createNotificationChannel(channelId, channelName)
50
+ startForegroundWithNotification(notification, foregroundServiceType)
51
+
52
+ return START_NOT_STICKY
53
+ }
54
+
55
+ private fun startForegroundWithNotification(
56
+ notification: Notification,
57
+ foregroundServiceType: Int
58
+ ) {
59
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
60
+ startForeground(
61
+ FOREGROUND_SERVICE_ID,
62
+ notification,
63
+ foregroundServiceType
64
+ )
65
+ } else {
66
+ startForeground(
67
+ FOREGROUND_SERVICE_ID,
68
+ notification
69
+ )
70
+ }
71
+ }
72
+
73
+ private fun createNotificationChannel(
74
+ channelId: String,
75
+ channelName: String
76
+ ) {
77
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
78
+ return
79
+ }
80
+
81
+ val serviceChannel =
82
+ NotificationChannel(
83
+ channelId,
84
+ channelName,
85
+ NotificationManager.IMPORTANCE_DEFAULT
86
+ )
87
+ val notificationManager = getSystemService(NOTIFICATION_SERVICE) as? NotificationManager
88
+ notificationManager?.createNotificationChannel(serviceChannel)
89
+ }
90
+ }