@stream-io/video-react-native-sdk 1.30.5 → 1.31.1-beta.0

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 (248) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/android/src/main/AndroidManifest.xml +8 -1
  3. package/android/src/main/AndroidManifestNew.xml +11 -0
  4. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +127 -5
  5. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/WebRtcAudioUtils.kt +70 -6
  6. package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +6 -4
  7. package/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt +83 -0
  8. package/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt +149 -0
  9. package/android/src/main/java/com/streamvideo/reactnative/screenshare/ScreenAudioCapture.kt +111 -0
  10. package/dist/commonjs/components/Call/CallControls/ScreenShareToggleButton.js +3 -2
  11. package/dist/commonjs/components/Call/CallControls/ScreenShareToggleButton.js.map +1 -1
  12. package/dist/commonjs/hooks/index.js +11 -0
  13. package/dist/commonjs/hooks/index.js.map +1 -1
  14. package/dist/commonjs/hooks/push/index.js +0 -2
  15. package/dist/commonjs/hooks/push/index.js.map +1 -1
  16. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js +144 -0
  17. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -0
  18. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +18 -31
  19. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  20. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js +64 -97
  21. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  22. package/dist/commonjs/hooks/useScreenShareAudioMixing.js +126 -0
  23. package/dist/commonjs/hooks/useScreenShareAudioMixing.js.map +1 -0
  24. package/dist/commonjs/hooks/useScreenShareButton.js +57 -3
  25. package/dist/commonjs/hooks/useScreenShareButton.js.map +1 -1
  26. package/dist/commonjs/index.js +1 -0
  27. package/dist/commonjs/index.js.map +1 -1
  28. package/dist/commonjs/modules/ScreenShareAudioManager.js +54 -0
  29. package/dist/commonjs/modules/ScreenShareAudioManager.js.map +1 -0
  30. package/dist/commonjs/modules/call-manager/CallManager.js +26 -0
  31. package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -1
  32. package/dist/commonjs/providers/StreamCall/index.js +16 -6
  33. package/dist/commonjs/providers/StreamCall/index.js.map +1 -1
  34. package/dist/commonjs/utils/StreamVideoRN/index.js +35 -21
  35. package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
  36. package/dist/commonjs/utils/internal/callingx/audioSessionPromise.js +68 -0
  37. package/dist/commonjs/utils/internal/callingx/audioSessionPromise.js.map +1 -0
  38. package/dist/commonjs/utils/internal/callingx/callingx.js +150 -0
  39. package/dist/commonjs/utils/internal/callingx/callingx.js.map +1 -0
  40. package/dist/commonjs/utils/internal/registerSDKGlobals.js +53 -3
  41. package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -1
  42. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js +48 -0
  43. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js.map +1 -0
  44. package/dist/commonjs/utils/push/android.js +135 -202
  45. package/dist/commonjs/utils/push/android.js.map +1 -1
  46. package/dist/commonjs/utils/push/internal/ios.js +17 -34
  47. package/dist/commonjs/utils/push/internal/ios.js.map +1 -1
  48. package/dist/commonjs/utils/push/internal/rxSubjects.js +1 -45
  49. package/dist/commonjs/utils/push/internal/rxSubjects.js.map +1 -1
  50. package/dist/commonjs/utils/push/internal/utils.js +71 -53
  51. package/dist/commonjs/utils/push/internal/utils.js.map +1 -1
  52. package/dist/commonjs/utils/push/ios.js.map +1 -1
  53. package/dist/commonjs/utils/push/libs/callingx.js +75 -0
  54. package/dist/commonjs/utils/push/libs/callingx.js.map +1 -0
  55. package/dist/commonjs/utils/push/libs/index.js +8 -19
  56. package/dist/commonjs/utils/push/libs/index.js.map +1 -1
  57. package/dist/commonjs/utils/push/libs/notifee/index.js +0 -19
  58. package/dist/commonjs/utils/push/libs/notifee/index.js.map +1 -1
  59. package/dist/commonjs/utils/push/setupCallingExpEvents.js +105 -0
  60. package/dist/commonjs/utils/push/setupCallingExpEvents.js.map +1 -0
  61. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js +7 -6
  62. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js.map +1 -1
  63. package/dist/commonjs/version.js +1 -1
  64. package/dist/commonjs/version.js.map +1 -1
  65. package/dist/module/components/Call/CallControls/ScreenShareToggleButton.js +3 -2
  66. package/dist/module/components/Call/CallControls/ScreenShareToggleButton.js.map +1 -1
  67. package/dist/module/hooks/index.js +1 -0
  68. package/dist/module/hooks/index.js.map +1 -1
  69. package/dist/module/hooks/push/index.js +0 -2
  70. package/dist/module/hooks/push/index.js.map +1 -1
  71. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js +137 -0
  72. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -0
  73. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +18 -31
  74. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  75. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js +66 -99
  76. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  77. package/dist/module/hooks/useScreenShareAudioMixing.js +119 -0
  78. package/dist/module/hooks/useScreenShareAudioMixing.js.map +1 -0
  79. package/dist/module/hooks/useScreenShareButton.js +57 -3
  80. package/dist/module/hooks/useScreenShareButton.js.map +1 -1
  81. package/dist/module/index.js +1 -0
  82. package/dist/module/index.js.map +1 -1
  83. package/dist/module/modules/ScreenShareAudioManager.js +47 -0
  84. package/dist/module/modules/ScreenShareAudioManager.js.map +1 -0
  85. package/dist/module/modules/call-manager/CallManager.js +26 -0
  86. package/dist/module/modules/call-manager/CallManager.js.map +1 -1
  87. package/dist/module/providers/StreamCall/index.js +16 -6
  88. package/dist/module/providers/StreamCall/index.js.map +1 -1
  89. package/dist/module/utils/StreamVideoRN/index.js +35 -21
  90. package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
  91. package/dist/module/utils/internal/callingx/audioSessionPromise.js +61 -0
  92. package/dist/module/utils/internal/callingx/audioSessionPromise.js.map +1 -0
  93. package/dist/module/utils/internal/callingx/callingx.js +140 -0
  94. package/dist/module/utils/internal/callingx/callingx.js.map +1 -0
  95. package/dist/module/utils/internal/registerSDKGlobals.js +53 -3
  96. package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -1
  97. package/dist/module/utils/keepCallAliveHeadlessTask.js +42 -0
  98. package/dist/module/utils/keepCallAliveHeadlessTask.js.map +1 -0
  99. package/dist/module/utils/push/android.js +137 -204
  100. package/dist/module/utils/push/android.js.map +1 -1
  101. package/dist/module/utils/push/internal/ios.js +17 -34
  102. package/dist/module/utils/push/internal/ios.js.map +1 -1
  103. package/dist/module/utils/push/internal/rxSubjects.js +0 -44
  104. package/dist/module/utils/push/internal/rxSubjects.js.map +1 -1
  105. package/dist/module/utils/push/internal/utils.js +67 -50
  106. package/dist/module/utils/push/internal/utils.js.map +1 -1
  107. package/dist/module/utils/push/ios.js.map +1 -1
  108. package/dist/module/utils/push/libs/callingx.js +67 -0
  109. package/dist/module/utils/push/libs/callingx.js.map +1 -0
  110. package/dist/module/utils/push/libs/index.js +1 -2
  111. package/dist/module/utils/push/libs/index.js.map +1 -1
  112. package/dist/module/utils/push/libs/notifee/index.js +0 -18
  113. package/dist/module/utils/push/libs/notifee/index.js.map +1 -1
  114. package/dist/module/utils/push/setupCallingExpEvents.js +99 -0
  115. package/dist/module/utils/push/setupCallingExpEvents.js.map +1 -0
  116. package/dist/module/utils/push/setupIosVoipPushEvents.js +7 -6
  117. package/dist/module/utils/push/setupIosVoipPushEvents.js.map +1 -1
  118. package/dist/module/version.js +1 -1
  119. package/dist/module/version.js.map +1 -1
  120. package/dist/typescript/components/Call/CallControls/ScreenShareToggleButton.d.ts +6 -1
  121. package/dist/typescript/components/Call/CallControls/ScreenShareToggleButton.d.ts.map +1 -1
  122. package/dist/typescript/hooks/index.d.ts +1 -0
  123. package/dist/typescript/hooks/index.d.ts.map +1 -1
  124. package/dist/typescript/hooks/push/index.d.ts.map +1 -1
  125. package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts +5 -0
  126. package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts.map +1 -0
  127. package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
  128. package/dist/typescript/hooks/useAndroidKeepCallAliveEffect.d.ts.map +1 -1
  129. package/dist/typescript/hooks/useScreenShareAudioMixing.d.ts +14 -0
  130. package/dist/typescript/hooks/useScreenShareAudioMixing.d.ts.map +1 -0
  131. package/dist/typescript/hooks/useScreenShareButton.d.ts +39 -2
  132. package/dist/typescript/hooks/useScreenShareButton.d.ts.map +1 -1
  133. package/dist/typescript/index.d.ts +1 -0
  134. package/dist/typescript/index.d.ts.map +1 -1
  135. package/dist/typescript/modules/ScreenShareAudioManager.d.ts +28 -0
  136. package/dist/typescript/modules/ScreenShareAudioManager.d.ts.map +1 -0
  137. package/dist/typescript/modules/call-manager/CallManager.d.ts +5 -0
  138. package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -1
  139. package/dist/typescript/providers/StreamCall/index.d.ts.map +1 -1
  140. package/dist/typescript/utils/StreamVideoRN/index.d.ts +22 -2
  141. package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
  142. package/dist/typescript/utils/StreamVideoRN/types.d.ts +59 -25
  143. package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
  144. package/dist/typescript/utils/internal/callingx/audioSessionPromise.d.ts +16 -0
  145. package/dist/typescript/utils/internal/callingx/audioSessionPromise.d.ts.map +1 -0
  146. package/dist/typescript/utils/internal/callingx/callingx.d.ts +18 -0
  147. package/dist/typescript/utils/internal/callingx/callingx.d.ts.map +1 -0
  148. package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -1
  149. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts +10 -0
  150. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts.map +1 -0
  151. package/dist/typescript/utils/push/android.d.ts +1 -2
  152. package/dist/typescript/utils/push/android.d.ts.map +1 -1
  153. package/dist/typescript/utils/push/internal/ios.d.ts.map +1 -1
  154. package/dist/typescript/utils/push/internal/rxSubjects.d.ts +0 -33
  155. package/dist/typescript/utils/push/internal/rxSubjects.d.ts.map +1 -1
  156. package/dist/typescript/utils/push/internal/utils.d.ts +14 -8
  157. package/dist/typescript/utils/push/internal/utils.d.ts.map +1 -1
  158. package/dist/typescript/utils/push/ios.d.ts +1 -2
  159. package/dist/typescript/utils/push/ios.d.ts.map +1 -1
  160. package/dist/typescript/utils/push/libs/callingx.d.ts +9 -0
  161. package/dist/typescript/utils/push/libs/callingx.d.ts.map +1 -0
  162. package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts +16 -2
  163. package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts.map +1 -1
  164. package/dist/typescript/utils/push/libs/index.d.ts +1 -2
  165. package/dist/typescript/utils/push/libs/index.d.ts.map +1 -1
  166. package/dist/typescript/utils/push/libs/notifee/index.d.ts +0 -1
  167. package/dist/typescript/utils/push/libs/notifee/index.d.ts.map +1 -1
  168. package/dist/typescript/utils/push/setupCallingExpEvents.d.ts +8 -0
  169. package/dist/typescript/utils/push/setupCallingExpEvents.d.ts.map +1 -0
  170. package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts.map +1 -1
  171. package/dist/typescript/version.d.ts +1 -1
  172. package/dist/typescript/version.d.ts.map +1 -1
  173. package/expo-config-plugin/dist/withAndroidManifest.js +1 -33
  174. package/expo-config-plugin/dist/withAndroidPermissions.js +2 -7
  175. package/expo-config-plugin/dist/withAppDelegate.js +19 -197
  176. package/expo-config-plugin/dist/withMainActivity.js +1 -1
  177. package/expo-config-plugin/dist/withiOSInfoPlist.js +2 -3
  178. package/ios/StreamInCallManager.m +2 -0
  179. package/ios/StreamInCallManager.swift +19 -7
  180. package/ios/StreamVideoReactNative.h +7 -4
  181. package/ios/StreamVideoReactNative.m +282 -86
  182. package/package.json +13 -18
  183. package/src/components/Call/CallControls/ScreenShareToggleButton.tsx +11 -1
  184. package/src/hooks/index.ts +1 -0
  185. package/src/hooks/push/index.ts +0 -2
  186. package/src/hooks/push/useCallingExpWithCallingStateEffect.ts +189 -0
  187. package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +21 -34
  188. package/src/hooks/useAndroidKeepCallAliveEffect.ts +94 -120
  189. package/src/hooks/useScreenShareAudioMixing.ts +130 -0
  190. package/src/hooks/useScreenShareButton.ts +87 -2
  191. package/src/index.ts +1 -0
  192. package/src/modules/ScreenShareAudioManager.ts +49 -0
  193. package/src/modules/call-manager/CallManager.ts +36 -0
  194. package/src/modules/call-manager/native-module.d.ts +7 -0
  195. package/src/providers/StreamCall/index.tsx +17 -6
  196. package/src/utils/StreamVideoRN/index.ts +42 -30
  197. package/src/utils/StreamVideoRN/types.ts +61 -25
  198. package/src/utils/internal/callingx/audioSessionPromise.ts +65 -0
  199. package/src/utils/internal/callingx/callingx.ts +194 -0
  200. package/src/utils/internal/registerSDKGlobals.ts +52 -4
  201. package/src/utils/keepCallAliveHeadlessTask.ts +54 -0
  202. package/src/utils/push/android.ts +198 -311
  203. package/src/utils/push/internal/ios.ts +28 -44
  204. package/src/utils/push/internal/rxSubjects.ts +0 -61
  205. package/src/utils/push/internal/utils.ts +108 -64
  206. package/src/utils/push/ios.ts +1 -6
  207. package/src/utils/push/libs/callingx.ts +89 -0
  208. package/src/utils/push/libs/index.ts +1 -2
  209. package/src/utils/push/libs/notifee/index.ts +0 -27
  210. package/src/utils/push/setupCallingExpEvents.ts +135 -0
  211. package/src/utils/push/setupIosVoipPushEvents.ts +11 -7
  212. package/src/version.ts +1 -1
  213. package/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt +0 -95
  214. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js +0 -160
  215. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +0 -1
  216. package/dist/commonjs/hooks/push/useProcessPushCallEffect.js +0 -67
  217. package/dist/commonjs/hooks/push/useProcessPushCallEffect.js.map +0 -1
  218. package/dist/commonjs/utils/push/libs/callkeep.js +0 -17
  219. package/dist/commonjs/utils/push/libs/callkeep.js.map +0 -1
  220. package/dist/commonjs/utils/push/libs/voipPushNotification.js +0 -17
  221. package/dist/commonjs/utils/push/libs/voipPushNotification.js.map +0 -1
  222. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js +0 -205
  223. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js.map +0 -1
  224. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js +0 -153
  225. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +0 -1
  226. package/dist/module/hooks/push/useProcessPushCallEffect.js +0 -60
  227. package/dist/module/hooks/push/useProcessPushCallEffect.js.map +0 -1
  228. package/dist/module/utils/push/libs/callkeep.js +0 -11
  229. package/dist/module/utils/push/libs/callkeep.js.map +0 -1
  230. package/dist/module/utils/push/libs/voipPushNotification.js +0 -11
  231. package/dist/module/utils/push/libs/voipPushNotification.js.map +0 -1
  232. package/dist/module/utils/push/setupIosCallKeepEvents.js +0 -199
  233. package/dist/module/utils/push/setupIosCallKeepEvents.js.map +0 -1
  234. package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts +0 -5
  235. package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts.map +0 -1
  236. package/dist/typescript/hooks/push/useProcessPushCallEffect.d.ts +0 -8
  237. package/dist/typescript/hooks/push/useProcessPushCallEffect.d.ts.map +0 -1
  238. package/dist/typescript/utils/push/libs/callkeep.d.ts +0 -3
  239. package/dist/typescript/utils/push/libs/callkeep.d.ts.map +0 -1
  240. package/dist/typescript/utils/push/libs/voipPushNotification.d.ts +0 -3
  241. package/dist/typescript/utils/push/libs/voipPushNotification.d.ts.map +0 -1
  242. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts +0 -6
  243. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts.map +0 -1
  244. package/src/hooks/push/useIosCallkeepWithCallingStateEffect.ts +0 -235
  245. package/src/hooks/push/useProcessPushCallEffect.ts +0 -108
  246. package/src/utils/push/libs/callkeep.ts +0 -16
  247. package/src/utils/push/libs/voipPushNotification.ts +0 -17
  248. package/src/utils/push/setupIosCallKeepEvents.ts +0 -252
@@ -0,0 +1,130 @@
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
+ import { hasScreenShare, videoLoggerSystem } from '@stream-io/video-client';
3
+ import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
4
+ import { screenShareAudioMixingManager } from '../modules/ScreenShareAudioManager';
5
+ import { NoiseCancellationWrapper } from '../providers/NoiseCancellation/lib';
6
+
7
+ const logger = videoLoggerSystem.getLogger('useScreenShareAudioMixing');
8
+
9
+ /**
10
+ * Tries to disable noise cancellation so screen audio passes through
11
+ * unfiltered. Returns true if NC was disabled (and should be re-enabled later).
12
+ */
13
+ async function disableNoiseCancellation(): Promise<boolean> {
14
+ try {
15
+ const nc = NoiseCancellationWrapper.getInstance();
16
+ const wasEnabled = await nc.isEnabled();
17
+ if (wasEnabled) {
18
+ await nc.disable();
19
+ logger.info('Noise cancellation disabled for screen share audio');
20
+ }
21
+ return wasEnabled;
22
+ } catch {
23
+ // NC module not installed or not configured — nothing to do
24
+ return false;
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Re-enables noise cancellation if it was previously disabled.
30
+ */
31
+ async function restoreNoiseCancellation() {
32
+ try {
33
+ const nc = NoiseCancellationWrapper.getInstance();
34
+ await nc.enable();
35
+ logger.info('Noise cancellation re-enabled after screen share audio');
36
+ } catch {
37
+ // NC module not installed — nothing to do
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Hook that manages the lifecycle of screen share audio mixing.
43
+ *
44
+ * When screen share is active and audio mixing is enabled
45
+ * (via `call.screenShare.enableScreenShareAudio()`), this hook
46
+ * calls the native module to mix captured screen/app audio
47
+ * into the microphone audio track.
48
+ *
49
+ * Noise cancellation is temporarily disabled while screen audio mixing
50
+ * is active so that all captured sounds (music, game audio, etc.)
51
+ * pass through without being filtered.
52
+ */
53
+ export const useScreenShareAudioMixing = () => {
54
+ const call = useCall();
55
+ const { useLocalParticipant } = useCallStateHooks();
56
+ const localParticipant = useLocalParticipant();
57
+ const isScreenSharing =
58
+ localParticipant != null && hasScreenShare(localParticipant);
59
+
60
+ const [audioEnabled, setAudioEnabled] = useState(
61
+ () => call?.screenShare.state.audioEnabled ?? false,
62
+ );
63
+
64
+ const isMixingActiveRef = useRef(false);
65
+ const ncWasEnabledRef = useRef(false);
66
+
67
+ useEffect(() => {
68
+ if (!call) return;
69
+ const sub = call.screenShare.state.audioEnabled$.subscribe(setAudioEnabled);
70
+ return () => sub.unsubscribe();
71
+ }, [call]);
72
+
73
+ const startMixing = useCallback(async () => {
74
+ if (isMixingActiveRef.current) return;
75
+ try {
76
+ // Disable NC before starting mixing so screen audio is not filtered
77
+ ncWasEnabledRef.current = await disableNoiseCancellation();
78
+
79
+ logger.info('Starting screen share audio mixing');
80
+ await screenShareAudioMixingManager.startScreenShareAudioMixing();
81
+ isMixingActiveRef.current = true;
82
+ } catch (error) {
83
+ logger.warn('Failed to start screen share audio mixing', error);
84
+ if (ncWasEnabledRef.current) {
85
+ restoreNoiseCancellation().catch(() => {});
86
+ ncWasEnabledRef.current = false;
87
+ }
88
+ }
89
+ }, []);
90
+
91
+ const stopMixing = useCallback(async () => {
92
+ if (!isMixingActiveRef.current) return;
93
+ try {
94
+ logger.info('Stopping screen share audio mixing');
95
+ await screenShareAudioMixingManager.stopScreenShareAudioMixing();
96
+ isMixingActiveRef.current = false;
97
+
98
+ if (ncWasEnabledRef.current) {
99
+ await restoreNoiseCancellation();
100
+ ncWasEnabledRef.current = false;
101
+ }
102
+ } catch (error) {
103
+ logger.warn('Failed to stop screen share audio mixing', error);
104
+ }
105
+ }, []);
106
+
107
+ // Start/stop audio mixing based on screen share status and audio preference
108
+ useEffect(() => {
109
+ if (isScreenSharing && audioEnabled) {
110
+ startMixing();
111
+ } else {
112
+ stopMixing();
113
+ }
114
+ }, [isScreenSharing, audioEnabled, startMixing, stopMixing]);
115
+
116
+ useEffect(() => {
117
+ return () => {
118
+ if (isMixingActiveRef.current) {
119
+ screenShareAudioMixingManager
120
+ .stopScreenShareAudioMixing()
121
+ .catch(() => {});
122
+ isMixingActiveRef.current = false;
123
+ if (ncWasEnabledRef.current) {
124
+ restoreNoiseCancellation().catch(() => {});
125
+ ncWasEnabledRef.current = false;
126
+ }
127
+ }
128
+ };
129
+ }, []);
130
+ };
@@ -8,6 +8,42 @@ import React, { useEffect, useRef } from 'react';
8
8
  import { findNodeHandle, NativeModules, Platform } from 'react-native';
9
9
  import { usePrevious } from '../utils/hooks';
10
10
  import { useIsIosScreenshareBroadcastStarted } from './useIsIosScreenshareBroadcastStarted';
11
+ import { screenShareAudioMixingManager } from '../modules/ScreenShareAudioManager';
12
+
13
+ /**
14
+ * The type of screen sharing to use on iOS.
15
+ *
16
+ * - `'broadcast'` — Uses a Broadcast Upload Extension (RPSystemBroadcastPickerView).
17
+ * Captures the entire device screen, works across all apps. Requires an extension target.
18
+ * - `'inApp'` — Uses RPScreenRecorder.startCapture to capture the current app's screen.
19
+ * Only captures the current app. Supports `.audioApp` sample buffers for audio mixing.
20
+ *
21
+ * On Android, this option is ignored — the system screen capture dialog is always used.
22
+ */
23
+ export type ScreenShareType = 'broadcast' | 'inApp';
24
+
25
+ /**
26
+ * Options for screen share behavior.
27
+ */
28
+ export type ScreenShareOptions = {
29
+ /**
30
+ * The type of screen sharing on iOS. Default: `'broadcast'`.
31
+ * On Android this is ignored.
32
+ */
33
+ type?: ScreenShareType;
34
+ /**
35
+ * Whether to capture and mix system/app audio into the microphone audio track.
36
+ * When `true`, remote participants will hear media audio from the shared screen
37
+ * (e.g., YouTube video audio) mixed with the user's microphone.
38
+ *
39
+ * - iOS in-app: Audio captured from RPScreenRecorder `.audioApp` buffers.
40
+ * - iOS broadcast: Audio mixing is **not** currently supported.
41
+ * - Android: Audio captured via AudioPlaybackCaptureConfiguration (API 29+).
42
+ *
43
+ * Default: `false`.
44
+ */
45
+ includeAudio?: boolean;
46
+ };
11
47
 
12
48
  // ios >= 14.0 or android - platform restrictions
13
49
  const CanDeviceScreenShare =
@@ -18,7 +54,7 @@ const CanDeviceScreenShare =
18
54
  export const useScreenShareButton = (
19
55
  /**
20
56
  * Ref of the ScreenCapturePickerView component.
21
- *
57
+ * Required for iOS broadcast screen sharing. Can be `null` for in-app mode.
22
58
  */
23
59
  screenCapturePickerViewiOSRef: React.MutableRefObject<any>,
24
60
  /**
@@ -36,6 +72,10 @@ export const useScreenShareButton = (
36
72
  *
37
73
  */
38
74
  onMissingScreenShareStreamPermission?: () => void,
75
+ /**
76
+ * Options for screen share behavior (type, includeAudio).
77
+ */
78
+ screenShareOptions?: ScreenShareOptions,
39
79
  ) => {
40
80
  const call = useCall();
41
81
  const { useLocalParticipant, useCallSettings, useOwnCapabilities } =
@@ -47,6 +87,9 @@ export const useScreenShareButton = (
47
87
  );
48
88
  const isScreenSharingEnabledInCall = callSettings?.screensharing.enabled;
49
89
 
90
+ const screenShareType = screenShareOptions?.type ?? 'broadcast';
91
+ const includeAudio = screenShareOptions?.includeAudio ?? false;
92
+
50
93
  const onScreenShareStartedHandlerRef = useRef(onScreenShareStartedHandler);
51
94
  onScreenShareStartedHandlerRef.current = onScreenShareStartedHandler;
52
95
  const onScreenShareStoppedHandlerRef = useRef(onScreenShareStoppedHandler);
@@ -62,15 +105,22 @@ export const useScreenShareButton = (
62
105
  localParticipant && hasScreenShare(localParticipant);
63
106
 
64
107
  // listens to iOS screen share broadcast started event from the system
108
+ // (only relevant for broadcast mode)
65
109
  useEffect(() => {
66
110
  if (Platform.OS !== 'ios') {
67
111
  return;
68
112
  }
113
+ if (screenShareType !== 'broadcast') {
114
+ return;
115
+ }
69
116
  if (
70
117
  iosScreenShareStartedFromSystem &&
71
118
  !prevIosScreenShareStartedFromSystem
72
119
  ) {
73
120
  onScreenShareStartedHandlerRef.current?.();
121
+ if (includeAudio) {
122
+ call?.screenShare.enableScreenShareAudio();
123
+ }
74
124
  call?.screenShare.enable();
75
125
  } else if (
76
126
  !iosScreenShareStartedFromSystem &&
@@ -81,6 +131,8 @@ export const useScreenShareButton = (
81
131
  }
82
132
  }, [
83
133
  call,
134
+ includeAudio,
135
+ screenShareType,
84
136
  iosScreenShareStartedFromSystem,
85
137
  prevIosScreenShareStartedFromSystem,
86
138
  ]);
@@ -92,14 +144,43 @@ export const useScreenShareButton = (
92
144
  'User does not have permissions to stream the screen share media, calling onMissingScreenShareStreamPermission handler if present',
93
145
  );
94
146
  onMissingScreenShareStreamPermission?.();
147
+ return;
95
148
  }
149
+
96
150
  if (!hasPublishedScreenShare) {
97
- if (Platform.OS === 'ios') {
151
+ // Set audio mixing preference before starting screen share
152
+ if (includeAudio) {
153
+ call?.screenShare.enableScreenShareAudio();
154
+ } else {
155
+ try {
156
+ await call?.screenShare.disableScreenShareAudio();
157
+ } catch (error) {
158
+ const logger = videoLoggerSystem.getLogger('useScreenShareButton');
159
+ logger.warn('Failed to disable screen share audio', error);
160
+ }
161
+ }
162
+
163
+ if (Platform.OS === 'ios' && screenShareType === 'inApp') {
164
+ // In-app screen sharing on iOS — uses RPScreenRecorder directly
165
+ try {
166
+ await screenShareAudioMixingManager.startInAppScreenCapture(
167
+ includeAudio,
168
+ );
169
+ await call?.screenShare.enable();
170
+ onScreenShareStartedHandler?.();
171
+ } catch (error) {
172
+ await screenShareAudioMixingManager.stopInAppScreenCapture();
173
+ const logger = videoLoggerSystem.getLogger('useScreenShareButton');
174
+ logger.warn('Failed to start in-app screen capture', error);
175
+ }
176
+ } else if (Platform.OS === 'ios') {
177
+ // Broadcast screen sharing on iOS — shows the system picker
98
178
  const reactTag = findNodeHandle(screenCapturePickerViewiOSRef.current);
99
179
  await NativeModules.ScreenCapturePickerViewManager.show(reactTag);
100
180
  // After this the iOS screen share broadcast started/stopped event will be triggered
101
181
  // and the useEffect listener will handle the rest
102
182
  } else {
183
+ // Android screen sharing
103
184
  try {
104
185
  await call?.screenShare.enable();
105
186
  onScreenShareStartedHandler?.();
@@ -114,6 +195,10 @@ export const useScreenShareButton = (
114
195
  }
115
196
  } else if (hasPublishedScreenShare) {
116
197
  onScreenShareStoppedHandler?.();
198
+ // Stop in-app screen capture if it was active (iOS only)
199
+ if (Platform.OS === 'ios' && screenShareType === 'inApp') {
200
+ await screenShareAudioMixingManager.stopInAppScreenCapture();
201
+ }
117
202
  await call?.screenShare.disable(true);
118
203
  }
119
204
  };
package/src/index.ts CHANGED
@@ -9,6 +9,7 @@ import { registerGlobals } from '@stream-io/react-native-webrtc';
9
9
  import Logger from '@stream-io/react-native-webrtc/src/Logger';
10
10
  import { Platform } from 'react-native';
11
11
  import { registerSDKGlobals } from './utils/internal/registerSDKGlobals';
12
+ import './utils/keepCallAliveHeadlessTask';
12
13
 
13
14
  // We're registering globals, because our video JS client is serving SDKs that use browser based webRTC functions.
14
15
  // This will result in creation of 2 global objects: `window` and `navigator`
@@ -0,0 +1,49 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ const StreamVideoReactNative = NativeModules.StreamVideoReactNative;
4
+
5
+ export class ScreenShareAudioManager {
6
+ /**
7
+ * Starts mixing screen share audio into the microphone audio track.
8
+ * On iOS, this enables audio buffer processing on the prepared mixer.
9
+ * On Android, this registers an audio processor that captures system media
10
+ * audio via AudioPlaybackCaptureConfiguration and mixes it into the mic buffer.
11
+ */
12
+ async startScreenShareAudioMixing(): Promise<void> {
13
+ return StreamVideoReactNative?.startScreenShareAudioMixing();
14
+ }
15
+
16
+ /**
17
+ * Stops mixing screen share audio into the microphone audio track
18
+ * and restores the original audio pipeline.
19
+ */
20
+ async stopScreenShareAudioMixing(): Promise<void> {
21
+ return StreamVideoReactNative?.stopScreenShareAudioMixing();
22
+ }
23
+
24
+ /**
25
+ * Starts in-app screen capture using RPScreenRecorder (iOS only).
26
+ * Unlike broadcast screen sharing, in-app capture runs in the main app process
27
+ * and can directly provide `.audioApp` sample buffers for mixing.
28
+ *
29
+ * @param includeAudio Whether to capture and mix app audio.
30
+ */
31
+ async startInAppScreenCapture(includeAudio: boolean): Promise<void> {
32
+ if (Platform.OS !== 'ios') {
33
+ return;
34
+ }
35
+ return StreamVideoReactNative?.startInAppScreenCapture(includeAudio);
36
+ }
37
+
38
+ /**
39
+ * Stops in-app screen capture (iOS only).
40
+ */
41
+ async stopInAppScreenCapture(): Promise<void> {
42
+ if (Platform.OS !== 'ios') {
43
+ return;
44
+ }
45
+ return StreamVideoReactNative?.stopInAppScreenCapture();
46
+ }
47
+ }
48
+
49
+ export const screenShareAudioMixingManager = new ScreenShareAudioManager();
@@ -1,7 +1,10 @@
1
1
  import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
2
2
  import { AudioDeviceStatus, StreamInCallManagerConfig } from './types';
3
+ import { getCallingxLibIfAvailable } from '../../utils/push/libs/callingx';
4
+ import { videoLoggerSystem } from '@stream-io/video-client';
3
5
 
4
6
  const NativeManager = NativeModules.StreamInCallManager;
7
+ const CallingxModule = getCallingxLibIfAvailable();
5
8
 
6
9
  const invariant = (condition: boolean, message: string) => {
7
10
  if (!condition) throw new Error(message);
@@ -72,6 +75,19 @@ class SpeakerManager {
72
75
  };
73
76
  }
74
77
 
78
+ const shouldBypassForCallKit = (): boolean => {
79
+ if (Platform.OS !== 'ios') {
80
+ return false;
81
+ }
82
+ if (!CallingxModule) {
83
+ return false;
84
+ }
85
+ return (
86
+ CallingxModule.isSetup &&
87
+ (CallingxModule.hasRegisteredCall() || CallingxModule.isOngoingCallsEnabled)
88
+ );
89
+ };
90
+
75
91
  export class CallManager {
76
92
  android = new AndroidCallManager();
77
93
  ios = new IOSCallManager();
@@ -95,6 +111,14 @@ export class CallManager {
95
111
  * @param config.enableStereoAudioOutput Whether to enable stereo audio output. Only supported for listener audio role.
96
112
  */
97
113
  start = (config?: StreamInCallManagerConfig): void => {
114
+ if (shouldBypassForCallKit()) {
115
+ videoLoggerSystem
116
+ .getLogger('CallManager')
117
+ .debug(
118
+ 'start: skipping start as callkit is handling the audio session',
119
+ );
120
+ return;
121
+ }
98
122
  NativeManager.setAudioRole(config?.audioRole ?? 'communicator');
99
123
  if (config?.audioRole === 'communicator') {
100
124
  const type = config.deviceEndpointType ?? 'speaker';
@@ -110,6 +134,12 @@ export class CallManager {
110
134
  * Stops the in call manager.
111
135
  */
112
136
  stop = (): void => {
137
+ if (shouldBypassForCallKit()) {
138
+ videoLoggerSystem
139
+ .getLogger('CallManager')
140
+ .debug('stop: skipping stop as callkit is handling the audio session');
141
+ return;
142
+ }
113
143
  NativeManager.stop();
114
144
  };
115
145
 
@@ -118,4 +148,10 @@ export class CallManager {
118
148
  * in the native layer.
119
149
  */
120
150
  logAudioState = (): void => NativeManager.logAudioState();
151
+
152
+ /**
153
+ * For debugging purposes, returns the current audio state as a string.
154
+ * @returns A string containing the current audio state information.
155
+ */
156
+ getAudioStateLog = (): string => NativeManager.getAudioStateLog();
121
157
  }
@@ -82,6 +82,13 @@ export interface CallManager extends NativeModule {
82
82
  * Meant for debugging purposes.
83
83
  */
84
84
  logAudioState: () => void;
85
+
86
+ /**
87
+ * Get the current audio state as a string.
88
+ * Meant for debugging purposes.
89
+ * @returns A string containing the current audio state information.
90
+ */
91
+ getAudioStateLog: () => string;
85
92
  }
86
93
 
87
94
  declare module 'react-native' {
@@ -1,12 +1,13 @@
1
1
  import { StreamCallProvider } from '@stream-io/video-react-bindings';
2
2
  import React, { type PropsWithChildren, useEffect } from 'react';
3
3
  import { Call } from '@stream-io/video-client';
4
- import { useIosCallkeepWithCallingStateEffect } from '../../hooks/push/useIosCallkeepWithCallingStateEffect';
5
4
  import { canAddPushWSSubscriptionsRef } from '../../utils/push/internal/utils';
6
5
  import { useAndroidKeepCallAliveEffect } from '../../hooks/useAndroidKeepCallAliveEffect';
6
+ import { useScreenShareAudioMixing } from '../../hooks/useScreenShareAudioMixing';
7
7
  import { AppStateListener } from './AppStateListener';
8
8
  import { DeviceStats } from './DeviceStats';
9
9
  import { pushUnsubscriptionCallbacks } from '../../utils/push/internal/constants';
10
+ import { useCallingExpWithCallingStateEffect } from '../../hooks/push/useCallingExpWithCallingStateEffect';
10
11
 
11
12
  // const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
12
13
 
@@ -34,8 +35,9 @@ export const StreamCall = ({
34
35
  <StreamCallProvider call={call}>
35
36
  <AppStateListener />
36
37
  <AndroidKeepCallAlive />
37
- <IosInformCallkeepCallEnd />
38
+ <CallingExpWithCallingState />
38
39
  <ClearPushWSSubscriptions />
40
+ <ScreenShareAudioMixer />
39
41
  <DeviceStats />
40
42
  {children}
41
43
  </StreamCallProvider>
@@ -52,11 +54,20 @@ const AndroidKeepCallAlive = () => {
52
54
  };
53
55
 
54
56
  /**
55
- * This is a renderless component to end the call in callkeep for ios.
56
- * useAndroidKeepCallAliveEffect needs to called inside a child of StreamCallProvider.
57
+ * This is a renderless component to sync state between stream call and CallKit/Telecom.
58
+ * useCallingExpWithCallingStateEffect needs to called inside a child of StreamCallProvider.
59
+ */
60
+ const CallingExpWithCallingState = () => {
61
+ useCallingExpWithCallingStateEffect();
62
+ return null;
63
+ };
64
+
65
+ /**
66
+ * This is a renderless component to manage screen share audio mixing lifecycle.
67
+ * It starts/stops native audio mixing based on screen share status and audio preference.
57
68
  */
58
- const IosInformCallkeepCallEnd = () => {
59
- useIosCallkeepWithCallingStateEffect();
69
+ const ScreenShareAudioMixer = () => {
70
+ useScreenShareAudioMixing();
60
71
  return null;
61
72
  };
62
73
 
@@ -3,9 +3,14 @@ import pushLogoutCallbacks from '../internal/pushLogoutCallback';
3
3
  import newNotificationCallbacks, {
4
4
  type NewCallNotificationCallback,
5
5
  } from '../internal/newNotificationCallbacks';
6
- import { setupIosCallKeepEvents } from '../push/setupIosCallKeepEvents';
7
6
  import { setupIosVoipPushEvents } from '../push/setupIosVoipPushEvents';
7
+ import { setupCallingExpEvents } from '../push/setupCallingExpEvents';
8
+ import {
9
+ extractCallingExpOptions,
10
+ getCallingxLib,
11
+ } from '../push/libs/callingx';
8
12
  import { NativeModules, Platform } from 'react-native';
13
+ import { videoLoggerSystem } from '@stream-io/video-client';
9
14
 
10
15
  // Utility type for deep partial
11
16
  type DeepPartial<T> = {
@@ -47,10 +52,7 @@ const DEFAULT_STREAM_VIDEO_CONFIG: StreamVideoConfig = {
47
52
  android: {
48
53
  channel: {
49
54
  id: 'stream_call_foreground_service',
50
- name: 'To keep calls alive',
51
- lights: false,
52
- vibration: false,
53
- importance: 3,
55
+ name: 'Ongoing calls',
54
56
  },
55
57
  notificationTexts: {
56
58
  title: 'Call in progress',
@@ -76,20 +78,6 @@ export class StreamVideoRN {
76
78
  this.config = deepMerge(this.config, updateConfig);
77
79
  }
78
80
 
79
- static updateAndroidIncomingCallChannel(
80
- updateChannel: Partial<
81
- NonNullable<StreamVideoConfig['push']>['android']['incomingCallChannel']
82
- >,
83
- ) {
84
- const prevChannel = this.config.push?.android?.incomingCallChannel;
85
- if (prevChannel) {
86
- this.config.push!.android.incomingCallChannel = {
87
- ...prevChannel,
88
- ...updateChannel,
89
- };
90
- }
91
- }
92
-
93
81
  /**
94
82
  * Set the push config for StreamVideoRN.
95
83
  * This method must be called **outside** of your application lifecycle, e.g. alongside your
@@ -102,7 +90,28 @@ export class StreamVideoRN {
102
90
  * import App from './App';
103
91
  * // Set push config
104
92
  * const pushConfig = {}; // construct your config
105
- * StreamVideoRN.setPushConfig(pushConfig);
93
+ * // Set CallKit/Android Telecom API integration options. All params are optional. If not provided, the default values will be used.
94
+ * const callingExpOptions = {
95
+ * ios: {
96
+ * callsHistory: true,
97
+ * displayCallTimeout: 60000,
98
+ * sound: 'ringtone',
99
+ * imageName: 'callkit_icon',
100
+ * },
101
+ * android: {
102
+ * incomingChannel: {
103
+ * id: 'stream_incoming_call_notifications',
104
+ * name: 'Call notifications',
105
+ * vibration: true,
106
+ * sound: 'default',
107
+ * },
108
+ * titleTransformer: (memberName: string, incoming: boolean) =>
109
+ * incoming
110
+ * ? `${memberName} is calling you`
111
+ * : `You are calling ${memberName}`,
112
+ * },
113
+ * };
114
+ * StreamVideoRN.setPushConfig(pushConfig, callingExpOptions);
106
115
  * AppRegistry.registerComponent('app', () => App);
107
116
  */
108
117
  static setPushConfig(pushConfig: NonNullable<StreamVideoConfig['push']>) {
@@ -110,20 +119,23 @@ export class StreamVideoRN {
110
119
  // Ignoring this config as push config was already set
111
120
  return;
112
121
  }
113
- if (
114
- __DEV__ &&
115
- (pushConfig.navigateAcceptCall || pushConfig.navigateToIncomingCall)
116
- ) {
122
+
123
+ this.config.push = pushConfig;
124
+
125
+ try {
126
+ const callingx = getCallingxLib();
127
+ videoLoggerSystem
128
+ .getLogger('StreamVideoRN.setPushConfig')
129
+ .info(JSON.stringify(this.config));
130
+ const options = extractCallingExpOptions(this.config);
131
+ callingx.setup(options);
132
+ } catch {
117
133
  throw new Error(
118
- `Support for navigateAcceptCall or navigateToIncomingCall in pushConfig has been removed.
119
- Please watch for incoming and outgoing calls in the root component of your app.
120
- Please see https://getstream.io/video/docs/react-native/advanced/ringing-calls/#watch-for-incoming-and-outgoing-calls for more information.`,
134
+ 'react-native-callingx library is not installed. Please check the installation instructions: https://getstream.io/video/docs/react-native/incoming-calls/ringing-setup/react-native/.',
121
135
  );
122
136
  }
123
137
 
124
- this.config.push = pushConfig;
125
-
126
- setupIosCallKeepEvents(pushConfig);
138
+ setupCallingExpEvents(pushConfig);
127
139
  setupIosVoipPushEvents(pushConfig);
128
140
  }
129
141