@stream-io/video-react-native-sdk 1.27.3 → 1.28.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.
- package/CHANGELOG.md +24 -0
- package/android/build.gradle +1 -0
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoAppLifecycleModule.kt +86 -0
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativePackage.kt +5 -1
- package/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt +19 -11
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt +23 -9
- package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +48 -14
- package/dist/commonjs/index.js +2 -0
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/modules/call-manager/CallManager.js +5 -0
- package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -1
- package/dist/commonjs/providers/StreamCall/AppStateListener.js +74 -16
- package/dist/commonjs/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/commonjs/utils/internal/registerSDKGlobals.js +34 -0
- package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -0
- package/dist/commonjs/version.js +1 -1
- package/dist/module/index.js +2 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/modules/call-manager/CallManager.js +5 -0
- package/dist/module/modules/call-manager/CallManager.js.map +1 -1
- package/dist/module/providers/StreamCall/AppStateListener.js +74 -16
- package/dist/module/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/module/utils/internal/registerSDKGlobals.js +28 -0
- package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/modules/call-manager/CallManager.d.ts +2 -0
- package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -1
- package/dist/typescript/modules/call-manager/types.d.ts +1 -0
- package/dist/typescript/modules/call-manager/types.d.ts.map +1 -1
- package/dist/typescript/providers/StreamCall/AppStateListener.d.ts.map +1 -1
- package/dist/typescript/utils/internal/registerSDKGlobals.d.ts +2 -0
- package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -0
- package/dist/typescript/version.d.ts +1 -1
- package/ios/StreamInCallManager.m +4 -0
- package/ios/StreamInCallManager.swift +213 -88
- package/package.json +7 -7
- package/src/index.ts +2 -0
- package/src/modules/call-manager/CallManager.ts +5 -0
- package/src/modules/call-manager/native-module.d.ts +11 -0
- package/src/modules/call-manager/types.ts +1 -0
- package/src/providers/StreamCall/AppStateListener.tsx +116 -17
- package/src/utils/internal/registerSDKGlobals.ts +30 -0
- package/src/version.ts +1 -1
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt +0 -41
|
@@ -2,6 +2,7 @@ import { useCall } from '@stream-io/video-react-bindings';
|
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
AppState,
|
|
5
|
+
type AppStateStatus,
|
|
5
6
|
NativeEventEmitter,
|
|
6
7
|
NativeModules,
|
|
7
8
|
Platform,
|
|
@@ -11,6 +12,8 @@ import { disablePiPMode$, isInPiPMode$ } from '../../utils/internal/rxSubjects';
|
|
|
11
12
|
import { RxUtils, videoLoggerSystem } from '@stream-io/video-client';
|
|
12
13
|
|
|
13
14
|
const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
|
|
15
|
+
const ANDROID_APP_STATE_CHANGED_EVENT =
|
|
16
|
+
'StreamVideoAppLifecycle_APP_STATE_CHANGED';
|
|
14
17
|
|
|
15
18
|
const isAndroid8OrAbove = Platform.OS === 'android' && Platform.Version >= 26;
|
|
16
19
|
|
|
@@ -19,7 +22,7 @@ const isAndroid8OrAbove = Platform.OS === 'android' && Platform.Version >= 26;
|
|
|
19
22
|
// 2. Handle PiP mode in Android
|
|
20
23
|
export const AppStateListener = () => {
|
|
21
24
|
const call = useCall();
|
|
22
|
-
const appState = useRef(AppState.currentState);
|
|
25
|
+
const appState = useRef<AppStateStatus>(AppState.currentState);
|
|
23
26
|
const cameraDisabledByAppState = useRef<boolean>(false);
|
|
24
27
|
|
|
25
28
|
// on mount: set initial PiP mode and listen to PiP events
|
|
@@ -62,25 +65,65 @@ export const AppStateListener = () => {
|
|
|
62
65
|
}, []);
|
|
63
66
|
|
|
64
67
|
useEffect(() => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
const logger = videoLoggerSystem.getLogger('AppStateListener');
|
|
69
|
+
|
|
70
|
+
const handleAppStateChange = (nextAppState: AppStateStatus) => {
|
|
71
|
+
logger.debug(
|
|
72
|
+
'AppState changed to ',
|
|
73
|
+
nextAppState,
|
|
74
|
+
' from ',
|
|
75
|
+
appState.current,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// due to strange behavior in iOS when app goes to "inactive" state
|
|
79
|
+
// we dont check for inactive states
|
|
80
|
+
// ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
|
|
70
81
|
if (appState.current.match(/background/) && nextAppState === 'active') {
|
|
71
82
|
if (call?.camera?.state.status === 'enabled') {
|
|
83
|
+
logger.debug(
|
|
84
|
+
'attempt to Disable and reenable camera as app came to foreground',
|
|
85
|
+
);
|
|
72
86
|
// Android: when device is locked and resumed, the status isnt made disabled but stays enabled
|
|
73
87
|
// iOS PiP: when local track was replaced by remote track, the local track shown is blank
|
|
74
88
|
// as a workaround we stop the track and enable again if its already in enabled state
|
|
75
|
-
|
|
76
|
-
call?.camera
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
const renableCamera = () => {
|
|
90
|
+
const camera = call?.camera;
|
|
91
|
+
if (!camera) return Promise.resolve();
|
|
92
|
+
return camera
|
|
93
|
+
.disable(true)
|
|
94
|
+
.then(() => camera.enable())
|
|
95
|
+
.catch((e) => {
|
|
96
|
+
logger.warn(
|
|
97
|
+
'Failed to disable+reenable camera as app came to foreground',
|
|
98
|
+
e,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
if (Platform.OS === 'android') {
|
|
103
|
+
NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(
|
|
104
|
+
(isCallAliveConfigured: boolean) => {
|
|
105
|
+
if (!isCallAliveConfigured) {
|
|
106
|
+
renableCamera();
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
renableCamera();
|
|
112
|
+
}
|
|
79
113
|
} else {
|
|
80
114
|
if (cameraDisabledByAppState.current) {
|
|
81
|
-
call?.camera
|
|
82
|
-
|
|
83
|
-
|
|
115
|
+
call?.camera
|
|
116
|
+
?.resume()
|
|
117
|
+
.then(() => {
|
|
118
|
+
cameraDisabledByAppState.current = false;
|
|
119
|
+
logger.debug('Camera resumed as app came to foreground');
|
|
120
|
+
})
|
|
121
|
+
.catch((e) => {
|
|
122
|
+
logger.warn(
|
|
123
|
+
'Failed to resume camera as app came to foreground',
|
|
124
|
+
e,
|
|
125
|
+
);
|
|
126
|
+
});
|
|
84
127
|
}
|
|
85
128
|
}
|
|
86
129
|
appState.current = nextAppState;
|
|
@@ -90,9 +133,18 @@ export const AppStateListener = () => {
|
|
|
90
133
|
) {
|
|
91
134
|
const disableCameraIfNeeded = () => {
|
|
92
135
|
if (call?.camera?.state.status === 'enabled') {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
136
|
+
call?.camera
|
|
137
|
+
?.disable()
|
|
138
|
+
.then(() => {
|
|
139
|
+
cameraDisabledByAppState.current = true;
|
|
140
|
+
logger.debug('Camera disabled by app going to background');
|
|
141
|
+
})
|
|
142
|
+
.catch((e) => {
|
|
143
|
+
logger.warn(
|
|
144
|
+
'Failed to disable camera as app went to background',
|
|
145
|
+
e,
|
|
146
|
+
);
|
|
147
|
+
});
|
|
96
148
|
}
|
|
97
149
|
};
|
|
98
150
|
if (Platform.OS === 'android') {
|
|
@@ -113,7 +165,15 @@ export const AppStateListener = () => {
|
|
|
113
165
|
// this happens on foreground push notifications
|
|
114
166
|
return;
|
|
115
167
|
}
|
|
116
|
-
|
|
168
|
+
// check if keep call alive is configured
|
|
169
|
+
// if not, then disable the camera as we are not able to keep the call alive in the background
|
|
170
|
+
NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(
|
|
171
|
+
(isCallAliveConfigured: boolean) => {
|
|
172
|
+
if (!isCallAliveConfigured) {
|
|
173
|
+
disableCameraIfNeeded();
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
);
|
|
117
177
|
}
|
|
118
178
|
},
|
|
119
179
|
);
|
|
@@ -128,6 +188,45 @@ export const AppStateListener = () => {
|
|
|
128
188
|
}
|
|
129
189
|
appState.current = nextAppState;
|
|
130
190
|
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// for Android use our custom native module to listen to app state changes
|
|
194
|
+
// because the default react-native AppState listener works for activity and ours works for application process
|
|
195
|
+
if (Platform.OS === 'android') {
|
|
196
|
+
const nativeModule = NativeModules.StreamVideoAppLifecycle;
|
|
197
|
+
const eventEmitter = new NativeEventEmitter(nativeModule);
|
|
198
|
+
let cancelled = false;
|
|
199
|
+
|
|
200
|
+
nativeModule
|
|
201
|
+
.getCurrentAppState()
|
|
202
|
+
.then((initialState: AppStateStatus | null | undefined) => {
|
|
203
|
+
if (cancelled) return;
|
|
204
|
+
if (initialState === 'active' || initialState === 'background') {
|
|
205
|
+
appState.current = initialState;
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
.catch(() => {
|
|
209
|
+
logger.warn('Failed to get current app state from native module');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const subscription = eventEmitter.addListener(
|
|
213
|
+
ANDROID_APP_STATE_CHANGED_EVENT,
|
|
214
|
+
(nextAppState: AppStateStatus) => {
|
|
215
|
+
if (nextAppState === 'active' || nextAppState === 'background') {
|
|
216
|
+
handleAppStateChange(nextAppState);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
return () => {
|
|
222
|
+
cancelled = true;
|
|
223
|
+
subscription.remove();
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// for iOS use the default react-native AppState listener
|
|
228
|
+
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
229
|
+
handleAppStateChange(nextAppState);
|
|
131
230
|
});
|
|
132
231
|
|
|
133
232
|
return () => {
|
|
@@ -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: ({ default_device }) => {
|
|
9
|
+
StreamInCallManagerNativeModule.setDefaultAudioDeviceEndpointType(
|
|
10
|
+
default_device,
|
|
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
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.
|
|
1
|
+
export const version = '1.28.0';
|
package/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt
DELETED
|
@@ -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
|
-
}
|