@stream-io/video-react-native-sdk 1.27.4 → 1.28.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 (42) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt +19 -11
  3. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt +23 -9
  4. package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +48 -14
  5. package/dist/commonjs/index.js +2 -0
  6. package/dist/commonjs/index.js.map +1 -1
  7. package/dist/commonjs/modules/call-manager/CallManager.js +5 -0
  8. package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -1
  9. package/dist/commonjs/utils/internal/registerSDKGlobals.js +34 -0
  10. package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -0
  11. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js +44 -0
  12. package/dist/commonjs/utils/push/setupIosCallKeepEvents.js.map +1 -1
  13. package/dist/commonjs/version.js +1 -1
  14. package/dist/module/index.js +2 -0
  15. package/dist/module/index.js.map +1 -1
  16. package/dist/module/modules/call-manager/CallManager.js +5 -0
  17. package/dist/module/modules/call-manager/CallManager.js.map +1 -1
  18. package/dist/module/utils/internal/registerSDKGlobals.js +28 -0
  19. package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -0
  20. package/dist/module/utils/push/setupIosCallKeepEvents.js +44 -0
  21. package/dist/module/utils/push/setupIosCallKeepEvents.js.map +1 -1
  22. package/dist/module/version.js +1 -1
  23. package/dist/typescript/index.d.ts.map +1 -1
  24. package/dist/typescript/modules/call-manager/CallManager.d.ts +2 -0
  25. package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -1
  26. package/dist/typescript/modules/call-manager/types.d.ts +1 -0
  27. package/dist/typescript/modules/call-manager/types.d.ts.map +1 -1
  28. package/dist/typescript/utils/internal/registerSDKGlobals.d.ts +2 -0
  29. package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -0
  30. package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts.map +1 -1
  31. package/dist/typescript/version.d.ts +1 -1
  32. package/ios/StreamInCallManager.m +4 -0
  33. package/ios/StreamInCallManager.swift +213 -88
  34. package/package.json +7 -7
  35. package/src/index.ts +2 -0
  36. package/src/modules/call-manager/CallManager.ts +5 -0
  37. package/src/modules/call-manager/native-module.d.ts +11 -0
  38. package/src/modules/call-manager/types.ts +1 -0
  39. package/src/utils/internal/registerSDKGlobals.ts +30 -0
  40. package/src/utils/push/setupIosCallKeepEvents.ts +49 -0
  41. package/src/version.ts +1 -1
  42. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt +0 -41
@@ -91,6 +91,8 @@ export class CallManager {
91
91
  * @param config.deviceEndpointType The default audio device endpoint type to set. It can be one of the following:
92
92
  * - `'speaker'`: (Default) For normal video or voice calls.
93
93
  * - `'earpiece'`: For voice-only mobile call type scenarios.
94
+ *
95
+ * @param config.enableStereoAudioOutput Whether to enable stereo audio output. Only supported for listener audio role.
94
96
  */
95
97
  start = (config?: StreamInCallManagerConfig): void => {
96
98
  NativeManager.setAudioRole(config?.audioRole ?? 'communicator');
@@ -98,6 +100,9 @@ export class CallManager {
98
100
  const type = config.deviceEndpointType ?? 'speaker';
99
101
  NativeManager.setDefaultAudioDeviceEndpointType(type);
100
102
  }
103
+ if (config?.audioRole === 'listener' && config.enableStereoAudioOutput) {
104
+ NativeManager.setEnableStereoAudioOutput(true);
105
+ }
101
106
  NativeManager.start();
102
107
  };
103
108
 
@@ -51,6 +51,11 @@ export interface CallManager extends NativeModule {
51
51
  */
52
52
  stop: () => void;
53
53
 
54
+ /**
55
+ * Setup the in call manager.
56
+ */
57
+ setup: () => void;
58
+
54
59
  /**
55
60
  * Mutes the speaker
56
61
  */
@@ -66,6 +71,12 @@ export interface CallManager extends NativeModule {
66
71
  */
67
72
  setForceSpeakerphoneOn: (boolean) => void;
68
73
 
74
+ /**
75
+ * Enables or disables stereo audio output.
76
+ * @param enable - Whether to enable stereo audio output.
77
+ */
78
+ setEnableStereoAudioOutput: (enable: boolean) => void;
79
+
69
80
  /**
70
81
  * Log the current audio state natively.
71
82
  * Meant for debugging purposes.
@@ -22,4 +22,5 @@ export type StreamInCallManagerConfig =
22
22
  }
23
23
  | {
24
24
  audioRole: 'listener';
25
+ enableStereoAudioOutput?: boolean;
25
26
  };
@@ -0,0 +1,30 @@
1
+ import { StreamRNVideoSDKGlobals } from '@stream-io/video-client';
2
+ import { NativeModules } from 'react-native';
3
+
4
+ const StreamInCallManagerNativeModule = NativeModules.StreamInCallManager;
5
+
6
+ const streamRNVideoSDKGlobals: StreamRNVideoSDKGlobals = {
7
+ callManager: {
8
+ setup: ({ defaultDevice }) => {
9
+ StreamInCallManagerNativeModule.setDefaultAudioDeviceEndpointType(
10
+ defaultDevice,
11
+ );
12
+ StreamInCallManagerNativeModule.setup();
13
+ },
14
+ start: () => {
15
+ StreamInCallManagerNativeModule.start();
16
+ },
17
+ stop: () => {
18
+ StreamInCallManagerNativeModule.stop();
19
+ },
20
+ },
21
+ };
22
+
23
+ // Note: The global type declaration for `streamRNVideoSDK` is defined in
24
+ // @stream-io/video-client/src/types.ts and is automatically available when
25
+ // importing from the client package.
26
+ export function registerSDKGlobals() {
27
+ if (!global.streamRNVideoSDK) {
28
+ global.streamRNVideoSDK = streamRNVideoSDKGlobals;
29
+ }
30
+ }
@@ -12,6 +12,7 @@ import {
12
12
  processCallFromPushInBackground,
13
13
  } from './internal/utils';
14
14
  import { AppState, NativeModules, Platform } from 'react-native';
15
+ import { RTCAudioSession } from '@stream-io/react-native-webrtc';
15
16
  import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
16
17
 
17
18
  type PushConfig = NonNullable<StreamVideoConfig['push']>;
@@ -75,6 +76,34 @@ export function setupIosCallKeepEvents(
75
76
  });
76
77
  }
77
78
 
79
+ /**
80
+ * CallKeep / CallKit audio-session events -> WebRTC (iOS)
81
+ *
82
+ * iOS CallKit is the authority that *activates* and *deactivates* the underlying `AVAudioSession`
83
+ * when a call is answered/ended from the system UI (lock screen, Call UI, Bluetooth, etc).
84
+ *
85
+ * WebRTC on iOS wraps `AVAudioSession` with `RTCAudioSession` and its AudioDeviceModule relies on
86
+ * being notified of those lifecycle transitions to correctly start/stop audio I/O and keep its
87
+ * internal activation state consistent (e.g. activation count, playout/recording start).
88
+ *
89
+ * If these callbacks don’t reach WebRTC, answering via the native dialer UI can result in:
90
+ * - no microphone capture / one-way audio
91
+ * - silent playout until the app forces an audio reconfiguration
92
+ * - flaky audio routing (speaker/earpiece/Bluetooth) across subsequent calls
93
+ *
94
+ * We forward CallKeep’s `didActivateAudioSession` / `didDeactivateAudioSession` events to WebRTC’s
95
+ * `RTCAudioSession.audioSessionDidActivate()` / `audioSessionDidDeactivate()` methods.
96
+ */
97
+ function didActivateAudioSession() {
98
+ logger.debug('didActivateAudioSession');
99
+ RTCAudioSession.audioSessionDidActivate();
100
+ }
101
+
102
+ function didDeactivateAudioSession() {
103
+ logger.debug('didDeactivateAudioSession');
104
+ RTCAudioSession.audioSessionDidDeactivate();
105
+ }
106
+
78
107
  function didDisplayIncomingCall(callUUID: string, payload: object) {
79
108
  const voipPushNotification = getVoipPushNotificationLib();
80
109
  // @ts-expect-error - call_cid is not part of RNCallKeepEventPayload
@@ -118,6 +147,20 @@ export function setupIosCallKeepEvents(
118
147
  },
119
148
  );
120
149
 
150
+ const { remove: removeDidActivateAudioSession } = callkeep.addEventListener(
151
+ 'didActivateAudioSession',
152
+ () => {
153
+ didActivateAudioSession();
154
+ },
155
+ );
156
+
157
+ const { remove: removeDidDeactivateAudioSession } = callkeep.addEventListener(
158
+ 'didDeactivateAudioSession',
159
+ () => {
160
+ didDeactivateAudioSession();
161
+ },
162
+ );
163
+
121
164
  const { remove: removeDidLoadWithEvents } = callkeep.addEventListener(
122
165
  'didLoadWithEvents',
123
166
  (events) => {
@@ -133,6 +176,10 @@ export function setupIosCallKeepEvents(
133
176
  answerCall(data.callUUID);
134
177
  } else if (name === 'RNCallKeepPerformEndCallAction') {
135
178
  endCall(data.callUUID);
179
+ } else if (name === 'RNCallKeepDidActivateAudioSession') {
180
+ didActivateAudioSession();
181
+ } else if (name === 'RNCallKeepDidDeactivateAudioSession') {
182
+ didDeactivateAudioSession();
136
183
  }
137
184
  });
138
185
  },
@@ -142,6 +189,8 @@ export function setupIosCallKeepEvents(
142
189
  removeAnswerCall();
143
190
  removeEndCall();
144
191
  removeDisplayIncomingCall();
192
+ removeDidActivateAudioSession();
193
+ removeDidDeactivateAudioSession();
145
194
  removeDidLoadWithEvents();
146
195
  });
147
196
  }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.27.4';
1
+ export const version = '1.28.1';
@@ -1,41 +0,0 @@
1
- package com.streamvideo.reactnative.audio.utils
2
-
3
- import android.media.AudioManager
4
- import android.util.Log
5
- import com.facebook.react.bridge.ReactContext
6
- import com.streamvideo.reactnative.audio.AudioDeviceManager
7
- import com.streamvideo.reactnative.callmanager.StreamInCallManagerModule.Companion.TAG
8
-
9
- class AudioSetupStoreUtil(
10
- private val mReactContext: ReactContext,
11
- private val mAudioManager: AudioManager,
12
- private val mAudioDeviceManager: AudioDeviceManager
13
- ) {
14
- private var isOrigAudioSetupStored = false
15
- private var origIsSpeakerPhoneOn = false
16
- private var origIsMicrophoneMute = false
17
- private var origAudioMode = AudioManager.MODE_NORMAL
18
-
19
- fun storeOriginalAudioSetup() {
20
- if (!isOrigAudioSetupStored) {
21
- origAudioMode = mAudioManager.mode
22
- origIsSpeakerPhoneOn = AudioManagerUtil.isSpeakerphoneOn(mAudioManager)
23
- origIsMicrophoneMute = mAudioManager.isMicrophoneMute
24
- isOrigAudioSetupStored = true
25
- }
26
- }
27
-
28
- fun restoreOriginalAudioSetup() {
29
- if (isOrigAudioSetupStored) {
30
- if (origIsSpeakerPhoneOn) {
31
- mAudioDeviceManager.setSpeakerphoneOn(true)
32
- }
33
- mAudioManager.setMicrophoneMute(origIsMicrophoneMute)
34
- mAudioManager.mode = origAudioMode
35
- mReactContext.currentActivity?.apply {
36
- volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE
37
- }
38
- isOrigAudioSetupStored = false
39
- }
40
- }
41
- }