@fishjam-cloud/react-native-client 0.2.1 → 0.2.3

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 (105) hide show
  1. package/android/build.gradle +1 -1
  2. package/android/src/main/AndroidManifest.xml +2 -0
  3. package/android/src/main/java/io/fishjam/reactnative/AudioDeviceKind.kt +34 -0
  4. package/android/src/main/java/io/fishjam/reactnative/AudioSwitchManager.kt +61 -0
  5. package/android/src/main/java/io/fishjam/reactnative/EmitableEvents.kt +15 -0
  6. package/android/src/main/java/io/fishjam/reactnative/Errors.kt +31 -0
  7. package/android/src/main/java/io/fishjam/reactnative/FishjamForegroundService.kt +90 -0
  8. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClient.kt +883 -0
  9. package/android/src/main/java/io/fishjam/reactnative/RNFishjamClientModule.kt +371 -0
  10. package/android/src/main/java/io/fishjam/reactnative/Utils.kt +11 -0
  11. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewView.kt +40 -0
  12. package/android/src/main/java/io/fishjam/reactnative/VideoPreviewViewModule.kt +17 -0
  13. package/android/src/main/java/io/fishjam/reactnative/VideoRendererView.kt +58 -0
  14. package/android/src/main/java/io/fishjam/reactnative/VideoRendererViewModule.kt +25 -0
  15. package/android/src/main/java/io/fishjam/reactnative/VideoView.kt +77 -0
  16. package/build/RNFishjamClientModule.d.ts +2 -0
  17. package/build/RNFishjamClientModule.d.ts.map +1 -1
  18. package/build/RNFishjamClientModule.js +2 -1
  19. package/build/RNFishjamClientModule.js.map +1 -1
  20. package/build/common/client.d.ts +4 -1
  21. package/build/common/client.d.ts.map +1 -1
  22. package/build/common/client.js +4 -1
  23. package/build/common/client.js.map +1 -1
  24. package/build/common/metadata.d.ts +1 -0
  25. package/build/common/metadata.d.ts.map +1 -1
  26. package/build/common/metadata.js +1 -0
  27. package/build/common/metadata.js.map +1 -1
  28. package/build/common/webRTC.d.ts +2 -0
  29. package/build/common/webRTC.d.ts.map +1 -1
  30. package/build/common/webRTC.js +2 -0
  31. package/build/common/webRTC.js.map +1 -1
  32. package/build/components/VideoPreviewView.d.ts +6 -3
  33. package/build/components/VideoPreviewView.d.ts.map +1 -1
  34. package/build/components/VideoPreviewView.js +5 -0
  35. package/build/components/VideoPreviewView.js.map +1 -1
  36. package/build/components/VideoRendererView.d.ts +5 -3
  37. package/build/components/VideoRendererView.d.ts.map +1 -1
  38. package/build/components/VideoRendererView.js +4 -0
  39. package/build/components/VideoRendererView.js.map +1 -1
  40. package/build/hooks/useAudioSettings.d.ts +15 -0
  41. package/build/hooks/useAudioSettings.d.ts.map +1 -1
  42. package/build/hooks/useAudioSettings.js +15 -13
  43. package/build/hooks/useAudioSettings.js.map +1 -1
  44. package/build/hooks/useBandwidthEstimation.d.ts +2 -0
  45. package/build/hooks/useBandwidthEstimation.d.ts.map +1 -1
  46. package/build/hooks/useBandwidthEstimation.js +2 -0
  47. package/build/hooks/useBandwidthEstimation.js.map +1 -1
  48. package/build/hooks/useCamera.d.ts +45 -1
  49. package/build/hooks/useCamera.d.ts.map +1 -1
  50. package/build/hooks/useCamera.js +45 -37
  51. package/build/hooks/useCamera.js.map +1 -1
  52. package/build/hooks/useFishjamEvent.d.ts +1 -0
  53. package/build/hooks/useFishjamEvent.d.ts.map +1 -1
  54. package/build/hooks/useFishjamEvent.js +3 -4
  55. package/build/hooks/useFishjamEvent.js.map +1 -1
  56. package/build/hooks/useMicrophone.d.ts +4 -0
  57. package/build/hooks/useMicrophone.d.ts.map +1 -1
  58. package/build/hooks/useMicrophone.js +8 -4
  59. package/build/hooks/useMicrophone.js.map +1 -1
  60. package/build/hooks/useParticipants.d.ts +2 -0
  61. package/build/hooks/useParticipants.d.ts.map +1 -1
  62. package/build/hooks/useParticipants.js +2 -0
  63. package/build/hooks/useParticipants.js.map +1 -1
  64. package/build/hooks/useReconnection.d.ts +6 -0
  65. package/build/hooks/useReconnection.d.ts.map +1 -1
  66. package/build/hooks/useReconnection.js +6 -0
  67. package/build/hooks/useReconnection.js.map +1 -1
  68. package/build/hooks/useScreencast.d.ts +22 -3
  69. package/build/hooks/useScreencast.d.ts.map +1 -1
  70. package/build/hooks/useScreencast.js +30 -22
  71. package/build/hooks/useScreencast.js.map +1 -1
  72. package/build/index.d.ts +1 -1
  73. package/build/index.d.ts.map +1 -1
  74. package/build/index.js +2 -0
  75. package/build/index.js.map +1 -1
  76. package/build/stats/useRTCStatistics.d.ts +1 -0
  77. package/build/stats/useRTCStatistics.d.ts.map +1 -1
  78. package/build/stats/useRTCStatistics.js +1 -0
  79. package/build/stats/useRTCStatistics.js.map +1 -1
  80. package/build/types.d.ts +6 -0
  81. package/build/types.d.ts.map +1 -1
  82. package/build/types.js.map +1 -1
  83. package/build/utils/errorListener.d.ts +2 -0
  84. package/build/utils/errorListener.d.ts.map +1 -0
  85. package/build/utils/errorListener.js +16 -0
  86. package/build/utils/errorListener.js.map +1 -0
  87. package/build/utils/foregroundService.d.ts +2 -0
  88. package/build/utils/foregroundService.d.ts.map +1 -1
  89. package/build/utils/foregroundService.js +2 -0
  90. package/build/utils/foregroundService.js.map +1 -1
  91. package/ios/Events.swift +1 -0
  92. package/ios/RNFishjamClient.podspec +1 -1
  93. package/ios/RNFishjamClient.swift +14 -0
  94. package/package.json +7 -6
  95. package/plugin/broadcastExtensionFiles/FishjamBroadcastSampleHandler.swift +50 -0
  96. package/plugin/broadcastExtensionFiles/FishjamScreenBroadcastExtension.entitlements +10 -0
  97. package/plugin/broadcastExtensionFiles/Info.plist +27 -0
  98. package/plugin/build/types.d.ts +9 -0
  99. package/plugin/build/types.js +2 -0
  100. package/plugin/build/withFishjam.d.ts +4 -0
  101. package/plugin/build/withFishjam.js +13 -0
  102. package/plugin/build/withFishjamAndroid.d.ts +3 -0
  103. package/plugin/build/withFishjamAndroid.js +32 -0
  104. package/plugin/build/withFishjamIos.d.ts +9 -0
  105. package/plugin/build/withFishjamIos.js +222 -0
@@ -0,0 +1,16 @@
1
+ import { ReceivableEvents } from '../hooks/useFishjamEvent';
2
+ import { nativeModuleEventEmitter } from '../RNFishjamClientModule';
3
+ export const initializeWarningListener = () => {
4
+ if (!__DEV__) {
5
+ return;
6
+ }
7
+ try {
8
+ nativeModuleEventEmitter.addListener(ReceivableEvents.Warning, ({ message }) => {
9
+ console.warn(message);
10
+ });
11
+ }
12
+ catch (error) {
13
+ console.error(`Failed to start warning listener: ${error?.message ?? ''}`);
14
+ }
15
+ };
16
+ //# sourceMappingURL=errorListener.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorListener.js","sourceRoot":"","sources":["../../src/utils/errorListener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,EAAE;IAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,IAAI,CAAC;QACH,wBAAwB,CAAC,WAAW,CAClC,gBAAgB,CAAC,OAAO,EACxB,CAAC,EAAE,OAAO,EAAuB,EAAE,EAAE;YACnC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,KAAK,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC,CAAC","sourcesContent":["import { ReceivableEvents } from '../hooks/useFishjamEvent';\nimport { nativeModuleEventEmitter } from '../RNFishjamClientModule';\n\nexport const initializeWarningListener = () => {\n if (!__DEV__) {\n return;\n }\n try {\n nativeModuleEventEmitter.addListener(\n ReceivableEvents.Warning,\n ({ message }: { message: string }) => {\n console.warn(message);\n },\n );\n } catch (error) {\n console.error(`Failed to start warning listener: ${error?.message ?? ''}`);\n }\n};\n"]}
@@ -18,6 +18,7 @@ import { ForegroundServiceOptions } from '../types';
18
18
  ```
19
19
  <service android:name="io.fishjam.reactnative.FishjamForegroundService" android:foregroundServiceType="camera|microphone|mediaProjection"/>
20
20
  ```
21
+ * @category Screenshare
21
22
  */
22
23
  export declare const startForegroundService: (options: ForegroundServiceOptions) => void;
23
24
  /**
@@ -25,6 +26,7 @@ export declare const startForegroundService: (options: ForegroundServiceOptions)
25
26
  * @see {@link startForegroundService} for further information.
26
27
  *
27
28
  * Does nothing on other platforms.
29
+ * @category Screenshare
28
30
  */
29
31
  export declare const stopForegroundService: () => void;
30
32
  //# sourceMappingURL=foregroundService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"foregroundService.d.ts","sourceRoot":"","sources":["../../src/utils/foregroundService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;MAmBM;AACN,eAAO,MAAM,sBAAsB,YAAa,wBAAwB,SAKvE,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,YAKjC,CAAC"}
1
+ {"version":3,"file":"foregroundService.d.ts","sourceRoot":"","sources":["../../src/utils/foregroundService.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;MAoBM;AACN,eAAO,MAAM,sBAAsB,YAAa,wBAAwB,SAKvE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,YAKjC,CAAC"}
@@ -19,6 +19,7 @@ import RNFishjamClientModule from '../RNFishjamClientModule';
19
19
  ```
20
20
  <service android:name="io.fishjam.reactnative.FishjamForegroundService" android:foregroundServiceType="camera|microphone|mediaProjection"/>
21
21
  ```
22
+ * @category Screenshare
22
23
  */
23
24
  export const startForegroundService = (options) => {
24
25
  if (Platform.OS !== 'android') {
@@ -31,6 +32,7 @@ export const startForegroundService = (options) => {
31
32
  * @see {@link startForegroundService} for further information.
32
33
  *
33
34
  * Does nothing on other platforms.
35
+ * @category Screenshare
34
36
  */
35
37
  export const stopForegroundService = () => {
36
38
  if (Platform.OS !== 'android') {
@@ -1 +1 @@
1
- {"version":3,"file":"foregroundService.js","sourceRoot":"","sources":["../../src/utils/foregroundService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;MAmBM;AACN,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAiC,EAAE,EAAE;IAC1E,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,qBAAqB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,EAAE;IACxC,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,qBAAqB,CAAC,qBAAqB,EAAE,CAAC;AAChD,CAAC,CAAC","sourcesContent":["import { Platform } from 'react-native';\nimport RNFishjamClientModule from '../RNFishjamClientModule';\nimport { ForegroundServiceOptions } from '../types';\n\n/**\n * Launches a foreground service on Android.\n * Does nothing on other platforms.\n * \n * @remarks\n * You must have have the following permissions enabled before calling this function:\n * @example\n ```\n \"android.permission.FOREGROUND_SERVICE\"\n \"android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION\"\n \"android.permission.FOREGROUND_SERVICE_CAMERA\"\n \"android.permission.FOREGROUND_SERVICE_MICROPHONE\"\n \"android.permission.POST_NOTIFICATIONS\"\n *```\n * as well as FishjamForegroundService in your AndroidManifest:\n * @example\n ```\n <service android:name=\"io.fishjam.reactnative.FishjamForegroundService\" android:foregroundServiceType=\"camera|microphone|mediaProjection\"/>\n ```\n */\nexport const startForegroundService = (options: ForegroundServiceOptions) => {\n if (Platform.OS !== 'android') {\n return;\n }\n RNFishjamClientModule.startForegroundService(options);\n};\n\n/**\n * Stops previously launched Android foreground service.\n * @see {@link startForegroundService} for further information.\n *\n * Does nothing on other platforms.\n */\nexport const stopForegroundService = () => {\n if (Platform.OS !== 'android') {\n return;\n }\n RNFishjamClientModule.stopForegroundService();\n};\n"]}
1
+ {"version":3,"file":"foregroundService.js","sourceRoot":"","sources":["../../src/utils/foregroundService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAG7D;;;;;;;;;;;;;;;;;;;;MAoBM;AACN,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,OAAiC,EAAE,EAAE;IAC1E,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,qBAAqB,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,EAAE;IACxC,IAAI,QAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,qBAAqB,CAAC,qBAAqB,EAAE,CAAC;AAChD,CAAC,CAAC","sourcesContent":["import { Platform } from 'react-native';\nimport RNFishjamClientModule from '../RNFishjamClientModule';\nimport { ForegroundServiceOptions } from '../types';\n\n/**\n * Launches a foreground service on Android.\n * Does nothing on other platforms.\n * \n * @remarks\n * You must have have the following permissions enabled before calling this function:\n * @example\n ```\n \"android.permission.FOREGROUND_SERVICE\"\n \"android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION\"\n \"android.permission.FOREGROUND_SERVICE_CAMERA\"\n \"android.permission.FOREGROUND_SERVICE_MICROPHONE\"\n \"android.permission.POST_NOTIFICATIONS\"\n *```\n * as well as FishjamForegroundService in your AndroidManifest:\n * @example\n ```\n <service android:name=\"io.fishjam.reactnative.FishjamForegroundService\" android:foregroundServiceType=\"camera|microphone|mediaProjection\"/>\n ```\n * @category Screenshare\n */\nexport const startForegroundService = (options: ForegroundServiceOptions) => {\n if (Platform.OS !== 'android') {\n return;\n }\n RNFishjamClientModule.startForegroundService(options);\n};\n\n/**\n * Stops previously launched Android foreground service.\n * @see {@link startForegroundService} for further information.\n *\n * Does nothing on other platforms.\n * @category Screenshare\n */\nexport const stopForegroundService = () => {\n if (Platform.OS !== 'android') {\n return;\n }\n RNFishjamClientModule.stopForegroundService();\n};\n"]}
package/ios/Events.swift CHANGED
@@ -10,4 +10,5 @@ struct EmitableEvents {
10
10
  static let ReconnectionRetriesLimitReached = "ReconnectionRetriesLimitReached"
11
11
  static let ReconnectionStarted = "ReconnectionStarted"
12
12
  static let Reconnected = "Reconnected"
13
+ static let Warning = "Warning"
13
14
  }
@@ -25,6 +25,6 @@ Pod::Spec.new do |s|
25
25
 
26
26
  s.source_files = '**/*.{h,m,swift}'
27
27
 
28
- s.dependency 'FishjamCloudClient', '>= 0.1'
28
+ s.dependency 'FishjamCloudClient', '0.2.3'
29
29
 
30
30
  end
@@ -13,6 +13,8 @@ class RNFishjamClient: FishjamClientListener {
13
13
  var isScreencastOn = false
14
14
  var isConnected = false
15
15
 
16
+ private var isCameraInitialized = false
17
+
16
18
  var connectPromise: Promise? = nil
17
19
 
18
20
  var videoSimulcastConfig: SimulcastConfig = SimulcastConfig()
@@ -211,14 +213,22 @@ class RNFishjamClient: FishjamClientListener {
211
213
  isCameraOn = false
212
214
  isScreencastOn = false
213
215
  isConnected = false
216
+ isCameraInitialized = false
214
217
  RNFishjamClient.fishjamClient?.leave()
215
218
  }
216
219
 
217
220
  func startCamera(config: CameraConfig) throws {
218
221
  try ensureCreated()
222
+
223
+ guard !isCameraInitialized else {
224
+ emit(warning: "Camera already started. You may only call startCamera once before leaveRoom is called.")
225
+ return
226
+ }
227
+
219
228
  let cameraTrack = try createCameraTrack(config: config)
220
229
  setCameraTrackState(cameraTrack, enabled: config.cameraEnabled)
221
230
  emitEndpoints()
231
+ isCameraInitialized = true
222
232
  }
223
233
 
224
234
  private func createCameraTrack(config: CameraConfig) throws -> LocalVideoTrack {
@@ -667,6 +677,10 @@ class RNFishjamClient: FishjamClientListener {
667
677
  sendEvent(name, data)
668
678
  }
669
679
 
680
+ func emit(warning: String) {
681
+ emitEvent(name: EmitableEvents.Warning, data: ["message": warning])
682
+ }
683
+
670
684
  func emitEndpoints() {
671
685
  let eventName = EmitableEvents.PeersUpdate
672
686
  let EndpointsUpdateMap = [eventName: (try? getPeers()) ?? []]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fishjam-cloud/react-native-client",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "A React Native client for Fishjam Cloud",
5
5
  "author": "Fishjam Cloud Team",
6
6
  "license": "Apache-2.0",
@@ -27,7 +27,7 @@
27
27
  "MembraneWebRTC",
28
28
  "fishjam"
29
29
  ],
30
- "repository": "https://github.com/fishjam-cloud/mobile-client-sdk",
30
+ "repository": "github:fishjam-cloud/mobile-client-sdk",
31
31
  "bugs": {
32
32
  "url": "https://github.com/fishjam-cloud/mobile-client-sdk/issues"
33
33
  },
@@ -46,7 +46,7 @@
46
46
  "@types/lodash": "^4.17.7",
47
47
  "@types/phoenix": "^1.6.5",
48
48
  "@types/promise-fs": "^2.1.2",
49
- "@types/react": "^18.3.5",
49
+ "@types/react": "^18.3.7",
50
50
  "expo": "^51.0.32",
51
51
  "expo-module-scripts": "^3.5.2",
52
52
  "expo-modules-core": "^1.12.24",
@@ -78,12 +78,13 @@
78
78
  "testEnvironment": "jsdom"
79
79
  },
80
80
  "files": [
81
- "build/",
81
+ "build/**",
82
82
  "android/build.gradle",
83
- "android/src/",
83
+ "android/src/**",
84
84
  "ios/*.swift",
85
85
  "ios/RNFishjamClient.podspec",
86
- "plugin/build/",
86
+ "plugin/build/**",
87
+ "plugin/broadcastExtensionFiles/**",
87
88
  "app.plugin.js",
88
89
  "expo-module.config.json",
89
90
  "README.md",
@@ -0,0 +1,50 @@
1
+ import FishjamCloudClient
2
+ import Foundation
3
+ import ReplayKit
4
+ import WebRTC
5
+ import os.log
6
+
7
+ /// App Group used by the extension to exchange buffers with the target application
8
+ let appGroup = "{{GROUP_IDENTIFIER}}"
9
+
10
+ let logger = OSLog(subsystem: "{{BUNDLE_IDENTIFIER}}.FishjamBroadcastSampleHandler", category: "Broadcaster")
11
+
12
+ /// An example `SampleHandler` utilizing `BroadcastSampleSource` from `MembraneRTC` sending broadcast samples and necessary notification enabling device's screencast.
13
+ class FishjamBroadcastSampleHandler: RPBroadcastSampleHandler {
14
+ let broadcastSource = BroadcastSampleSource(appGroup: appGroup)
15
+ var started: Bool = false
16
+
17
+ override func broadcastStarted(withSetupInfo _: [String: NSObject]?) {
18
+ started = broadcastSource.connect()
19
+
20
+ guard started else {
21
+ os_log("failed to connect with ipc server", log: logger, type: .debug)
22
+
23
+ super.finishBroadcastWithError(NSError(domain: "", code: 0, userInfo: nil))
24
+
25
+ return
26
+ }
27
+
28
+ broadcastSource.started()
29
+ }
30
+
31
+ override func broadcastPaused() {
32
+ broadcastSource.paused()
33
+ }
34
+
35
+ override func broadcastResumed() {
36
+ broadcastSource.resumed()
37
+ }
38
+
39
+ override func broadcastFinished() {
40
+ broadcastSource.finished()
41
+ }
42
+
43
+ override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
44
+ guard started else {
45
+ return
46
+ }
47
+
48
+ broadcastSource.processFrame(sampleBuffer: sampleBuffer, ofType: sampleBufferType)
49
+ }
50
+ }
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>com.apple.security.application-groups</key>
6
+ <array>
7
+ <string>{{GROUP_IDENTIFIER}}</string>
8
+ </array>
9
+ </dict>
10
+ </plist>
@@ -0,0 +1,27 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDisplayName</key>
6
+ <string>FishjamScreenBroadcast</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleName</key>
12
+ <string>$(PRODUCT_NAME)</string>
13
+ <key>CFBundlePackageType</key>
14
+ <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
15
+ <key>CFBundleVersion</key>
16
+ <string>$(CURRENT_PROJECT_VERSION)</string>
17
+ <key>NSExtension</key>
18
+ <dict>
19
+ <key>NSExtensionPointIdentifier</key>
20
+ <string>com.apple.broadcast-services-upload</string>
21
+ <key>NSExtensionPrincipalClass</key>
22
+ <string>$(PRODUCT_MODULE_NAME).FishjamBroadcastSampleHandler</string>
23
+ <key>RPBroadcastProcessMode</key>
24
+ <string>RPBroadcastProcessModeSampleBuffer</string>
25
+ </dict>
26
+ </dict>
27
+ </plist>
@@ -0,0 +1,9 @@
1
+ export type FishjamPluginOptions = {
2
+ android?: {
3
+ enableForegroundService?: boolean;
4
+ };
5
+ ios?: {
6
+ iphoneDeploymentTarget?: string;
7
+ enableScreensharing?: boolean;
8
+ };
9
+ } | undefined;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import { FishjamPluginOptions } from './types';
3
+ declare const withFishjam: ConfigPlugin<FishjamPluginOptions>;
4
+ export default withFishjam;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const withFishjamAndroid_1 = require("./withFishjamAndroid");
7
+ const withFishjamIos_1 = __importDefault(require("./withFishjamIos"));
8
+ const withFishjam = (config, options) => {
9
+ (0, withFishjamAndroid_1.withFishjamAndroid)(config, options);
10
+ (0, withFishjamIos_1.default)(config, options);
11
+ return config;
12
+ };
13
+ exports.default = withFishjam;
@@ -0,0 +1,3 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import { FishjamPluginOptions } from './types';
3
+ export declare const withFishjamAndroid: ConfigPlugin<FishjamPluginOptions>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withFishjamAndroid = void 0;
4
+ const config_plugins_1 = require("@expo/config-plugins");
5
+ const Manifest_1 = require("@expo/config-plugins/build/android/Manifest");
6
+ const withFishjamForegroundService = (config) => {
7
+ return (0, config_plugins_1.withAndroidManifest)(config, async (config) => {
8
+ const mainApplication = (0, Manifest_1.getMainApplicationOrThrow)(config.modResults);
9
+ mainApplication.service = mainApplication.service || [];
10
+ const newService = {
11
+ $: {
12
+ 'android:name': 'io.fishjam.reactnative.FishjamForegroundService',
13
+ 'android:foregroundServiceType': 'camera|microphone|mediaProjection',
14
+ },
15
+ };
16
+ const existingServiceIndex = mainApplication.service.findIndex((service) => service.$['android:name'] === newService.$['android:name']);
17
+ if (existingServiceIndex !== -1) {
18
+ mainApplication.service[existingServiceIndex] = newService;
19
+ }
20
+ else {
21
+ mainApplication.service.push(newService);
22
+ }
23
+ return config;
24
+ });
25
+ };
26
+ const withFishjamAndroid = (config, props) => {
27
+ if (props?.android?.enableForegroundService) {
28
+ config = withFishjamForegroundService(config);
29
+ }
30
+ return config;
31
+ };
32
+ exports.withFishjamAndroid = withFishjamAndroid;
@@ -0,0 +1,9 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import { FishjamPluginOptions } from './types';
3
+ export declare const SBE_PODFILE_SNIPPET = "\ntarget 'FishjamScreenBroadcastExtension' do\n pod 'FishjamCloudClient/Broadcast'\nend";
4
+ /**
5
+ * Applies screen sharing plugin if enabled. In order for screensharing to work, we need to copy extension files to your iOS project.
6
+ * Allows for dynamically changing deploymentTarget.
7
+ */
8
+ declare const withFishjamIos: ConfigPlugin<FishjamPluginOptions>;
9
+ export default withFishjamIos;
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SBE_PODFILE_SNIPPET = void 0;
27
+ // ios-related code was mostly copied from OneSignal expo plugin: https://github.com/OneSignal/onesignal-expo-plugin/blob/main/onesignal/withOneSignalIos.ts
28
+ const config_plugins_1 = require("@expo/config-plugins");
29
+ const fs = __importStar(require("promise-fs"));
30
+ const path = __importStar(require("path"));
31
+ const SBE_TARGET_NAME = 'FishjamScreenBroadcastExtension';
32
+ exports.SBE_PODFILE_SNIPPET = `
33
+ target '${SBE_TARGET_NAME}' do
34
+ pod 'FishjamCloudClient/Broadcast'
35
+ end`;
36
+ const TARGETED_DEVICE_FAMILY = `"1,2"`;
37
+ const IPHONEOS_DEPLOYMENT_TARGET = '13.4';
38
+ const GROUP_IDENTIFIER_TEMPLATE_REGEX = /{{GROUP_IDENTIFIER}}/gm;
39
+ const BUNDLE_IDENTIFIER_TEMPLATE_REGEX = /{{BUNDLE_IDENTIFIER}}/gm;
40
+ /**
41
+ * A helper function for updating a value in a file for given regex
42
+ */
43
+ async function updateFileWithRegex(iosPath, fileName, regex, value) {
44
+ const filePath = `${iosPath}/${SBE_TARGET_NAME}/${fileName}`;
45
+ let file = await fs.readFile(filePath, { encoding: 'utf-8' });
46
+ file = file.replace(regex, value);
47
+ await fs.writeFile(filePath, file);
48
+ }
49
+ /**
50
+ * Inserts a required target to Podfile.
51
+ * This is needed to provide the dependency of FishjamCloudClient/Broadcast to the extension.
52
+ */
53
+ async function updatePodfile(iosPath) {
54
+ let matches;
55
+ try {
56
+ const podfile = await fs.readFile(`${iosPath}/Podfile`, {
57
+ encoding: 'utf-8',
58
+ });
59
+ matches = podfile.match(exports.SBE_PODFILE_SNIPPET);
60
+ }
61
+ catch (e) {
62
+ console.error('Error reading from Podfile: ', e);
63
+ }
64
+ if (matches) {
65
+ console.log(`${SBE_TARGET_NAME} target already added to Podfile. Skipping...`);
66
+ return;
67
+ }
68
+ try {
69
+ fs.appendFile(`${iosPath}/Podfile`, exports.SBE_PODFILE_SNIPPET);
70
+ }
71
+ catch (e) {
72
+ console.error('Error writing to Podfile: ', e);
73
+ }
74
+ }
75
+ /**
76
+ * Adds "App Group" permission
77
+ * App Group allow your app and the FishjamScreenBroadcastExtension to communicate with each other.
78
+ */
79
+ const withAppGroupPermissions = (config) => {
80
+ const APP_GROUP_KEY = 'com.apple.security.application-groups';
81
+ return (0, config_plugins_1.withEntitlementsPlist)(config, (newConfig) => {
82
+ if (!Array.isArray(newConfig.modResults[APP_GROUP_KEY])) {
83
+ newConfig.modResults[APP_GROUP_KEY] = [];
84
+ }
85
+ const modResultsArray = newConfig.modResults[APP_GROUP_KEY];
86
+ const entitlement = `group.${newConfig?.ios?.bundleIdentifier || ''}`;
87
+ if (modResultsArray.indexOf(entitlement) !== -1) {
88
+ return newConfig;
89
+ }
90
+ modResultsArray.push(entitlement);
91
+ return newConfig;
92
+ });
93
+ };
94
+ /**
95
+ * Adds constants to Info.plist
96
+ * In other to dynamically retreive extension's bundleId and group name we need to store it in Info.plist.
97
+ */
98
+ const withInfoPlistConstants = (config) => {
99
+ return (0, config_plugins_1.withInfoPlist)(config, (config) => {
100
+ const bundleIdentifier = config.ios?.bundleIdentifier || '';
101
+ config.modResults['AppGroupName'] = `group.${bundleIdentifier}`;
102
+ config.modResults['ScreencastExtensionBundleId'] =
103
+ `${bundleIdentifier}.${SBE_TARGET_NAME}`;
104
+ return config;
105
+ });
106
+ };
107
+ /**
108
+ * Updates and copies required extension files.
109
+ * Our extension needs to be properly setup inside the XCode project. In order to do that we need to copy the files and update the pbxproj.
110
+ */
111
+ const withFishjamSBE = (config, options) => {
112
+ return (0, config_plugins_1.withXcodeProject)(config, async (props) => {
113
+ const appName = props.modRequest.projectName || '';
114
+ const iosPath = props.modRequest.platformProjectRoot;
115
+ const bundleIdentifier = props.ios?.bundleIdentifier;
116
+ const xcodeProject = props.modResults;
117
+ const pluginDir = require.resolve('@fishjam-cloud/react-native-client/package.json');
118
+ const extensionSourceDir = path.join(pluginDir, '../plugin/broadcastExtensionFiles/');
119
+ await updatePodfile(iosPath);
120
+ const projPath = `${iosPath}/${appName}.xcodeproj/project.pbxproj`;
121
+ const extFiles = [
122
+ 'FishjamBroadcastSampleHandler.swift',
123
+ `${SBE_TARGET_NAME}.entitlements`,
124
+ `Info.plist`,
125
+ ];
126
+ await xcodeProject.parse(async function (err) {
127
+ if (err) {
128
+ console.error(`Error parsing iOS project: ${JSON.stringify(err)}`);
129
+ return;
130
+ }
131
+ if (xcodeProject.pbxTargetByName(`"${SBE_TARGET_NAME}"`)) {
132
+ console.log(`${SBE_TARGET_NAME} already exists in project. Skipping...`);
133
+ return;
134
+ }
135
+ try {
136
+ // copy extension files
137
+ await fs.mkdir(`${iosPath}/${SBE_TARGET_NAME}`, { recursive: true });
138
+ for (let i = 0; i < extFiles.length; i++) {
139
+ const extFile = extFiles[i];
140
+ const targetFile = `${iosPath}/${SBE_TARGET_NAME}/${extFile}`;
141
+ await fs.copyFile(`${extensionSourceDir}${extFile}`, targetFile);
142
+ }
143
+ }
144
+ catch (e) {
145
+ console.error('Error copying extension files: ', e);
146
+ }
147
+ // update extension files
148
+ await updateFileWithRegex(iosPath, `${SBE_TARGET_NAME}.entitlements`, GROUP_IDENTIFIER_TEMPLATE_REGEX, `group.${bundleIdentifier}`);
149
+ await updateFileWithRegex(iosPath, 'FishjamBroadcastSampleHandler.swift', GROUP_IDENTIFIER_TEMPLATE_REGEX, `group.${bundleIdentifier}`);
150
+ await updateFileWithRegex(iosPath, 'FishjamBroadcastSampleHandler.swift', BUNDLE_IDENTIFIER_TEMPLATE_REGEX, bundleIdentifier || '');
151
+ // Create new PBXGroup for the extension
152
+ const extGroup = xcodeProject.addPbxGroup(extFiles, SBE_TARGET_NAME, SBE_TARGET_NAME);
153
+ // Add the new PBXGroup to the top level group. This makes the
154
+ // files / folder appear in the file explorer in Xcode.
155
+ const groups = xcodeProject.hash.project.objects['PBXGroup'];
156
+ Object.keys(groups).forEach(function (key) {
157
+ if (groups[key].name === undefined) {
158
+ xcodeProject.addToPbxGroup(extGroup.uuid, key);
159
+ }
160
+ });
161
+ // WORK AROUND for codeProject.addTarget BUG
162
+ // Xcode projects don't contain these if there is only one target
163
+ // An upstream fix should be made to the code referenced in this link:
164
+ // - https://github.com/apache/cordova-node-xcode/blob/8b98cabc5978359db88dc9ff2d4c015cba40f150/lib/pbxProject.js#L860
165
+ const projObjects = xcodeProject.hash.project.objects;
166
+ projObjects['PBXTargetDependency'] =
167
+ projObjects['PBXTargetDependency'] || {};
168
+ projObjects['PBXContainerItemProxy'] =
169
+ projObjects['PBXTargetDependency'] || {};
170
+ // Add the SBE target
171
+ // This adds PBXTargetDependency and PBXContainerItemProxy for you
172
+ const sbeTarget = xcodeProject.addTarget(SBE_TARGET_NAME, 'app_extension', SBE_TARGET_NAME, `${bundleIdentifier}.${SBE_TARGET_NAME}`);
173
+ // Add build phases to the new target
174
+ xcodeProject.addBuildPhase(['FishjamBroadcastSampleHandler.swift'], 'PBXSourcesBuildPhase', 'Sources', sbeTarget.uuid);
175
+ xcodeProject.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', sbeTarget.uuid);
176
+ xcodeProject.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', sbeTarget.uuid);
177
+ xcodeProject.addFramework('ReplayKit.framework', {
178
+ target: sbeTarget.uuid,
179
+ });
180
+ // Edit the Deployment info of the new Target, only IphoneOS and Targeted Device Family
181
+ // However, can be more
182
+ const configurations = xcodeProject.pbxXCBuildConfigurationSection();
183
+ for (const key in configurations) {
184
+ if (typeof configurations[key].buildSettings !== 'undefined' &&
185
+ configurations[key].buildSettings.PRODUCT_NAME ===
186
+ `"${SBE_TARGET_NAME}"`) {
187
+ const buildSettingsObj = configurations[key].buildSettings;
188
+ buildSettingsObj.IPHONEOS_DEPLOYMENT_TARGET =
189
+ options?.ios?.iphoneDeploymentTarget ?? IPHONEOS_DEPLOYMENT_TARGET;
190
+ buildSettingsObj.TARGETED_DEVICE_FAMILY = TARGETED_DEVICE_FAMILY;
191
+ buildSettingsObj.CODE_SIGN_ENTITLEMENTS = `${SBE_TARGET_NAME}/${SBE_TARGET_NAME}.entitlements`;
192
+ buildSettingsObj.CODE_SIGN_STYLE = 'Automatic';
193
+ buildSettingsObj.INFOPLIST_FILE = `${SBE_TARGET_NAME}/Info.plist`;
194
+ buildSettingsObj.SWIFT_VERSION = '5.0';
195
+ buildSettingsObj.MARKETING_VERSION = '1.0.0';
196
+ buildSettingsObj.CURRENT_PROJECT_VERSION = '1';
197
+ buildSettingsObj.ENABLE_BITCODE = 'NO';
198
+ }
199
+ }
200
+ await fs.writeFile(projPath, xcodeProject.writeSync());
201
+ });
202
+ return props;
203
+ });
204
+ };
205
+ /**
206
+ * Applies screen sharing plugin if enabled. In order for screensharing to work, we need to copy extension files to your iOS project.
207
+ * Allows for dynamically changing deploymentTarget.
208
+ */
209
+ const withFishjamIos = (config, props) => {
210
+ if (props?.ios?.enableScreensharing) {
211
+ withAppGroupPermissions(config);
212
+ withInfoPlistConstants(config);
213
+ withFishjamSBE(config, props);
214
+ }
215
+ (0, config_plugins_1.withPodfileProperties)(config, (config) => {
216
+ config.modResults['ios.deploymentTarget'] =
217
+ props?.ios?.iphoneDeploymentTarget ?? IPHONEOS_DEPLOYMENT_TARGET;
218
+ return config;
219
+ });
220
+ return config;
221
+ };
222
+ exports.default = withFishjamIos;