@stream-io/video-react-native-sdk 1.30.0 → 1.30.1-beta.1

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 (208) hide show
  1. package/android/src/main/AndroidManifest.xml +8 -1
  2. package/android/src/main/AndroidManifestNew.xml +11 -0
  3. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +42 -5
  4. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/WebRtcAudioUtils.kt +70 -6
  5. package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +6 -4
  6. package/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt +83 -0
  7. package/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt +149 -0
  8. package/dist/commonjs/hooks/push/index.js +0 -2
  9. package/dist/commonjs/hooks/push/index.js.map +1 -1
  10. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js +121 -0
  11. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -0
  12. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +18 -31
  13. package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  14. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js +64 -97
  15. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  16. package/dist/commonjs/index.js +1 -0
  17. package/dist/commonjs/index.js.map +1 -1
  18. package/dist/commonjs/modules/call-manager/CallManager.js +26 -0
  19. package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -1
  20. package/dist/commonjs/providers/StreamCall/index.js +6 -6
  21. package/dist/commonjs/providers/StreamCall/index.js.map +1 -1
  22. package/dist/commonjs/utils/StreamVideoRN/index.js +33 -21
  23. package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
  24. package/dist/commonjs/utils/internal/callingx/audioSessionPromise.js +68 -0
  25. package/dist/commonjs/utils/internal/callingx/audioSessionPromise.js.map +1 -0
  26. package/dist/commonjs/utils/internal/callingx/callingx.js +123 -0
  27. package/dist/commonjs/utils/internal/callingx/callingx.js.map +1 -0
  28. package/dist/commonjs/utils/internal/registerSDKGlobals.js +52 -3
  29. package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -1
  30. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js +48 -0
  31. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js.map +1 -0
  32. package/dist/commonjs/utils/push/android.js +135 -202
  33. package/dist/commonjs/utils/push/android.js.map +1 -1
  34. package/dist/commonjs/utils/push/internal/ios.js +17 -34
  35. package/dist/commonjs/utils/push/internal/ios.js.map +1 -1
  36. package/dist/commonjs/utils/push/internal/rxSubjects.js +1 -45
  37. package/dist/commonjs/utils/push/internal/rxSubjects.js.map +1 -1
  38. package/dist/commonjs/utils/push/internal/utils.js +67 -52
  39. package/dist/commonjs/utils/push/internal/utils.js.map +1 -1
  40. package/dist/commonjs/utils/push/ios.js.map +1 -1
  41. package/dist/commonjs/utils/push/libs/callingx.js +78 -0
  42. package/dist/commonjs/utils/push/libs/callingx.js.map +1 -0
  43. package/dist/commonjs/utils/push/libs/index.js +8 -19
  44. package/dist/commonjs/utils/push/libs/index.js.map +1 -1
  45. package/dist/commonjs/utils/push/libs/notifee/index.js +0 -19
  46. package/dist/commonjs/utils/push/libs/notifee/index.js.map +1 -1
  47. package/dist/commonjs/utils/push/setupCallingExpEvents.js +105 -0
  48. package/dist/commonjs/utils/push/setupCallingExpEvents.js.map +1 -0
  49. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js +7 -6
  50. package/dist/commonjs/utils/push/setupIosVoipPushEvents.js.map +1 -1
  51. package/dist/commonjs/version.js +1 -1
  52. package/dist/commonjs/version.js.map +1 -1
  53. package/dist/module/hooks/push/index.js +0 -2
  54. package/dist/module/hooks/push/index.js.map +1 -1
  55. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js +114 -0
  56. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -0
  57. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +18 -31
  58. package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
  59. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js +66 -99
  60. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  61. package/dist/module/index.js +1 -0
  62. package/dist/module/index.js.map +1 -1
  63. package/dist/module/modules/call-manager/CallManager.js +26 -0
  64. package/dist/module/modules/call-manager/CallManager.js.map +1 -1
  65. package/dist/module/providers/StreamCall/index.js +6 -6
  66. package/dist/module/providers/StreamCall/index.js.map +1 -1
  67. package/dist/module/utils/StreamVideoRN/index.js +33 -21
  68. package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
  69. package/dist/module/utils/internal/callingx/audioSessionPromise.js +61 -0
  70. package/dist/module/utils/internal/callingx/audioSessionPromise.js.map +1 -0
  71. package/dist/module/utils/internal/callingx/callingx.js +114 -0
  72. package/dist/module/utils/internal/callingx/callingx.js.map +1 -0
  73. package/dist/module/utils/internal/registerSDKGlobals.js +52 -3
  74. package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -1
  75. package/dist/module/utils/keepCallAliveHeadlessTask.js +42 -0
  76. package/dist/module/utils/keepCallAliveHeadlessTask.js.map +1 -0
  77. package/dist/module/utils/push/android.js +137 -204
  78. package/dist/module/utils/push/android.js.map +1 -1
  79. package/dist/module/utils/push/internal/ios.js +17 -34
  80. package/dist/module/utils/push/internal/ios.js.map +1 -1
  81. package/dist/module/utils/push/internal/rxSubjects.js +0 -44
  82. package/dist/module/utils/push/internal/rxSubjects.js.map +1 -1
  83. package/dist/module/utils/push/internal/utils.js +63 -49
  84. package/dist/module/utils/push/internal/utils.js.map +1 -1
  85. package/dist/module/utils/push/ios.js.map +1 -1
  86. package/dist/module/utils/push/libs/callingx.js +70 -0
  87. package/dist/module/utils/push/libs/callingx.js.map +1 -0
  88. package/dist/module/utils/push/libs/index.js +1 -2
  89. package/dist/module/utils/push/libs/index.js.map +1 -1
  90. package/dist/module/utils/push/libs/notifee/index.js +0 -18
  91. package/dist/module/utils/push/libs/notifee/index.js.map +1 -1
  92. package/dist/module/utils/push/setupCallingExpEvents.js +99 -0
  93. package/dist/module/utils/push/setupCallingExpEvents.js.map +1 -0
  94. package/dist/module/utils/push/setupIosVoipPushEvents.js +7 -6
  95. package/dist/module/utils/push/setupIosVoipPushEvents.js.map +1 -1
  96. package/dist/module/version.js +1 -1
  97. package/dist/module/version.js.map +1 -1
  98. package/dist/typescript/hooks/push/index.d.ts.map +1 -1
  99. package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts +5 -0
  100. package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts.map +1 -0
  101. package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
  102. package/dist/typescript/hooks/useAndroidKeepCallAliveEffect.d.ts.map +1 -1
  103. package/dist/typescript/index.d.ts +1 -0
  104. package/dist/typescript/index.d.ts.map +1 -1
  105. package/dist/typescript/modules/call-manager/CallManager.d.ts +5 -0
  106. package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -1
  107. package/dist/typescript/utils/StreamVideoRN/index.d.ts +20 -2
  108. package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
  109. package/dist/typescript/utils/StreamVideoRN/types.d.ts +63 -25
  110. package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
  111. package/dist/typescript/utils/internal/callingx/audioSessionPromise.d.ts +16 -0
  112. package/dist/typescript/utils/internal/callingx/audioSessionPromise.d.ts.map +1 -0
  113. package/dist/typescript/utils/internal/callingx/callingx.d.ts +17 -0
  114. package/dist/typescript/utils/internal/callingx/callingx.d.ts.map +1 -0
  115. package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -1
  116. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts +10 -0
  117. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts.map +1 -0
  118. package/dist/typescript/utils/push/android.d.ts +1 -2
  119. package/dist/typescript/utils/push/android.d.ts.map +1 -1
  120. package/dist/typescript/utils/push/internal/ios.d.ts.map +1 -1
  121. package/dist/typescript/utils/push/internal/rxSubjects.d.ts +0 -33
  122. package/dist/typescript/utils/push/internal/rxSubjects.d.ts.map +1 -1
  123. package/dist/typescript/utils/push/internal/utils.d.ts +14 -8
  124. package/dist/typescript/utils/push/internal/utils.d.ts.map +1 -1
  125. package/dist/typescript/utils/push/ios.d.ts +1 -2
  126. package/dist/typescript/utils/push/ios.d.ts.map +1 -1
  127. package/dist/typescript/utils/push/libs/callingx.d.ts +9 -0
  128. package/dist/typescript/utils/push/libs/callingx.d.ts.map +1 -0
  129. package/dist/typescript/utils/push/libs/index.d.ts +1 -2
  130. package/dist/typescript/utils/push/libs/index.d.ts.map +1 -1
  131. package/dist/typescript/utils/push/libs/notifee/index.d.ts +0 -1
  132. package/dist/typescript/utils/push/libs/notifee/index.d.ts.map +1 -1
  133. package/dist/typescript/utils/push/setupCallingExpEvents.d.ts +8 -0
  134. package/dist/typescript/utils/push/setupCallingExpEvents.d.ts.map +1 -0
  135. package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts.map +1 -1
  136. package/dist/typescript/version.d.ts +1 -1
  137. package/dist/typescript/version.d.ts.map +1 -1
  138. package/expo-config-plugin/dist/withAndroidManifest.js +1 -33
  139. package/expo-config-plugin/dist/withAndroidPermissions.js +2 -7
  140. package/expo-config-plugin/dist/withAppDelegate.js +19 -197
  141. package/expo-config-plugin/dist/withMainActivity.js +1 -1
  142. package/expo-config-plugin/dist/withiOSInfoPlist.js +2 -3
  143. package/ios/StreamInCallManager.m +2 -0
  144. package/ios/StreamInCallManager.swift +19 -7
  145. package/ios/StreamVideoReactNative.h +7 -4
  146. package/ios/StreamVideoReactNative.m +189 -82
  147. package/package.json +13 -18
  148. package/src/hooks/push/index.ts +0 -2
  149. package/src/hooks/push/useCallingExpWithCallingStateEffect.ts +147 -0
  150. package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +21 -34
  151. package/src/hooks/useAndroidKeepCallAliveEffect.ts +94 -120
  152. package/src/index.ts +1 -0
  153. package/src/modules/call-manager/CallManager.ts +36 -0
  154. package/src/modules/call-manager/native-module.d.ts +7 -0
  155. package/src/providers/StreamCall/index.tsx +6 -6
  156. package/src/utils/StreamVideoRN/index.ts +40 -30
  157. package/src/utils/StreamVideoRN/types.ts +65 -25
  158. package/src/utils/internal/callingx/audioSessionPromise.ts +65 -0
  159. package/src/utils/internal/callingx/callingx.ts +165 -0
  160. package/src/utils/internal/registerSDKGlobals.ts +47 -4
  161. package/src/utils/keepCallAliveHeadlessTask.ts +54 -0
  162. package/src/utils/push/android.ts +196 -311
  163. package/src/utils/push/internal/ios.ts +28 -44
  164. package/src/utils/push/internal/rxSubjects.ts +0 -61
  165. package/src/utils/push/internal/utils.ts +104 -63
  166. package/src/utils/push/ios.ts +1 -6
  167. package/src/utils/push/libs/callingx.ts +93 -0
  168. package/src/utils/push/libs/index.ts +1 -2
  169. package/src/utils/push/libs/notifee/index.ts +0 -27
  170. package/src/utils/push/setupCallingExpEvents.ts +135 -0
  171. package/src/utils/push/setupIosVoipPushEvents.ts +11 -7
  172. package/src/version.ts +1 -1
  173. package/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt +0 -95
  174. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js +0 -160
  175. package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +0 -1
  176. package/dist/commonjs/hooks/push/useProcessPushCallEffect.js +0 -67
  177. package/dist/commonjs/hooks/push/useProcessPushCallEffect.js.map +0 -1
  178. package/dist/commonjs/utils/push/libs/callkeep.js +0 -17
  179. package/dist/commonjs/utils/push/libs/callkeep.js.map +0 -1
  180. package/dist/commonjs/utils/push/libs/voipPushNotification.js +0 -17
  181. package/dist/commonjs/utils/push/libs/voipPushNotification.js.map +0 -1
  182. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js +0 -205
  183. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js.map +0 -1
  184. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js +0 -153
  185. package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +0 -1
  186. package/dist/module/hooks/push/useProcessPushCallEffect.js +0 -60
  187. package/dist/module/hooks/push/useProcessPushCallEffect.js.map +0 -1
  188. package/dist/module/utils/push/libs/callkeep.js +0 -11
  189. package/dist/module/utils/push/libs/callkeep.js.map +0 -1
  190. package/dist/module/utils/push/libs/voipPushNotification.js +0 -11
  191. package/dist/module/utils/push/libs/voipPushNotification.js.map +0 -1
  192. package/dist/module/utils/push/setupIosCallKeepEvents.js +0 -199
  193. package/dist/module/utils/push/setupIosCallKeepEvents.js.map +0 -1
  194. package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts +0 -5
  195. package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts.map +0 -1
  196. package/dist/typescript/hooks/push/useProcessPushCallEffect.d.ts +0 -8
  197. package/dist/typescript/hooks/push/useProcessPushCallEffect.d.ts.map +0 -1
  198. package/dist/typescript/utils/push/libs/callkeep.d.ts +0 -3
  199. package/dist/typescript/utils/push/libs/callkeep.d.ts.map +0 -1
  200. package/dist/typescript/utils/push/libs/voipPushNotification.d.ts +0 -3
  201. package/dist/typescript/utils/push/libs/voipPushNotification.d.ts.map +0 -1
  202. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts +0 -6
  203. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts.map +0 -1
  204. package/src/hooks/push/useIosCallkeepWithCallingStateEffect.ts +0 -235
  205. package/src/hooks/push/useProcessPushCallEffect.ts +0 -108
  206. package/src/utils/push/libs/callkeep.ts +0 -16
  207. package/src/utils/push/libs/voipPushNotification.ts +0 -17
  208. package/src/utils/push/setupIosCallKeepEvents.ts +0 -252
@@ -5,76 +5,48 @@ import {
5
5
  AppState,
6
6
  type AppStateStatus,
7
7
  NativeModules,
8
+ PermissionsAndroid,
8
9
  Platform,
9
10
  } from 'react-native';
10
- import { Call, CallingState, videoLoggerSystem } from '@stream-io/video-client';
11
- import {
12
- getKeepCallAliveForegroundServiceTypes,
13
- getNotifeeLibNoThrowForKeepCallAlive,
14
- } from '../utils/push/libs/notifee';
11
+ import { CallingState, videoLoggerSystem } from '@stream-io/video-client';
12
+ import { keepCallAliveCallRef } from '../utils/keepCallAliveHeadlessTask';
13
+ import { getNotifeeLibNoThrowForKeepCallAlive } from '../utils/push/libs/notifee';
14
+ import { getCallingxLibIfAvailable } from '../utils/push/libs';
15
15
 
16
16
  const notifeeLib = getNotifeeLibNoThrowForKeepCallAlive();
17
- const callToPassToForegroundService: { current: Call | undefined } = {
18
- current: undefined,
19
- };
20
17
 
21
- function setForegroundService() {
22
- if (Platform.OS === 'ios' || !notifeeLib) return;
23
- NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(
24
- (isConfigured: boolean) => {
25
- if (!isConfigured) {
26
- const logger = videoLoggerSystem.getLogger(
27
- 'setForegroundService method',
28
- );
29
- logger.info(
30
- 'KeepCallAlive is not configured. Skipping foreground service setup.',
31
- );
32
- return;
33
- }
34
- notifeeLib.default.registerForegroundService(() => {
35
- const task = new Promise((resolve) => {
36
- const logger = videoLoggerSystem.getLogger(
37
- 'setForegroundService method',
38
- );
39
- logger.info('Foreground service running for call in progress');
40
- // any task to run from SDK in the foreground service must be added
41
- resolve(true);
42
- });
43
- const videoConfig = StreamVideoRN.getConfig();
44
- const foregroundServiceConfig = videoConfig.foregroundService;
45
- const { taskToRun } = foregroundServiceConfig.android;
46
- const call = callToPassToForegroundService.current;
47
- if (!call) {
48
- const logger = videoLoggerSystem.getLogger(
49
- 'setForegroundService method',
50
- );
51
- logger.warn('No call to pass to foreground service');
52
- return task.then(() => new Promise(() => {}));
53
- }
54
- callToPassToForegroundService.current = undefined;
55
- return task.then(() => taskToRun(call));
56
- });
57
- },
58
- );
18
+ async function stopForegroundServiceNoThrow() {
19
+ const logger = videoLoggerSystem.getLogger('stopForegroundServiceNoThrow');
20
+ try {
21
+ await NativeModules.StreamVideoReactNative.stopKeepCallAliveService();
22
+ } catch (e) {
23
+ logger.warn('Failed to stop keep-call-alive foreground service', e);
24
+ }
59
25
  }
60
26
 
61
27
  async function startForegroundService(call_cid: string) {
62
- const isCallAliveConfigured =
63
- await NativeModules.StreamVideoReactNative.isCallAliveConfigured();
28
+ const logger = videoLoggerSystem.getLogger('startForegroundService');
29
+ const isCallAliveConfigured = await (async () => {
30
+ try {
31
+ return await NativeModules.StreamVideoReactNative.isCallAliveConfigured();
32
+ } catch (e) {
33
+ logger.warn('Failed to check whether KeepCallAlive is configured', e);
34
+ return false;
35
+ }
36
+ })();
64
37
  if (!isCallAliveConfigured) {
65
- const logger = videoLoggerSystem.getLogger('startForegroundService');
66
38
  logger.info(
67
39
  'KeepCallAlive is not configured. Skipping foreground service setup.',
68
40
  );
69
41
  return;
70
42
  }
71
- // check for notification permission and then start the foreground service
72
- if (!notifeeLib) return;
73
- const settings = await notifeeLib.default.getNotificationSettings();
74
- if (
75
- settings.authorizationStatus !== notifeeLib.AuthorizationStatus.AUTHORIZED
76
- ) {
77
- const logger = videoLoggerSystem.getLogger('startForegroundService');
43
+ // Check for notification permission (Android 13+) before starting the service.
44
+ const hasPostNotificationsPermission =
45
+ Number(Platform.Version) < 33 ||
46
+ (await PermissionsAndroid.check(
47
+ PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
48
+ ));
49
+ if (!hasPostNotificationsPermission) {
78
50
  logger.info(
79
51
  'Notification permission not granted, can not start foreground service to keep the call alive',
80
52
  );
@@ -83,38 +55,28 @@ async function startForegroundService(call_cid: string) {
83
55
  const videoConfig = StreamVideoRN.getConfig();
84
56
  const foregroundServiceConfig = videoConfig.foregroundService;
85
57
  const notificationTexts = foregroundServiceConfig.android.notificationTexts;
86
- const channelId = foregroundServiceConfig.android.channel.id;
87
- await notifeeLib.default.createChannel(
88
- foregroundServiceConfig.android.channel,
89
- );
90
- const foregroundServiceTypes = await getKeepCallAliveForegroundServiceTypes();
58
+ const channel = foregroundServiceConfig.android.channel;
59
+ const smallIconName = videoConfig.push?.android.smallIcon;
60
+
91
61
  // NOTE: we use requestAnimationFrame to ensure that the foreground service is started after all the current UI operations are done
92
62
  // this is a workaround for the crash - android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()
93
63
  // this crash was reproducible only in some android devices
94
- requestAnimationFrame(() => {
95
- notifeeLib.default.displayNotification({
96
- id: call_cid,
97
- title: notificationTexts.title,
98
- body: notificationTexts.body,
99
- android: {
100
- channelId,
101
- smallIcon: videoConfig.push?.android.smallIcon,
102
- foregroundServiceTypes,
103
- asForegroundService: true,
104
- ongoing: true, // user cannot dismiss the notification
105
- colorized: true,
106
- pressAction: {
107
- id: 'default',
108
- launchActivity: 'default', // open the app when the notification is pressed
109
- },
110
- },
111
- });
64
+ requestAnimationFrame(async () => {
65
+ try {
66
+ await NativeModules.StreamVideoReactNative.startKeepCallAliveService(
67
+ call_cid,
68
+ channel.id,
69
+ channel.name,
70
+ notificationTexts.title,
71
+ notificationTexts.body,
72
+ smallIconName ?? null,
73
+ );
74
+ } catch (e) {
75
+ logger.warn('Failed to start keep-call-alive foreground service', e);
76
+ }
112
77
  });
113
78
  }
114
79
 
115
- // flag to check if setForegroundService has already been run once
116
- let isSetForegroundServiceRan = false;
117
-
118
80
  /**
119
81
  * This hook is used to keep the call alive in the background for Android.
120
82
  * It starts a foreground service to keep the call alive as soon as the call is joined
@@ -125,7 +87,7 @@ export const useAndroidKeepCallAliveEffect = () => {
125
87
  const foregroundServiceStartedRef = useRef(false);
126
88
 
127
89
  const call = useCall();
128
- callToPassToForegroundService.current = call;
90
+ keepCallAliveCallRef.current = call;
129
91
  const activeCallCid = call?.cid;
130
92
  const { useCallCallingState } = useCallStateHooks();
131
93
  const callingState = useCallCallingState();
@@ -133,6 +95,7 @@ export const useAndroidKeepCallAliveEffect = () => {
133
95
  const isOutgoingCall =
134
96
  callingState === CallingState.RINGING && call?.isCreatedByMe;
135
97
  const isCallJoined = callingState === CallingState.JOINED;
98
+ const isRingingCall = call?.ringing;
136
99
 
137
100
  const shouldStartForegroundService =
138
101
  !foregroundServiceStartedRef.current && (isOutgoingCall || isCallJoined);
@@ -141,7 +104,14 @@ export const useAndroidKeepCallAliveEffect = () => {
141
104
  if (Platform.OS === 'ios' || !activeCallCid) {
142
105
  return undefined;
143
106
  }
144
- if (!notifeeLib) return undefined;
107
+
108
+ const callingx = getCallingxLibIfAvailable();
109
+ if (
110
+ callingx?.isSetup &&
111
+ (isRingingCall || (!isRingingCall && callingx?.isOngoingCallsEnabled))
112
+ ) {
113
+ return undefined;
114
+ }
145
115
 
146
116
  // start foreground service as soon as the call is joined
147
117
  if (shouldStartForegroundService) {
@@ -149,23 +119,21 @@ export const useAndroidKeepCallAliveEffect = () => {
149
119
  if (foregroundServiceStartedRef.current) {
150
120
  return;
151
121
  }
152
- if (!isSetForegroundServiceRan) {
153
- isSetForegroundServiceRan = true;
154
- setForegroundService();
155
- }
156
- const notifee = notifeeLib.default;
157
- const displayedNotifications =
158
- await notifee.getDisplayedNotifications();
159
- const activeCallNotification = displayedNotifications.find(
160
- (notification) => notification.id === activeCallCid,
161
- );
162
- if (activeCallNotification) {
163
- callToPassToForegroundService.current = undefined;
164
- // this means that we have a incoming call notification shown as foreground service and we must stop it
165
- notifee.stopForegroundService();
166
- notifee.cancelDisplayedNotification(activeCallCid);
122
+ // Optional compatibility cleanup: if the app uses Notifee for ringing push,
123
+ // we might have an incoming call notification running as a foreground service.
124
+ if (notifeeLib) {
125
+ const notifee = notifeeLib.default;
126
+ const displayedNotifications =
127
+ await notifee.getDisplayedNotifications();
128
+ const activeCallNotification = displayedNotifications.find(
129
+ (notification) => notification.id === activeCallCid,
130
+ );
131
+ if (activeCallNotification) {
132
+ // this means that we have a incoming call notification shown as foreground service and we must stop it
133
+ notifee.stopForegroundService();
134
+ notifee.cancelDisplayedNotification(activeCallCid);
135
+ }
167
136
  }
168
- // check for notification permission and then start the foreground service
169
137
 
170
138
  await startForegroundService(activeCallCid);
171
139
  foregroundServiceStartedRef.current = true;
@@ -174,7 +142,6 @@ export const useAndroidKeepCallAliveEffect = () => {
174
142
  // ensure that app is active before running the function
175
143
  if (AppState.currentState === 'active') {
176
144
  run();
177
- return undefined;
178
145
  }
179
146
  const sub = AppState.addEventListener(
180
147
  'change',
@@ -192,42 +159,49 @@ export const useAndroidKeepCallAliveEffect = () => {
192
159
  return () => {
193
160
  // cancel any notifee displayed notification when the call has transitioned out of ringing
194
161
  // NOTE: cancels only the non fg service notifications
195
- notifeeLib.default.cancelDisplayedNotification(activeCallCid);
162
+ if (notifeeLib) {
163
+ notifeeLib.default.cancelDisplayedNotification(activeCallCid);
164
+ }
196
165
  };
197
166
  } else if (
198
167
  callingState === CallingState.IDLE ||
199
168
  callingState === CallingState.LEFT
200
169
  ) {
201
170
  if (foregroundServiceStartedRef.current) {
202
- callToPassToForegroundService.current = undefined;
171
+ keepCallAliveCallRef.current = undefined;
203
172
  // stop foreground service when the call is not active
204
- notifeeLib.default.stopForegroundService();
173
+ stopForegroundServiceNoThrow();
205
174
  foregroundServiceStartedRef.current = false;
206
175
  } else {
207
- notifeeLib.default
208
- .getDisplayedNotifications()
209
- .then((displayedNotifications) => {
210
- const activeCallNotification = displayedNotifications.find(
211
- (notification) => notification.id === activeCallCid,
212
- );
213
- if (activeCallNotification) {
214
- callToPassToForegroundService.current = undefined;
215
- // this means that we have a incoming call notification shown as foreground service and we must stop it
216
- notifeeLib.default.stopForegroundService();
217
- }
218
- });
176
+ if (notifeeLib) {
177
+ notifeeLib.default
178
+ .getDisplayedNotifications()
179
+ .then((displayedNotifications) => {
180
+ const activeCallNotification = displayedNotifications.find(
181
+ (notification) => notification.id === activeCallCid,
182
+ );
183
+ if (activeCallNotification) {
184
+ // this means that we have a incoming call notification shown as foreground service and we must stop it
185
+ notifeeLib.default.stopForegroundService();
186
+ }
187
+ });
188
+ }
219
189
  }
220
190
  }
221
191
  return undefined;
222
- }, [activeCallCid, callingState, shouldStartForegroundService]);
192
+ }, [
193
+ activeCallCid,
194
+ callingState,
195
+ shouldStartForegroundService,
196
+ isRingingCall,
197
+ ]);
223
198
 
224
199
  useEffect(() => {
225
200
  return () => {
226
201
  // stop foreground service when this effect is unmounted
227
202
  if (foregroundServiceStartedRef.current) {
228
- if (!notifeeLib) return;
229
- callToPassToForegroundService.current = undefined;
230
- notifeeLib.default.stopForegroundService();
203
+ keepCallAliveCallRef.current = undefined;
204
+ stopForegroundServiceNoThrow();
231
205
  foregroundServiceStartedRef.current = false;
232
206
  }
233
207
  };
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`
@@ -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,12 @@
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';
7
6
  import { AppStateListener } from './AppStateListener';
8
7
  import { DeviceStats } from './DeviceStats';
9
8
  import { pushUnsubscriptionCallbacks } from '../../utils/push/internal/constants';
9
+ import { useCallingExpWithCallingStateEffect } from '../../hooks/push/useCallingExpWithCallingStateEffect';
10
10
 
11
11
  // const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
12
12
 
@@ -34,7 +34,7 @@ export const StreamCall = ({
34
34
  <StreamCallProvider call={call}>
35
35
  <AppStateListener />
36
36
  <AndroidKeepCallAlive />
37
- <IosInformCallkeepCallEnd />
37
+ <CallingExpWithCallingState />
38
38
  <ClearPushWSSubscriptions />
39
39
  <DeviceStats />
40
40
  {children}
@@ -52,11 +52,11 @@ const AndroidKeepCallAlive = () => {
52
52
  };
53
53
 
54
54
  /**
55
- * This is a renderless component to end the call in callkeep for ios.
56
- * useAndroidKeepCallAliveEffect needs to called inside a child of StreamCallProvider.
55
+ * This is a renderless component to sync state between stream call and CallKit/Telecom.
56
+ * useCallingExpWithCallingStateEffect needs to called inside a child of StreamCallProvider.
57
57
  */
58
- const IosInformCallkeepCallEnd = () => {
59
- useIosCallkeepWithCallingStateEffect();
58
+ const CallingExpWithCallingState = () => {
59
+ useCallingExpWithCallingStateEffect();
60
60
  return null;
61
61
  };
62
62
 
@@ -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,26 @@ 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: (text: string) => text,
109
+ * subtitleTransformer: (text: string) => text,
110
+ * },
111
+ * };
112
+ * StreamVideoRN.setPushConfig(pushConfig, callingExpOptions);
106
113
  * AppRegistry.registerComponent('app', () => App);
107
114
  */
108
115
  static setPushConfig(pushConfig: NonNullable<StreamVideoConfig['push']>) {
@@ -110,20 +117,23 @@ export class StreamVideoRN {
110
117
  // Ignoring this config as push config was already set
111
118
  return;
112
119
  }
113
- if (
114
- __DEV__ &&
115
- (pushConfig.navigateAcceptCall || pushConfig.navigateToIncomingCall)
116
- ) {
120
+
121
+ this.config.push = pushConfig;
122
+
123
+ try {
124
+ const callingx = getCallingxLib();
125
+ videoLoggerSystem
126
+ .getLogger('StreamVideoRN.setPushConfig')
127
+ .info(JSON.stringify(this.config));
128
+ const options = extractCallingExpOptions(this.config);
129
+ callingx.setup(options);
130
+ } catch {
117
131
  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.`,
132
+ '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
133
  );
122
134
  }
123
135
 
124
- this.config.push = pushConfig;
125
-
126
- setupIosCallKeepEvents(pushConfig);
136
+ setupCallingExpEvents(pushConfig);
127
137
  setupIosVoipPushEvents(pushConfig);
128
138
  }
129
139