@stream-io/video-react-native-sdk 1.21.2 → 1.22.1-alpha.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 (219) hide show
  1. package/android/gradle.properties +3 -3
  2. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +10 -0
  3. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativePackage.kt +2 -1
  4. package/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt +592 -0
  5. package/android/src/main/java/com/streamvideo/reactnative/audio/BluetoothManager.kt +755 -0
  6. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioDeviceEndpointUtils.kt +192 -0
  7. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt +62 -0
  8. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioManagerUtil.kt +159 -0
  9. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt +41 -0
  10. package/android/src/main/java/com/streamvideo/reactnative/audio/utils/WebRtcAudioUtils.kt +250 -0
  11. package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +191 -0
  12. package/android/src/main/java/com/streamvideo/reactnative/model/AudioDeviceEndpoint.kt +136 -0
  13. package/android/src/main/java/org/webrtc/audio/WebRtcAudioTrackHelper.kt +12 -0
  14. package/dist/commonjs/components/Call/CallContent/CallContent.js +13 -9
  15. package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
  16. package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js +9 -2
  17. package/dist/commonjs/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
  18. package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js +3 -0
  19. package/dist/commonjs/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
  20. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js +10 -6
  21. package/dist/commonjs/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
  22. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +8 -4
  23. package/dist/commonjs/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  24. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js +26 -27
  25. package/dist/commonjs/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  26. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js +10 -6
  27. package/dist/commonjs/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  28. package/dist/commonjs/hooks/useIsInPiPMode.js +3 -3
  29. package/dist/commonjs/hooks/useIsInPiPMode.js.map +1 -1
  30. package/dist/commonjs/hooks/usePermissionNotification.js +5 -5
  31. package/dist/commonjs/hooks/usePermissionNotification.js.map +1 -1
  32. package/dist/commonjs/index.js +12 -0
  33. package/dist/commonjs/index.js.map +1 -1
  34. package/dist/commonjs/modules/call-manager/CallManager.js +113 -0
  35. package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -0
  36. package/dist/commonjs/modules/call-manager/PrevLibDetection.js +18 -0
  37. package/dist/commonjs/modules/call-manager/PrevLibDetection.js.map +1 -0
  38. package/dist/commonjs/modules/call-manager/index.js +24 -0
  39. package/dist/commonjs/modules/call-manager/index.js.map +1 -0
  40. package/dist/commonjs/modules/call-manager/native-module.d.js +4 -0
  41. package/dist/commonjs/modules/call-manager/native-module.d.js.map +1 -0
  42. package/dist/commonjs/modules/call-manager/types.js +2 -0
  43. package/dist/commonjs/modules/call-manager/types.js.map +1 -0
  44. package/dist/commonjs/providers/StreamCall/AppStateListener.js +5 -5
  45. package/dist/commonjs/providers/StreamCall/AppStateListener.js.map +1 -1
  46. package/dist/commonjs/theme/theme.js.map +1 -1
  47. package/dist/commonjs/utils/internal/rxSubjects.js +2 -2
  48. package/dist/commonjs/utils/internal/rxSubjects.js.map +1 -1
  49. package/dist/commonjs/version.js +1 -1
  50. package/dist/commonjs/version.js.map +1 -1
  51. package/dist/module/components/Call/CallContent/CallContent.js +14 -9
  52. package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
  53. package/dist/module/components/Call/CallContent/RTCViewPipIOS.js +9 -2
  54. package/dist/module/components/Call/CallContent/RTCViewPipIOS.js.map +1 -1
  55. package/dist/module/components/Call/CallContent/RTCViewPipNative.js +3 -0
  56. package/dist/module/components/Call/CallContent/RTCViewPipNative.js.map +1 -1
  57. package/dist/module/components/Livestream/HostLivestream/HostLivestream.js +10 -5
  58. package/dist/module/components/Livestream/HostLivestream/HostLivestream.js.map +1 -1
  59. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js +9 -4
  60. package/dist/module/components/Livestream/LivestreamControls/ViewerLivestreamControls.js.map +1 -1
  61. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js +26 -27
  62. package/dist/module/components/Livestream/LivestreamLayout/LivestreamLayout.js.map +1 -1
  63. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js +10 -5
  64. package/dist/module/components/Livestream/ViewerLivestream/ViewerLivestream.js.map +1 -1
  65. package/dist/module/hooks/useIsInPiPMode.js +4 -4
  66. package/dist/module/hooks/useIsInPiPMode.js.map +1 -1
  67. package/dist/module/hooks/usePermissionNotification.js +7 -6
  68. package/dist/module/hooks/usePermissionNotification.js.map +1 -1
  69. package/dist/module/icons/Back.js +1 -1
  70. package/dist/module/icons/Back.js.map +1 -1
  71. package/dist/module/icons/CameraSwitch.js +1 -1
  72. package/dist/module/icons/CameraSwitch.js.map +1 -1
  73. package/dist/module/icons/Mic.js +1 -1
  74. package/dist/module/icons/Mic.js.map +1 -1
  75. package/dist/module/icons/MicOff.js +1 -1
  76. package/dist/module/icons/MicOff.js.map +1 -1
  77. package/dist/module/icons/Phone.js +1 -1
  78. package/dist/module/icons/Phone.js.map +1 -1
  79. package/dist/module/icons/PinVertical.js +1 -1
  80. package/dist/module/icons/PinVertical.js.map +1 -1
  81. package/dist/module/icons/Reaction.js +1 -1
  82. package/dist/module/icons/Reaction.js.map +1 -1
  83. package/dist/module/icons/Spotlight.js +1 -1
  84. package/dist/module/icons/Spotlight.js.map +1 -1
  85. package/dist/module/icons/Video.js +1 -1
  86. package/dist/module/icons/Video.js.map +1 -1
  87. package/dist/module/icons/VideoSlash.js +1 -1
  88. package/dist/module/icons/VideoSlash.js.map +1 -1
  89. package/dist/module/index.js +1 -0
  90. package/dist/module/index.js.map +1 -1
  91. package/dist/module/modules/call-manager/CallManager.js +106 -0
  92. package/dist/module/modules/call-manager/CallManager.js.map +1 -0
  93. package/dist/module/modules/call-manager/PrevLibDetection.js +12 -0
  94. package/dist/module/modules/call-manager/PrevLibDetection.js.map +1 -0
  95. package/dist/module/modules/call-manager/index.js +4 -0
  96. package/dist/module/modules/call-manager/index.js.map +1 -0
  97. package/dist/module/modules/call-manager/native-module.d.js +2 -0
  98. package/dist/module/modules/call-manager/native-module.d.js.map +1 -0
  99. package/dist/module/modules/call-manager/types.js +2 -0
  100. package/dist/module/modules/call-manager/types.js.map +1 -0
  101. package/dist/module/providers/StreamCall/AppStateListener.js +6 -6
  102. package/dist/module/providers/StreamCall/AppStateListener.js.map +1 -1
  103. package/dist/module/theme/theme.js.map +1 -1
  104. package/dist/module/utils/internal/rxSubjects.js +1 -1
  105. package/dist/module/utils/internal/rxSubjects.js.map +1 -1
  106. package/dist/module/version.js +1 -1
  107. package/dist/module/version.js.map +1 -1
  108. package/dist/typescript/components/Call/CallContent/CallContent.d.ts +3 -2
  109. package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
  110. package/dist/typescript/components/Call/CallContent/RTCViewPipIOS.d.ts +5 -0
  111. package/dist/typescript/components/Call/CallContent/RTCViewPipIOS.d.ts.map +1 -1
  112. package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts +6 -0
  113. package/dist/typescript/components/Call/CallContent/RTCViewPipNative.d.ts.map +1 -1
  114. package/dist/typescript/components/Livestream/HostLivestream/HostLivestream.d.ts.map +1 -1
  115. package/dist/typescript/components/Livestream/LivestreamControls/ViewerLivestreamControls.d.ts.map +1 -1
  116. package/dist/typescript/components/Livestream/LivestreamLayout/LivestreamLayout.d.ts.map +1 -1
  117. package/dist/typescript/components/Livestream/ViewerLivestream/ViewerLivestream.d.ts.map +1 -1
  118. package/dist/typescript/hooks/usePermissionNotification.d.ts.map +1 -1
  119. package/dist/typescript/icons/Back.d.ts +1 -1
  120. package/dist/typescript/icons/Back.d.ts.map +1 -1
  121. package/dist/typescript/icons/BadNetwork.d.ts +1 -1
  122. package/dist/typescript/icons/BadNetwork.d.ts.map +1 -1
  123. package/dist/typescript/icons/CameraSwitch.d.ts +1 -1
  124. package/dist/typescript/icons/CameraSwitch.d.ts.map +1 -1
  125. package/dist/typescript/icons/LivestreamControls.d.ts +1 -1
  126. package/dist/typescript/icons/LivestreamControls.d.ts.map +1 -1
  127. package/dist/typescript/icons/Lock.d.ts +1 -1
  128. package/dist/typescript/icons/Lock.d.ts.map +1 -1
  129. package/dist/typescript/icons/Maximize.d.ts +1 -1
  130. package/dist/typescript/icons/Maximize.d.ts.map +1 -1
  131. package/dist/typescript/icons/Mic.d.ts +1 -1
  132. package/dist/typescript/icons/Mic.d.ts.map +1 -1
  133. package/dist/typescript/icons/MicOff.d.ts +1 -1
  134. package/dist/typescript/icons/MicOff.d.ts.map +1 -1
  135. package/dist/typescript/icons/Phone.d.ts +1 -1
  136. package/dist/typescript/icons/Phone.d.ts.map +1 -1
  137. package/dist/typescript/icons/PhoneDown.d.ts +1 -1
  138. package/dist/typescript/icons/PhoneDown.d.ts.map +1 -1
  139. package/dist/typescript/icons/PinVertical.d.ts +1 -1
  140. package/dist/typescript/icons/PinVertical.d.ts.map +1 -1
  141. package/dist/typescript/icons/Reaction.d.ts +1 -1
  142. package/dist/typescript/icons/Reaction.d.ts.map +1 -1
  143. package/dist/typescript/icons/ScreenShare.d.ts +1 -1
  144. package/dist/typescript/icons/ScreenShare.d.ts.map +1 -1
  145. package/dist/typescript/icons/ScreenShareIndicator.d.ts +1 -1
  146. package/dist/typescript/icons/ScreenShareIndicator.d.ts.map +1 -1
  147. package/dist/typescript/icons/Spotlight.d.ts +1 -1
  148. package/dist/typescript/icons/Spotlight.d.ts.map +1 -1
  149. package/dist/typescript/icons/StopScreenShare.d.ts +1 -1
  150. package/dist/typescript/icons/StopScreenShare.d.ts.map +1 -1
  151. package/dist/typescript/icons/Video.d.ts +1 -1
  152. package/dist/typescript/icons/Video.d.ts.map +1 -1
  153. package/dist/typescript/icons/VideoSlash.d.ts +1 -1
  154. package/dist/typescript/icons/VideoSlash.d.ts.map +1 -1
  155. package/dist/typescript/index.d.ts +1 -0
  156. package/dist/typescript/index.d.ts.map +1 -1
  157. package/dist/typescript/modules/call-manager/CallManager.d.ts +67 -0
  158. package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -0
  159. package/dist/typescript/modules/call-manager/PrevLibDetection.d.ts +13 -0
  160. package/dist/typescript/modules/call-manager/PrevLibDetection.d.ts.map +1 -0
  161. package/dist/typescript/modules/call-manager/index.d.ts +4 -0
  162. package/dist/typescript/modules/call-manager/index.d.ts.map +1 -0
  163. package/dist/typescript/modules/call-manager/types.d.ts +15 -0
  164. package/dist/typescript/modules/call-manager/types.d.ts.map +1 -0
  165. package/dist/typescript/providers/StreamCall/AppStateListener.d.ts.map +1 -1
  166. package/dist/typescript/theme/theme.d.ts +1 -2
  167. package/dist/typescript/theme/theme.d.ts.map +1 -1
  168. package/dist/typescript/utils/internal/rxSubjects.d.ts +1 -1
  169. package/dist/typescript/utils/internal/rxSubjects.d.ts.map +1 -1
  170. package/dist/typescript/version.d.ts +1 -1
  171. package/dist/typescript/version.d.ts.map +1 -1
  172. package/ios/PictureInPicture/StreamPictureInPictureController.swift +5 -0
  173. package/ios/RTCViewPip.swift +15 -0
  174. package/ios/RTCViewPipManager.mm +1 -0
  175. package/ios/StreamInCallManager.m +26 -0
  176. package/ios/StreamInCallManager.swift +303 -0
  177. package/ios/StreamVideoReactNative-Bridging-Header.h +1 -0
  178. package/ios/StreamVideoReactNative.m +6 -5
  179. package/ios/StreamVideoReactNative.xcodeproj/project.xcworkspace/xcuserdata/santhoshvaiyapuri.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  180. package/ios/StreamVideoReactNative.xcodeproj/xcuserdata/santhoshvaiyapuri.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  181. package/package.json +33 -35
  182. package/src/components/Call/CallContent/CallContent.tsx +14 -10
  183. package/src/components/Call/CallContent/RTCViewPipIOS.tsx +17 -2
  184. package/src/components/Call/CallContent/RTCViewPipNative.tsx +8 -0
  185. package/src/components/Livestream/HostLivestream/HostLivestream.tsx +8 -3
  186. package/src/components/Livestream/LivestreamControls/ViewerLivestreamControls.tsx +11 -5
  187. package/src/components/Livestream/LivestreamLayout/LivestreamLayout.tsx +38 -29
  188. package/src/components/Livestream/ViewerLivestream/ViewerLivestream.tsx +8 -3
  189. package/src/hooks/useIsInPiPMode.tsx +4 -4
  190. package/src/hooks/usePermissionNotification.tsx +7 -12
  191. package/src/icons/Back.tsx +2 -2
  192. package/src/icons/BadNetwork.tsx +1 -1
  193. package/src/icons/CameraSwitch.tsx +2 -2
  194. package/src/icons/LivestreamControls.tsx +1 -1
  195. package/src/icons/Lock.tsx +1 -1
  196. package/src/icons/Maximize.tsx +1 -1
  197. package/src/icons/Mic.tsx +2 -2
  198. package/src/icons/MicOff.tsx +2 -2
  199. package/src/icons/Phone.tsx +2 -2
  200. package/src/icons/PhoneDown.tsx +1 -1
  201. package/src/icons/PinVertical.tsx +2 -2
  202. package/src/icons/Reaction.tsx +2 -2
  203. package/src/icons/ScreenShare.tsx +1 -1
  204. package/src/icons/ScreenShareIndicator.tsx +1 -1
  205. package/src/icons/Spotlight.tsx +2 -2
  206. package/src/icons/StopScreenShare.tsx +1 -1
  207. package/src/icons/Video.tsx +2 -2
  208. package/src/icons/VideoSlash.tsx +2 -2
  209. package/src/index.ts +1 -0
  210. package/src/modules/call-manager/CallManager.ts +116 -0
  211. package/src/modules/call-manager/PrevLibDetection.ts +27 -0
  212. package/src/modules/call-manager/index.ts +5 -0
  213. package/src/modules/call-manager/native-module.d.ts +80 -0
  214. package/src/modules/call-manager/types.ts +25 -0
  215. package/src/providers/StreamCall/AppStateListener.tsx +6 -9
  216. package/src/theme/theme.ts +2 -2
  217. package/src/utils/internal/rxSubjects.ts +1 -1
  218. package/src/version.ts +1 -1
  219. package/CHANGELOG.md +0 -2783
@@ -0,0 +1,303 @@
1
+ import Foundation
2
+ import React
3
+ import UIKit
4
+ import AVFoundation
5
+ import stream_react_native_webrtc
6
+ import AVKit
7
+ import MediaPlayer
8
+
9
+ enum CallAudioRole {
10
+ case listener
11
+ case communicator
12
+ }
13
+
14
+ enum DefaultAudioDevice {
15
+ case speaker
16
+ case earpiece
17
+ }
18
+
19
+ @objc(StreamInCallManager)
20
+ class StreamInCallManager: RCTEventEmitter {
21
+
22
+ private var audioManagerActivated = false
23
+ private var callAudioRole: CallAudioRole = .communicator
24
+ private var defaultAudioDevice: DefaultAudioDevice = .speaker
25
+ private var previousVolume: Float = 0.75
26
+
27
+ private struct AudioSessionState {
28
+ let category: AVAudioSession.Category
29
+ let mode: AVAudioSession.Mode
30
+ let options: AVAudioSession.CategoryOptions
31
+ }
32
+
33
+ private var previousAudioSessionState: AudioSessionState?
34
+
35
+ override func invalidate() {
36
+ stop()
37
+ super.invalidate()
38
+ }
39
+
40
+ override static func requiresMainQueueSetup() -> Bool {
41
+ return false
42
+ }
43
+
44
+
45
+ @objc(setAudioRole:)
46
+ func setAudioRole(audioRole: String) {
47
+ if audioManagerActivated {
48
+ log("AudioManager is already activated, audio role cannot be changed.")
49
+ return
50
+ }
51
+ self.callAudioRole = audioRole.lowercased() == "listener" ? .listener : .communicator
52
+
53
+ }
54
+
55
+ @objc(setDefaultAudioDeviceEndpointType:)
56
+ func setDefaultAudioDeviceEndpointType(endpointType: String) {
57
+ if audioManagerActivated {
58
+ log("AudioManager is already activated, default audio device cannot be changed.")
59
+ return
60
+ }
61
+ self.defaultAudioDevice = endpointType.lowercased() == "earpiece" ? .earpiece : .speaker
62
+
63
+ }
64
+
65
+ @objc
66
+ func start() {
67
+ if audioManagerActivated {
68
+ return
69
+ }
70
+ let session = AVAudioSession.sharedInstance()
71
+ previousAudioSessionState = AudioSessionState(
72
+ category: session.category,
73
+ mode: session.mode,
74
+ options: session.categoryOptions
75
+ )
76
+ configureAudioSession()
77
+ audioManagerActivated = true
78
+ }
79
+
80
+ @objc
81
+ func stop() {
82
+ if !audioManagerActivated {
83
+ return
84
+ }
85
+ if let prev = previousAudioSessionState {
86
+ let session = AVAudioSession.sharedInstance()
87
+ do {
88
+ try session.setCategory(prev.category, mode: prev.mode, options: prev.options)
89
+ } catch {
90
+ log("Error restoring previous audio session: \(error.localizedDescription)")
91
+ }
92
+ previousAudioSessionState = nil
93
+ }
94
+ audioManagerActivated = false
95
+
96
+ }
97
+
98
+ private func configureAudioSession() {
99
+ let intendedCategory: AVAudioSession.Category!
100
+ let intendedMode: AVAudioSession.Mode!
101
+ let intendedOptions: AVAudioSession.CategoryOptions!
102
+
103
+ if (callAudioRole == .listener) {
104
+ // enables high quality audio playback but disables microphone
105
+ intendedCategory = .playback
106
+ intendedMode = .default
107
+ intendedOptions = []
108
+ } else {
109
+ intendedCategory = .playAndRecord
110
+ intendedMode = .voiceChat
111
+
112
+ if (defaultAudioDevice == .speaker) {
113
+ // defaultToSpeaker will route to speaker if nothing else is connected
114
+ intendedOptions = [.allowBluetooth, .defaultToSpeaker]
115
+ } else {
116
+ // having no defaultToSpeaker makes sure audio goes to earpiece if nothing is connected
117
+ intendedOptions = [.allowBluetooth]
118
+ }
119
+ }
120
+
121
+ // START: set the config that webrtc must use when it takes control
122
+ let rtcConfig = RTCAudioSessionConfiguration.webRTC()
123
+ rtcConfig.category = intendedCategory.rawValue
124
+ rtcConfig.mode = intendedMode.rawValue
125
+ rtcConfig.categoryOptions = intendedOptions
126
+ RTCAudioSessionConfiguration.setWebRTC(rtcConfig)
127
+ // END
128
+
129
+ // START: compare current audio session with intended, and update if different
130
+ let session = RTCAudioSession.sharedInstance()
131
+ let currentCategory = session.category
132
+ let currentMode = session.mode
133
+ let currentOptions = session.categoryOptions
134
+ let currentIsActive = session.isActive
135
+
136
+ if currentCategory != intendedCategory.rawValue || currentMode != intendedMode.rawValue || currentOptions != intendedOptions || !currentIsActive {
137
+ session.lockForConfiguration()
138
+ defer {
139
+ session.unlockForConfiguration()
140
+ }
141
+ do {
142
+ try session.setCategory(intendedCategory, mode: intendedMode, options: intendedOptions)
143
+ try session.setActive(true)
144
+ log("configureAudioSession: setCategory success \(intendedCategory.rawValue) \(intendedMode.rawValue) \(intendedOptions.rawValue)")
145
+ } catch {
146
+ log("configureAudioSession: setCategory failed due to: \(error.localizedDescription)")
147
+ do {
148
+ try session.setMode(intendedMode)
149
+ try session.setActive(true)
150
+ log("configureAudioSession: setMode success \(intendedMode.rawValue)")
151
+ } catch {
152
+ log("configureAudioSession: Error setting mode: \(error.localizedDescription)")
153
+ }
154
+ }
155
+ } else {
156
+ log("configureAudioSession: no change needed")
157
+ }
158
+ // END
159
+ }
160
+
161
+ @objc(showAudioRoutePicker)
162
+ public func showAudioRoutePicker() {
163
+ guard #available(iOS 11.0, tvOS 11.0, macOS 10.15, *) else {
164
+ return
165
+ }
166
+ DispatchQueue.main.async {
167
+ // AVRoutePickerView is the default UI with a
168
+ // button that users tap to stream audio/video content to a media receiver
169
+ let routePicker = AVRoutePickerView()
170
+ // Send a touch up inside event to the button to trigger the audio route picker
171
+ (routePicker.subviews.first { $0 is UIButton } as? UIButton)?
172
+ .sendActions(for: .touchUpInside)
173
+ }
174
+ }
175
+
176
+ @objc(setForceSpeakerphoneOn:)
177
+ func setForceSpeakerphoneOn(enable: Bool) {
178
+ let session = AVAudioSession.sharedInstance()
179
+ do {
180
+ try session.overrideOutputAudioPort(enable ? .speaker : .none)
181
+ try session.setActive(true)
182
+ } catch {
183
+ log("Error setting speakerphone: \(error)")
184
+ }
185
+ }
186
+
187
+ @objc(setMicrophoneMute:)
188
+ func setMicrophoneMute(enable: Bool) {
189
+ log("iOS does not support setMicrophoneMute()")
190
+ }
191
+
192
+ @objc
193
+ func logAudioState() {
194
+ let session = AVAudioSession.sharedInstance()
195
+ let logString = """
196
+ Audio State:
197
+ Category: \(session.category.rawValue)
198
+ Mode: \(session.mode.rawValue)
199
+ Output Port: \(session.currentRoute.outputs.first?.portName ?? "N/A")
200
+ Input Port: \(session.currentRoute.inputs.first?.portName ?? "N/A")
201
+ Category Options: \(session.categoryOptions)
202
+ InputNumberOfChannels: \(session.inputNumberOfChannels)
203
+ OutputNumberOfChannels: \(session.outputNumberOfChannels)
204
+ """
205
+ log(logString)
206
+ }
207
+
208
+ @objc(muteAudioOutput)
209
+ func muteAudioOutput() {
210
+ DispatchQueue.main.async { [self] in
211
+ let volumeView = MPVolumeView()
212
+
213
+ // Add to a temporary view hierarchy to make it functional
214
+ if let window = getCurrentWindow() {
215
+ volumeView.frame = CGRect(x: -1000, y: -1000, width: 1, height: 1)
216
+ window.addSubview(volumeView)
217
+
218
+ // Give it a moment to initialize
219
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
220
+ if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
221
+ self.previousVolume = slider.value
222
+ slider.setValue(0.0, animated: false)
223
+ slider.sendActions(for: .valueChanged)
224
+ self.log("Audio output muted via slider event")
225
+ } else {
226
+ self.log("Could not find volume slider")
227
+ }
228
+
229
+ // Remove from view hierarchy after use
230
+ volumeView.removeFromSuperview()
231
+ }
232
+ }
233
+ }
234
+ }
235
+
236
+ @objc(unmuteAudioOutput)
237
+ func unmuteAudioOutput() {
238
+ DispatchQueue.main.async { [self] in
239
+ let volumeView = MPVolumeView()
240
+
241
+ // Add to a temporary view hierarchy to make it functional
242
+ if let window = getCurrentWindow() {
243
+ volumeView.frame = CGRect(x: -1000, y: -1000, width: 1, height: 1)
244
+ window.addSubview(volumeView)
245
+
246
+ // Give it a moment to initialize
247
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
248
+ if let slider = volumeView.subviews.first(where: { $0 is UISlider }) as? UISlider {
249
+ let targetVolume = self.previousVolume > 0 ? self.previousVolume : 0.75
250
+ slider.setValue(targetVolume, animated: false)
251
+ slider.sendActions(for: .valueChanged)
252
+ self.log("Audio output unmuted via slider event")
253
+ } else {
254
+ self.log("Could not find volume slider")
255
+ }
256
+
257
+ // Remove from view hierarchy after use
258
+ volumeView.removeFromSuperview()
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ // MARK: - RCTEventEmitter
265
+
266
+ override func supportedEvents() -> [String]! {
267
+ // TODO: list events that can be sent to JS
268
+ return []
269
+ }
270
+
271
+ @objc
272
+ override func addListener(_ eventName: String!) {
273
+ super.addListener(eventName)
274
+ }
275
+
276
+ @objc
277
+ override func removeListeners(_ count: Double) {
278
+ super.removeListeners(count)
279
+ }
280
+
281
+ // MARK: - Helper Methods
282
+ private func getCurrentWindow() -> UIWindow? {
283
+ if #available(iOS 13.0, *) {
284
+ return UIApplication.shared.connectedScenes
285
+ .compactMap({ $0 as? UIWindowScene })
286
+ .first?.windows
287
+ .first(where: { $0.isKeyWindow })
288
+ } else {
289
+ return UIApplication.shared.keyWindow
290
+ }
291
+ }
292
+
293
+ @objc
294
+ func methodQueue() -> DispatchQueue {
295
+ return DispatchQueue(label: "io.getstream.rn.audioSessionQueue", qos: .userInitiated)
296
+ }
297
+
298
+ // MARK: - Logging Helper
299
+ private func log(_ message: String) {
300
+ NSLog("InCallManager: %@", message)
301
+ }
302
+
303
+ }
@@ -12,3 +12,4 @@
12
12
  #import <WebRTC/RTCVideoRenderer.h>
13
13
  #import <WebRTC/RTCVideoFrameBuffer.h>
14
14
  #import "WebRTCModule.h"
15
+ #import "WebRTCModuleOptions.h"
@@ -69,7 +69,7 @@ RCT_EXPORT_MODULE();
69
69
  if ((self = [super init])) {
70
70
  _notificationCenter = CFNotificationCenterGetDarwinNotifyCenter();
71
71
  [UIDevice currentDevice].batteryMonitoringEnabled = YES;
72
- [self setupObserver];
72
+ [self setupScreenshareEventObserver];
73
73
  [StreamVideoReactNative initializeSharedDictionaries];
74
74
  }
75
75
  return self;
@@ -85,7 +85,7 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
85
85
  resolve(thermalStateString);
86
86
  }
87
87
 
88
- -(void)dealloc {
88
+ -(void)invalidate {
89
89
  if (_busyTonePlayer) {
90
90
  if (_busyTonePlayer.isPlaying) {
91
91
  [_busyTonePlayer stop];
@@ -93,11 +93,12 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
93
93
  _busyTonePlayer = nil;
94
94
  [self removeAudioInterruptionHandling];
95
95
  }
96
- [self clearObserver];
96
+ [self clearScreenshareEventObserver];
97
+ [super invalidate];
97
98
  }
98
99
 
99
100
 
100
- -(void)setupObserver {
101
+ -(void)setupScreenshareEventObserver {
101
102
  CFNotificationCenterAddObserver(_notificationCenter,
102
103
  (__bridge const void *)(self),
103
104
  broadcastNotificationCallback,
@@ -112,7 +113,7 @@ RCT_EXPORT_METHOD(currentThermalState:(RCTPromiseResolveBlock)resolve rejecter:(
112
113
  CFNotificationSuspensionBehaviorDeliverImmediately);
113
114
  }
114
115
 
115
- -(void)clearObserver {
116
+ -(void)clearScreenshareEventObserver {
116
117
  CFNotificationCenterRemoveObserver(_notificationCenter,
117
118
  (__bridge const void *)(self),
118
119
  (__bridge CFStringRef)kBroadcastStartedNotification,
@@ -0,0 +1,14 @@
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>SchemeUserState</key>
6
+ <dict>
7
+ <key>StreamVideoReactNative.xcscheme_^#shared#^_</key>
8
+ <dict>
9
+ <key>orderHint</key>
10
+ <integer>0</integer>
11
+ </dict>
12
+ </dict>
13
+ </dict>
14
+ </plist>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-native-sdk",
3
- "version": "1.21.2",
3
+ "version": "1.22.1-alpha.0",
4
4
  "description": "Stream Video SDK for React Native",
5
5
  "author": "https://getstream.io",
6
6
  "homepage": "https://getstream.io/video/docs/react-native/",
@@ -45,12 +45,12 @@
45
45
  "!**/.*"
46
46
  ],
47
47
  "dependencies": {
48
- "@stream-io/video-client": "1.33.1",
49
- "@stream-io/video-react-bindings": "1.9.1",
48
+ "@stream-io/video-client": "1.34.0",
49
+ "@stream-io/video-react-bindings": "1.10.0",
50
50
  "intl-pluralrules": "2.0.1",
51
51
  "lodash.merge": "^4.6.2",
52
- "react-native-url-polyfill": "1.3.0",
53
- "rxjs": "~7.8.1",
52
+ "react-native-url-polyfill": "^3.0.0",
53
+ "rxjs": "~7.8.2",
54
54
  "text-encoding-polyfill": "0.6.7"
55
55
  },
56
56
  "peerDependencies": {
@@ -60,16 +60,15 @@
60
60
  "@react-native-firebase/app": ">=17.5.0",
61
61
  "@react-native-firebase/messaging": ">=17.5.0",
62
62
  "@stream-io/noise-cancellation-react-native": ">=0.1.0",
63
- "@stream-io/react-native-webrtc": ">=125.4.3",
63
+ "@stream-io/react-native-webrtc": ">=125.4.4",
64
64
  "@stream-io/video-filters-react-native": ">=0.1.0",
65
65
  "expo": ">=47.0.0",
66
66
  "expo-build-properties": "*",
67
67
  "expo-notifications": "*",
68
68
  "react": ">=17.0.0",
69
- "react-native": ">=0.67.0",
69
+ "react-native": ">=0.73.0",
70
70
  "react-native-callkeep": ">=4.3.11",
71
71
  "react-native-gesture-handler": ">=2.8.0",
72
- "react-native-incall-manager": ">=4.2.0",
73
72
  "react-native-reanimated": ">=2.7.0",
74
73
  "react-native-svg": ">=13.6.0",
75
74
  "react-native-voip-push-notification": ">=3.3.1"
@@ -116,44 +115,43 @@
116
115
  }
117
116
  },
118
117
  "devDependencies": {
119
- "@expo/config-plugins": "10.0.2",
120
- "@expo/config-types": "^53.0.4",
121
- "@expo/plist": "^0.3.4",
118
+ "@babel/core": "^7.28.4",
119
+ "@expo/config-plugins": "54.0.2",
120
+ "@expo/config-types": "^54.0.8",
121
+ "@expo/plist": "^0.4.7",
122
122
  "@notifee/react-native": "9.1.8",
123
123
  "@react-native-community/netinfo": "11.4.1",
124
124
  "@react-native-community/push-notification-ios": "1.11.0",
125
- "@react-native-firebase/app": "^22.1.0",
126
- "@react-native-firebase/messaging": "^22.1.0",
127
- "@react-native/babel-preset": "^0.79.2",
128
- "@stream-io/noise-cancellation-react-native": "^0.3.0",
129
- "@stream-io/react-native-webrtc": "125.4.3",
130
- "@stream-io/video-filters-react-native": "^0.7.0",
125
+ "@react-native-firebase/app": "^23.4.0",
126
+ "@react-native-firebase/messaging": "^23.4.0",
127
+ "@react-native/babel-preset": "^0.81.4",
128
+ "@stream-io/noise-cancellation-react-native": "workspace:^",
129
+ "@stream-io/react-native-webrtc": "125.4.4",
130
+ "@stream-io/video-filters-react-native": "workspace:^",
131
131
  "@testing-library/jest-native": "^5.4.3",
132
- "@testing-library/react-native": "13.2.0",
133
- "@tsconfig/node14": "14.1.3",
132
+ "@testing-library/react-native": "13.3.3",
133
+ "@tsconfig/node18": "^18.2.4",
134
134
  "@types/jest": "^29.5.14",
135
135
  "@types/lodash.merge": "^4.6.9",
136
- "@types/react": "^19.1.3",
137
- "@types/react-native-incall-manager": "^4.0.3",
136
+ "@types/react": "~19.1.17",
138
137
  "@types/react-test-renderer": "^19.1.0",
139
- "expo": "~53.0.8",
140
- "expo-build-properties": "^0.13.2",
141
- "expo-module-scripts": "^4.0.5",
142
- "expo-modules-core": "2.2.3",
143
- "expo-notifications": "~0.29.14",
138
+ "expo": "~54.0.12",
139
+ "expo-build-properties": "^1.0.9",
140
+ "expo-module-scripts": "^5.0.7",
141
+ "expo-notifications": "~0.32.12",
144
142
  "jest": "^29.7.0",
145
- "react": "19.0.0",
146
- "react-native": "0.79.2",
143
+ "react": "19.1.0",
144
+ "react-native": "^0.81.4",
147
145
  "react-native-builder-bob": "~0.23",
148
146
  "react-native-callkeep": "^4.3.16",
149
- "react-native-gesture-handler": "^2.25.0",
150
- "react-native-incall-manager": "^4.2.1",
151
- "react-native-reanimated": "~3.17.5",
152
- "react-native-svg": "15.11.2",
147
+ "react-native-gesture-handler": "^2.28.0",
148
+ "react-native-reanimated": "~4.1.2",
149
+ "react-native-svg": "^15.14.0",
153
150
  "react-native-voip-push-notification": "3.3.3",
154
- "react-test-renderer": "19.0.0",
151
+ "react-native-worklets": "^0.5.0",
152
+ "react-test-renderer": "19.1.0",
155
153
  "rimraf": "^6.0.1",
156
- "typescript": "^5.8.3"
154
+ "typescript": "^5.9.3"
157
155
  },
158
156
  "react-native-builder-bob": {
159
157
  "source": "src",
@@ -164,4 +162,4 @@
164
162
  "typescript"
165
163
  ]
166
164
  }
167
- }
165
+ }
@@ -1,12 +1,11 @@
1
1
  import React, { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import {
3
+ NativeModules,
4
+ Platform,
3
5
  StyleSheet,
4
6
  View,
5
- NativeModules,
6
7
  type ViewStyle,
7
- Platform,
8
8
  } from 'react-native';
9
- import InCallManager from 'react-native-incall-manager';
10
9
  import {
11
10
  CallParticipantsGrid,
12
11
  type CallParticipantsGridProps,
@@ -43,6 +42,7 @@ import {
43
42
  type ScreenShareOverlayProps,
44
43
  } from '../../utility/ScreenShareOverlay';
45
44
  import { RTCViewPipIOS } from './RTCViewPipIOS';
45
+ import { getRNInCallManagerLibNoThrow } from '../../../modules/call-manager/PrevLibDetection';
46
46
 
47
47
  export type StreamReactionType = StreamReaction & {
48
48
  icon: string;
@@ -95,7 +95,8 @@ export type CallContentProps = Pick<
95
95
  */
96
96
  disablePictureInPicture?: boolean;
97
97
  /**
98
- * Props to set the audio mode for the InCallManager.
98
+ * @deprecated This prop is deprecated and will be removed in the future. Use `StreamInCallManager` instead.
99
+ * Props to set the audio mode for the react-native-incall-manager library
99
100
  * If media type is video, audio is routed by default to speaker, otherwise it is routed to earpiece.
100
101
  * Changing the mode on the fly is not supported.
101
102
  * Manually invoke `InCallManager.start({ media })` to achieve this.
@@ -119,9 +120,9 @@ export const CallContent = ({
119
120
  layout = 'grid',
120
121
  landscape = false,
121
122
  supportedReactions,
123
+ initialInCallManagerAudioMode = 'video',
122
124
  iOSPiPIncludeLocalParticipantVideo,
123
125
  disablePictureInPicture,
124
- initialInCallManagerAudioMode = 'video',
125
126
  }: CallContentProps) => {
126
127
  const [
127
128
  showRemoteParticipantInFloatingView,
@@ -140,8 +141,6 @@ export const CallContent = ({
140
141
 
141
142
  useAutoEnterPiPEffect(disablePictureInPicture);
142
143
 
143
- const incallManagerModeRef = useRef(initialInCallManagerAudioMode);
144
-
145
144
  const _remoteParticipants = useRemoteParticipants();
146
145
  const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
147
146
  const localParticipant = useLocalParticipant();
@@ -188,10 +187,15 @@ export const CallContent = ({
188
187
  /**
189
188
  * This hook is used to handle IncallManager specs of the application.
190
189
  */
190
+ const incallManagerModeRef = useRef(initialInCallManagerAudioMode);
191
191
  useEffect(() => {
192
- InCallManager.start({ media: incallManagerModeRef.current });
193
-
194
- return () => InCallManager.stop();
192
+ const prevInCallManager = getRNInCallManagerLibNoThrow();
193
+ if (prevInCallManager) {
194
+ prevInCallManager.start({ media: incallManagerModeRef.current });
195
+ return () => {
196
+ prevInCallManager.stop();
197
+ };
198
+ }
195
199
  }, []);
196
200
 
197
201
  const handleFloatingViewParticipantSwitch = () => {
@@ -18,13 +18,19 @@ import {
18
18
  import { useDebouncedValue } from '../../../utils/hooks';
19
19
  import { shouldDisableIOSLocalVideoOnBackgroundRef } from '../../../utils/internal/shouldDisableIOSLocalVideoOnBackground';
20
20
  import { useTrackDimensions } from '../../../hooks/useTrackDimensions';
21
+ import { isInPiPMode$ } from '../../../utils/internal/rxSubjects';
21
22
 
22
23
  type Props = {
23
24
  includeLocalParticipantVideo?: boolean;
25
+ /**
26
+ * Callback that is called when the PiP mode state changes.
27
+ * @param active - true when PiP started, false when PiP stopped
28
+ */
29
+ onPiPChange?: (active: boolean) => void;
24
30
  };
25
31
 
26
32
  export const RTCViewPipIOS = React.memo((props: Props) => {
27
- const { includeLocalParticipantVideo } = props;
33
+ const { includeLocalParticipantVideo, onPiPChange } = props;
28
34
  const call = useCall();
29
35
  const { useParticipants } = useCallStateHooks();
30
36
  const _allParticipants = useParticipants({
@@ -112,9 +118,18 @@ export const RTCViewPipIOS = React.memo((props: Props) => {
112
118
  return videoStreamToRender?.toURL();
113
119
  }, [videoStreamToRender]);
114
120
 
121
+ const handlePiPChange = (event: { nativeEvent: { active: boolean } }) => {
122
+ isInPiPMode$.next(event.nativeEvent.active);
123
+ onPiPChange?.(event.nativeEvent.active);
124
+ };
125
+
115
126
  return (
116
127
  <>
117
- <RTCViewPipNative streamURL={streamURL} ref={nativeRef} />
128
+ <RTCViewPipNative
129
+ streamURL={streamURL}
130
+ ref={nativeRef}
131
+ onPiPChange={handlePiPChange}
132
+ />
118
133
  {participantInSpotlight && (
119
134
  <DimensionsUpdatedRenderless
120
135
  participant={participantInSpotlight}
@@ -10,8 +10,13 @@ import {
10
10
 
11
11
  const COMPONENT_NAME = 'RTCViewPip';
12
12
 
13
+ export type PiPChangeEvent = {
14
+ active: boolean;
15
+ };
16
+
13
17
  type RTCViewPipNativeProps = {
14
18
  streamURL?: string;
19
+ onPiPChange?: (event: { nativeEvent: PiPChangeEvent }) => void;
15
20
  };
16
21
 
17
22
  const NativeComponent: HostComponent<RTCViewPipNativeProps> =
@@ -48,6 +53,7 @@ export const RTCViewPipNative = React.memo(
48
53
  React.Ref<any>,
49
54
  {
50
55
  streamURL?: string;
56
+ onPiPChange?: (event: { nativeEvent: PiPChangeEvent }) => void;
51
57
  }
52
58
  >((props, ref) => {
53
59
  if (Platform.OS !== 'ios') return null;
@@ -58,6 +64,8 @@ export const RTCViewPipNative = React.memo(
58
64
  pointerEvents={'none'}
59
65
  // eslint-disable-next-line react/prop-types
60
66
  streamURL={props.streamURL}
67
+ // eslint-disable-next-line react/prop-types
68
+ onPiPChange={props.onPiPChange}
61
69
  // @ts-expect-error - types issue
62
70
  ref={ref}
63
71
  />
@@ -1,6 +1,6 @@
1
1
  import React, { useEffect, useMemo } from 'react';
2
2
  import { StyleSheet, View } from 'react-native';
3
- import InCallManager from 'react-native-incall-manager';
3
+ import { getRNInCallManagerLibNoThrow } from '../../../modules/call-manager/PrevLibDetection';
4
4
 
5
5
  import { useTheme } from '../../../contexts';
6
6
  import {
@@ -89,8 +89,13 @@ export const HostLivestream = ({
89
89
 
90
90
  // Automatically route audio to speaker devices as relevant for watching videos.
91
91
  useEffect(() => {
92
- InCallManager.start({ media: 'video' });
93
- return () => InCallManager.stop();
92
+ const prevInCallManager = getRNInCallManagerLibNoThrow();
93
+ if (prevInCallManager) {
94
+ prevInCallManager.start({ media: 'video' });
95
+ return () => {
96
+ prevInCallManager.stop();
97
+ };
98
+ }
94
99
  }, []);
95
100
 
96
101
  const [topViewHeight, setTopViewHeight] = React.useState<number>();