@stream-io/video-react-native-sdk 1.27.2 → 1.27.4
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 +18 -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/dist/commonjs/providers/StreamCall/AppStateListener.js +74 -16
- package/dist/commonjs/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/module/providers/StreamCall/AppStateListener.js +74 -16
- package/dist/module/providers/StreamCall/AppStateListener.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/providers/StreamCall/AppStateListener.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/providers/StreamCall/AppStateListener.tsx +116 -17
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
|
|
4
4
|
|
|
5
|
+
## [1.27.4](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-native-sdk-1.27.3...@stream-io/video-react-native-sdk-1.27.4) (2026-01-16)
|
|
6
|
+
|
|
7
|
+
### Dependency Updates
|
|
8
|
+
|
|
9
|
+
- `@stream-io/video-client` updated to version `1.40.3`
|
|
10
|
+
- `@stream-io/video-react-bindings` updated to version `1.12.10`
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- do not disable camera on Android unnecessarily RN-335 ([#2085](https://github.com/GetStream/stream-video-js/issues/2085)) ([e4dfa39](https://github.com/GetStream/stream-video-js/commit/e4dfa39b7a001e60fee73db01d717ed8eb05d9b0))
|
|
15
|
+
|
|
16
|
+
## [1.27.3](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-native-sdk-1.27.2...@stream-io/video-react-native-sdk-1.27.3) (2026-01-15)
|
|
17
|
+
|
|
18
|
+
### Dependency Updates
|
|
19
|
+
|
|
20
|
+
- `@stream-io/video-client` updated to version `1.40.2`
|
|
21
|
+
- `@stream-io/video-react-bindings` updated to version `1.12.9`
|
|
22
|
+
|
|
5
23
|
## [1.27.2](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-native-sdk-1.27.1...@stream-io/video-react-native-sdk-1.27.2) (2026-01-14)
|
|
6
24
|
|
|
7
25
|
### Dependency Updates
|
package/android/build.gradle
CHANGED
|
@@ -97,4 +97,5 @@ dependencies {
|
|
|
97
97
|
implementation project(':stream-io_react-native-webrtc')
|
|
98
98
|
implementation "io.github.crow-misia.libyuv:libyuv-android:0.36.0"
|
|
99
99
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
100
|
+
implementation "androidx.lifecycle:lifecycle-process:2.10.0"
|
|
100
101
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
package com.streamvideo.reactnative
|
|
2
|
+
|
|
3
|
+
import androidx.lifecycle.Lifecycle
|
|
4
|
+
import androidx.lifecycle.LifecycleEventObserver
|
|
5
|
+
import androidx.lifecycle.ProcessLifecycleOwner
|
|
6
|
+
import com.facebook.react.bridge.Promise
|
|
7
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
8
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
9
|
+
import com.facebook.react.bridge.ReactMethod
|
|
10
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Emits application *process* lifecycle changes using ProcessLifecycleOwner.
|
|
14
|
+
*
|
|
15
|
+
* Based on:
|
|
16
|
+
* https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner
|
|
17
|
+
*
|
|
18
|
+
* Notes:
|
|
19
|
+
* - ON_CREATE is dispatched once and ON_DESTROY is never dispatched.
|
|
20
|
+
* - ON_STOP / ON_PAUSE are dispatched with a delay after the last activity stops/pauses.
|
|
21
|
+
*/
|
|
22
|
+
class StreamVideoAppLifecycleModule(reactContext: ReactApplicationContext) :
|
|
23
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
24
|
+
|
|
25
|
+
override fun getName(): String = NAME
|
|
26
|
+
|
|
27
|
+
private var observer: LifecycleEventObserver? = null
|
|
28
|
+
|
|
29
|
+
override fun initialize() {
|
|
30
|
+
super.initialize()
|
|
31
|
+
|
|
32
|
+
val lifecycle = ProcessLifecycleOwner.get().lifecycle
|
|
33
|
+
val lifecycleObserver = LifecycleEventObserver { _, event ->
|
|
34
|
+
when (event) {
|
|
35
|
+
Lifecycle.Event.ON_START -> emitAppState("active")
|
|
36
|
+
Lifecycle.Event.ON_STOP -> emitAppState("background")
|
|
37
|
+
else -> Unit
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
observer = lifecycleObserver
|
|
41
|
+
reactApplicationContext.runOnUiQueueThread {
|
|
42
|
+
lifecycle.addObserver(lifecycleObserver)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun invalidate() {
|
|
47
|
+
observer?.let {
|
|
48
|
+
reactApplicationContext.runOnUiQueueThread {
|
|
49
|
+
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
observer = null
|
|
53
|
+
super.invalidate()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private fun emitAppState(appState: String) {
|
|
57
|
+
reactApplicationContext
|
|
58
|
+
.getJSModule(RCTDeviceEventEmitter::class.java)
|
|
59
|
+
.emit(APP_STATE_CHANGED_EVENT, appState)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
@ReactMethod
|
|
63
|
+
fun getCurrentAppState(promise: Promise) {
|
|
64
|
+
val state = ProcessLifecycleOwner.get().lifecycle.currentState
|
|
65
|
+
val appState = if (state.isAtLeast(Lifecycle.State.STARTED)) "active" else "background"
|
|
66
|
+
promise.resolve(appState)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Suppress("UNUSED_PARAMETER")
|
|
70
|
+
@ReactMethod
|
|
71
|
+
fun addListener(eventName: String?) {
|
|
72
|
+
// Required for RN NativeEventEmitter
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@Suppress("UNUSED_PARAMETER")
|
|
76
|
+
@ReactMethod
|
|
77
|
+
fun removeListeners(count: Int) {
|
|
78
|
+
// Required for RN NativeEventEmitter
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
companion object {
|
|
82
|
+
private const val NAME = "StreamVideoAppLifecycle"
|
|
83
|
+
private const val APP_STATE_CHANGED_EVENT = NAME + "_APP_STATE_CHANGED"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
@@ -9,7 +9,11 @@ import com.streamvideo.reactnative.callmanager.StreamInCallManagerModule
|
|
|
9
9
|
|
|
10
10
|
class StreamVideoReactNativePackage : ReactPackage {
|
|
11
11
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
12
|
-
return listOf(
|
|
12
|
+
return listOf(
|
|
13
|
+
StreamVideoReactNativeModule(reactContext),
|
|
14
|
+
StreamVideoAppLifecycleModule(reactContext),
|
|
15
|
+
StreamInCallManagerModule(reactContext),
|
|
16
|
+
)
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
|
@@ -11,6 +11,7 @@ var _shouldDisableIOSLocalVideoOnBackground = require("../../utils/internal/shou
|
|
|
11
11
|
var _rxSubjects = require("../../utils/internal/rxSubjects");
|
|
12
12
|
var _videoClient = require("@stream-io/video-client");
|
|
13
13
|
const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
|
|
14
|
+
const ANDROID_APP_STATE_CHANGED_EVENT = 'StreamVideoAppLifecycle_APP_STATE_CHANGED';
|
|
14
15
|
const isAndroid8OrAbove = _reactNative.Platform.OS === 'android' && _reactNative.Platform.Version >= 26;
|
|
15
16
|
|
|
16
17
|
// Does 2 functionalities:
|
|
@@ -44,34 +45,55 @@ const AppStateListener = () => {
|
|
|
44
45
|
};
|
|
45
46
|
}, []);
|
|
46
47
|
(0, _react.useEffect)(() => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
const logger = _videoClient.videoLoggerSystem.getLogger('AppStateListener');
|
|
49
|
+
const handleAppStateChange = nextAppState => {
|
|
50
|
+
logger.debug('AppState changed to ', nextAppState, ' from ', appState.current);
|
|
51
|
+
|
|
52
|
+
// due to strange behavior in iOS when app goes to "inactive" state
|
|
53
|
+
// we dont check for inactive states
|
|
54
|
+
// ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
|
|
52
55
|
if (appState.current.match(/background/) && nextAppState === 'active') {
|
|
53
56
|
if (call?.camera?.state.status === 'enabled') {
|
|
57
|
+
logger.debug('attempt to Disable and reenable camera as app came to foreground');
|
|
54
58
|
// Android: when device is locked and resumed, the status isnt made disabled but stays enabled
|
|
55
59
|
// iOS PiP: when local track was replaced by remote track, the local track shown is blank
|
|
56
60
|
// as a workaround we stop the track and enable again if its already in enabled state
|
|
57
|
-
|
|
58
|
-
call?.camera
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
const renableCamera = () => {
|
|
62
|
+
const camera = call?.camera;
|
|
63
|
+
if (!camera) return Promise.resolve();
|
|
64
|
+
return camera.disable(true).then(() => camera.enable()).catch(e => {
|
|
65
|
+
logger.warn('Failed to disable+reenable camera as app came to foreground', e);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
if (_reactNative.Platform.OS === 'android') {
|
|
69
|
+
_reactNative.NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(isCallAliveConfigured => {
|
|
70
|
+
if (!isCallAliveConfigured) {
|
|
71
|
+
renableCamera();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
} else {
|
|
75
|
+
renableCamera();
|
|
76
|
+
}
|
|
61
77
|
} else {
|
|
62
78
|
if (cameraDisabledByAppState.current) {
|
|
63
|
-
call?.camera?.resume()
|
|
64
|
-
|
|
65
|
-
|
|
79
|
+
call?.camera?.resume().then(() => {
|
|
80
|
+
cameraDisabledByAppState.current = false;
|
|
81
|
+
logger.debug('Camera resumed as app came to foreground');
|
|
82
|
+
}).catch(e => {
|
|
83
|
+
logger.warn('Failed to resume camera as app came to foreground', e);
|
|
84
|
+
});
|
|
66
85
|
}
|
|
67
86
|
}
|
|
68
87
|
appState.current = nextAppState;
|
|
69
88
|
} else if (appState.current === 'active' && nextAppState.match(/background/)) {
|
|
70
89
|
const disableCameraIfNeeded = () => {
|
|
71
90
|
if (call?.camera?.state.status === 'enabled') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
91
|
+
call?.camera?.disable().then(() => {
|
|
92
|
+
cameraDisabledByAppState.current = true;
|
|
93
|
+
logger.debug('Camera disabled by app going to background');
|
|
94
|
+
}).catch(e => {
|
|
95
|
+
logger.warn('Failed to disable camera as app went to background', e);
|
|
96
|
+
});
|
|
75
97
|
}
|
|
76
98
|
};
|
|
77
99
|
if (_reactNative.Platform.OS === 'android') {
|
|
@@ -91,7 +113,13 @@ const AppStateListener = () => {
|
|
|
91
113
|
// this happens on foreground push notifications
|
|
92
114
|
return;
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
// check if keep call alive is configured
|
|
117
|
+
// if not, then disable the camera as we are not able to keep the call alive in the background
|
|
118
|
+
_reactNative.NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(isCallAliveConfigured => {
|
|
119
|
+
if (!isCallAliveConfigured) {
|
|
120
|
+
disableCameraIfNeeded();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
95
123
|
}
|
|
96
124
|
});
|
|
97
125
|
} else {
|
|
@@ -105,6 +133,36 @@ const AppStateListener = () => {
|
|
|
105
133
|
}
|
|
106
134
|
appState.current = nextAppState;
|
|
107
135
|
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// for Android use our custom native module to listen to app state changes
|
|
139
|
+
// because the default react-native AppState listener works for activity and ours works for application process
|
|
140
|
+
if (_reactNative.Platform.OS === 'android') {
|
|
141
|
+
const nativeModule = _reactNative.NativeModules.StreamVideoAppLifecycle;
|
|
142
|
+
const eventEmitter = new _reactNative.NativeEventEmitter(nativeModule);
|
|
143
|
+
let cancelled = false;
|
|
144
|
+
nativeModule.getCurrentAppState().then(initialState => {
|
|
145
|
+
if (cancelled) return;
|
|
146
|
+
if (initialState === 'active' || initialState === 'background') {
|
|
147
|
+
appState.current = initialState;
|
|
148
|
+
}
|
|
149
|
+
}).catch(() => {
|
|
150
|
+
logger.warn('Failed to get current app state from native module');
|
|
151
|
+
});
|
|
152
|
+
const subscription = eventEmitter.addListener(ANDROID_APP_STATE_CHANGED_EVENT, nextAppState => {
|
|
153
|
+
if (nextAppState === 'active' || nextAppState === 'background') {
|
|
154
|
+
handleAppStateChange(nextAppState);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
return () => {
|
|
158
|
+
cancelled = true;
|
|
159
|
+
subscription.remove();
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// for iOS use the default react-native AppState listener
|
|
164
|
+
const subscription = _reactNative.AppState.addEventListener('change', nextAppState => {
|
|
165
|
+
handleAppStateChange(nextAppState);
|
|
108
166
|
});
|
|
109
167
|
return () => {
|
|
110
168
|
subscription.remove();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_videoReactBindings","require","_react","_reactNative","_shouldDisableIOSLocalVideoOnBackground","_rxSubjects","_videoClient","PIP_CHANGE_EVENT","isAndroid8OrAbove","Platform","OS","Version","AppStateListener","call","useCall","appState","useRef","AppState","currentState","cameraDisabledByAppState","useEffect","disablePiP","RxUtils","getCurrentValue","disablePiPMode$","logger","videoLoggerSystem","getLogger","initialPipMode","isInPiPMode$","next","debug","NativeModules","StreamVideoReactNative","isInPiPMode","then","isInPiP","eventEmitter","NativeEventEmitter","subscriptionPiPChange","addListener","remove","
|
|
1
|
+
{"version":3,"names":["_videoReactBindings","require","_react","_reactNative","_shouldDisableIOSLocalVideoOnBackground","_rxSubjects","_videoClient","PIP_CHANGE_EVENT","ANDROID_APP_STATE_CHANGED_EVENT","isAndroid8OrAbove","Platform","OS","Version","AppStateListener","call","useCall","appState","useRef","AppState","currentState","cameraDisabledByAppState","useEffect","disablePiP","RxUtils","getCurrentValue","disablePiPMode$","logger","videoLoggerSystem","getLogger","initialPipMode","isInPiPMode$","next","debug","NativeModules","StreamVideoReactNative","isInPiPMode","then","isInPiP","eventEmitter","NativeEventEmitter","subscriptionPiPChange","addListener","remove","handleAppStateChange","nextAppState","current","match","camera","state","status","renableCamera","Promise","resolve","disable","enable","catch","e","warn","isCallAliveConfigured","resume","disableCameraIfNeeded","shouldDisableIOSLocalVideoOnBackgroundRef","nativeModule","StreamVideoAppLifecycle","cancelled","getCurrentAppState","initialState","subscription","addEventListener","exports"],"sourceRoot":"../../../../src","sources":["providers/StreamCall/AppStateListener.tsx"],"mappings":"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","ignoreList":[]}
|
package/dist/commonjs/version.js
CHANGED
|
@@ -5,6 +5,7 @@ import { shouldDisableIOSLocalVideoOnBackgroundRef } from '../../utils/internal/
|
|
|
5
5
|
import { disablePiPMode$, isInPiPMode$ } from '../../utils/internal/rxSubjects';
|
|
6
6
|
import { RxUtils, videoLoggerSystem } from '@stream-io/video-client';
|
|
7
7
|
const PIP_CHANGE_EVENT = 'StreamVideoReactNative_PIP_CHANGE_EVENT';
|
|
8
|
+
const ANDROID_APP_STATE_CHANGED_EVENT = 'StreamVideoAppLifecycle_APP_STATE_CHANGED';
|
|
8
9
|
const isAndroid8OrAbove = Platform.OS === 'android' && Platform.Version >= 26;
|
|
9
10
|
|
|
10
11
|
// Does 2 functionalities:
|
|
@@ -38,34 +39,55 @@ export const AppStateListener = () => {
|
|
|
38
39
|
};
|
|
39
40
|
}, []);
|
|
40
41
|
useEffect(() => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
const logger = videoLoggerSystem.getLogger('AppStateListener');
|
|
43
|
+
const handleAppStateChange = nextAppState => {
|
|
44
|
+
logger.debug('AppState changed to ', nextAppState, ' from ', appState.current);
|
|
45
|
+
|
|
46
|
+
// due to strange behavior in iOS when app goes to "inactive" state
|
|
47
|
+
// we dont check for inactive states
|
|
48
|
+
// ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
|
|
46
49
|
if (appState.current.match(/background/) && nextAppState === 'active') {
|
|
47
50
|
if (call?.camera?.state.status === 'enabled') {
|
|
51
|
+
logger.debug('attempt to Disable and reenable camera as app came to foreground');
|
|
48
52
|
// Android: when device is locked and resumed, the status isnt made disabled but stays enabled
|
|
49
53
|
// iOS PiP: when local track was replaced by remote track, the local track shown is blank
|
|
50
54
|
// as a workaround we stop the track and enable again if its already in enabled state
|
|
51
|
-
|
|
52
|
-
call?.camera
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
const renableCamera = () => {
|
|
56
|
+
const camera = call?.camera;
|
|
57
|
+
if (!camera) return Promise.resolve();
|
|
58
|
+
return camera.disable(true).then(() => camera.enable()).catch(e => {
|
|
59
|
+
logger.warn('Failed to disable+reenable camera as app came to foreground', e);
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
if (Platform.OS === 'android') {
|
|
63
|
+
NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(isCallAliveConfigured => {
|
|
64
|
+
if (!isCallAliveConfigured) {
|
|
65
|
+
renableCamera();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
} else {
|
|
69
|
+
renableCamera();
|
|
70
|
+
}
|
|
55
71
|
} else {
|
|
56
72
|
if (cameraDisabledByAppState.current) {
|
|
57
|
-
call?.camera?.resume()
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
call?.camera?.resume().then(() => {
|
|
74
|
+
cameraDisabledByAppState.current = false;
|
|
75
|
+
logger.debug('Camera resumed as app came to foreground');
|
|
76
|
+
}).catch(e => {
|
|
77
|
+
logger.warn('Failed to resume camera as app came to foreground', e);
|
|
78
|
+
});
|
|
60
79
|
}
|
|
61
80
|
}
|
|
62
81
|
appState.current = nextAppState;
|
|
63
82
|
} else if (appState.current === 'active' && nextAppState.match(/background/)) {
|
|
64
83
|
const disableCameraIfNeeded = () => {
|
|
65
84
|
if (call?.camera?.state.status === 'enabled') {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
call?.camera?.disable().then(() => {
|
|
86
|
+
cameraDisabledByAppState.current = true;
|
|
87
|
+
logger.debug('Camera disabled by app going to background');
|
|
88
|
+
}).catch(e => {
|
|
89
|
+
logger.warn('Failed to disable camera as app went to background', e);
|
|
90
|
+
});
|
|
69
91
|
}
|
|
70
92
|
};
|
|
71
93
|
if (Platform.OS === 'android') {
|
|
@@ -85,7 +107,13 @@ export const AppStateListener = () => {
|
|
|
85
107
|
// this happens on foreground push notifications
|
|
86
108
|
return;
|
|
87
109
|
}
|
|
88
|
-
|
|
110
|
+
// check if keep call alive is configured
|
|
111
|
+
// if not, then disable the camera as we are not able to keep the call alive in the background
|
|
112
|
+
NativeModules.StreamVideoReactNative.isCallAliveConfigured().then(isCallAliveConfigured => {
|
|
113
|
+
if (!isCallAliveConfigured) {
|
|
114
|
+
disableCameraIfNeeded();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
89
117
|
}
|
|
90
118
|
});
|
|
91
119
|
} else {
|
|
@@ -99,6 +127,36 @@ export const AppStateListener = () => {
|
|
|
99
127
|
}
|
|
100
128
|
appState.current = nextAppState;
|
|
101
129
|
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// for Android use our custom native module to listen to app state changes
|
|
133
|
+
// because the default react-native AppState listener works for activity and ours works for application process
|
|
134
|
+
if (Platform.OS === 'android') {
|
|
135
|
+
const nativeModule = NativeModules.StreamVideoAppLifecycle;
|
|
136
|
+
const eventEmitter = new NativeEventEmitter(nativeModule);
|
|
137
|
+
let cancelled = false;
|
|
138
|
+
nativeModule.getCurrentAppState().then(initialState => {
|
|
139
|
+
if (cancelled) return;
|
|
140
|
+
if (initialState === 'active' || initialState === 'background') {
|
|
141
|
+
appState.current = initialState;
|
|
142
|
+
}
|
|
143
|
+
}).catch(() => {
|
|
144
|
+
logger.warn('Failed to get current app state from native module');
|
|
145
|
+
});
|
|
146
|
+
const subscription = eventEmitter.addListener(ANDROID_APP_STATE_CHANGED_EVENT, nextAppState => {
|
|
147
|
+
if (nextAppState === 'active' || nextAppState === 'background') {
|
|
148
|
+
handleAppStateChange(nextAppState);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
return () => {
|
|
152
|
+
cancelled = true;
|
|
153
|
+
subscription.remove();
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// for iOS use the default react-native AppState listener
|
|
158
|
+
const subscription = AppState.addEventListener('change', nextAppState => {
|
|
159
|
+
handleAppStateChange(nextAppState);
|
|
102
160
|
});
|
|
103
161
|
return () => {
|
|
104
162
|
subscription.remove();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useCall","useEffect","useRef","AppState","NativeEventEmitter","NativeModules","Platform","shouldDisableIOSLocalVideoOnBackgroundRef","disablePiPMode$","isInPiPMode$","RxUtils","videoLoggerSystem","PIP_CHANGE_EVENT","isAndroid8OrAbove","OS","Version","AppStateListener","call","appState","currentState","cameraDisabledByAppState","disablePiP","getCurrentValue","logger","getLogger","initialPipMode","next","debug","StreamVideoReactNative","isInPiPMode","then","isInPiP","eventEmitter","subscriptionPiPChange","addListener","remove","
|
|
1
|
+
{"version":3,"names":["useCall","useEffect","useRef","AppState","NativeEventEmitter","NativeModules","Platform","shouldDisableIOSLocalVideoOnBackgroundRef","disablePiPMode$","isInPiPMode$","RxUtils","videoLoggerSystem","PIP_CHANGE_EVENT","ANDROID_APP_STATE_CHANGED_EVENT","isAndroid8OrAbove","OS","Version","AppStateListener","call","appState","currentState","cameraDisabledByAppState","disablePiP","getCurrentValue","logger","getLogger","initialPipMode","next","debug","StreamVideoReactNative","isInPiPMode","then","isInPiP","eventEmitter","subscriptionPiPChange","addListener","remove","handleAppStateChange","nextAppState","current","match","camera","state","status","renableCamera","Promise","resolve","disable","enable","catch","e","warn","isCallAliveConfigured","resume","disableCameraIfNeeded","nativeModule","StreamVideoAppLifecycle","cancelled","getCurrentAppState","initialState","subscription","addEventListener"],"sourceRoot":"../../../../src","sources":["providers/StreamCall/AppStateListener.tsx"],"mappings":"AAAA,SAASA,OAAO,QAAQ,iCAAiC;AACzD,SAASC,SAAS,EAAEC,MAAM,QAAQ,OAAO;AACzC,SACEC,QAAQ,EAERC,kBAAkB,EAClBC,aAAa,EACbC,QAAQ,QACH,cAAc;AACrB,SAASC,yCAAyC,QAAQ,6DAA6D;AACvH,SAASC,eAAe,EAAEC,YAAY,QAAQ,iCAAiC;AAC/E,SAASC,OAAO,EAAEC,iBAAiB,QAAQ,yBAAyB;AAEpE,MAAMC,gBAAgB,GAAG,yCAAyC;AAClE,MAAMC,+BAA+B,GACnC,2CAA2C;AAE7C,MAAMC,iBAAiB,GAAGR,QAAQ,CAACS,EAAE,KAAK,SAAS,IAAIT,QAAQ,CAACU,OAAO,IAAI,EAAE;;AAE7E;AACA;AACA;AACA,OAAO,MAAMC,gBAAgB,GAAGA,CAAA,KAAM;EACpC,MAAMC,IAAI,GAAGlB,OAAO,CAAC,CAAC;EACtB,MAAMmB,QAAQ,GAAGjB,MAAM,CAAiBC,QAAQ,CAACiB,YAAY,CAAC;EAC9D,MAAMC,wBAAwB,GAAGnB,MAAM,CAAU,KAAK,CAAC;;EAEvD;EACAD,SAAS,CAAC,MAAM;IACd,IAAI,CAACa,iBAAiB,EAAE;MACtB;IACF;IAEA,MAAMQ,UAAU,GAAGZ,OAAO,CAACa,eAAe,CAACf,eAAe,CAAC;IAC3D,MAAMgB,MAAM,GAAGb,iBAAiB,CAACc,SAAS,CAAC,kBAAkB,CAAC;IAC9D,MAAMC,cAAc,GAClB,CAACJ,UAAU,IAAInB,QAAQ,CAACiB,YAAY,KAAK,YAAY;IACvDX,YAAY,CAACkB,IAAI,CAACD,cAAc,CAAC;IACjCF,MAAM,CAACI,KAAK,CAAC,mCAAmC,EAAEF,cAAc,CAAC;IAEjErB,aAAa,EAAEwB,sBAAsB,EAAEC,WAAW,CAAC,CAAC,CAACC,IAAI,CACtDC,OAAmC,IAAK;MACvCvB,YAAY,CAACkB,IAAI,CAAC,CAAC,CAACK,OAAO,CAAC;MAC5BR,MAAM,CAACI,KAAK,CACV,gEAAgE,EAChE,CAAC,CAACI,OACJ,CAAC;IACH,CACF,CAAC;IAED,MAAMC,YAAY,GAAG,IAAI7B,kBAAkB,CACzCC,aAAa,CAACwB,sBAChB,CAAC;IAED,MAAMK,qBAAqB,GAAGD,YAAY,CAACE,WAAW,CACpDvB,gBAAgB,EACfkB,WAAoB,IAAK;MACxBrB,YAAY,CAACkB,IAAI,CAACG,WAAW,CAAC;IAChC,CACF,CAAC;IAED,OAAO,MAAM;MACXI,qBAAqB,CAACE,MAAM,CAAC,CAAC;IAChC,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAENnC,SAAS,CAAC,MAAM;IACd,MAAMuB,MAAM,GAAGb,iBAAiB,CAACc,SAAS,CAAC,kBAAkB,CAAC;IAE9D,MAAMY,oBAAoB,GAAIC,YAA4B,IAAK;MAC7Dd,MAAM,CAACI,KAAK,CACV,sBAAsB,EACtBU,YAAY,EACZ,QAAQ,EACRnB,QAAQ,CAACoB,OACX,CAAC;;MAED;MACA;MACA;MACA,IAAIpB,QAAQ,CAACoB,OAAO,CAACC,KAAK,CAAC,YAAY,CAAC,IAAIF,YAAY,KAAK,QAAQ,EAAE;QACrE,IAAIpB,IAAI,EAAEuB,MAAM,EAAEC,KAAK,CAACC,MAAM,KAAK,SAAS,EAAE;UAC5CnB,MAAM,CAACI,KAAK,CACV,kEACF,CAAC;UACD;UACA;UACA;UACA,MAAMgB,aAAa,GAAGA,CAAA,KAAM;YAC1B,MAAMH,MAAM,GAAGvB,IAAI,EAAEuB,MAAM;YAC3B,IAAI,CAACA,MAAM,EAAE,OAAOI,OAAO,CAACC,OAAO,CAAC,CAAC;YACrC,OAAOL,MAAM,CACVM,OAAO,CAAC,IAAI,CAAC,CACbhB,IAAI,CAAC,MAAMU,MAAM,CAACO,MAAM,CAAC,CAAC,CAAC,CAC3BC,KAAK,CAAEC,CAAC,IAAK;cACZ1B,MAAM,CAAC2B,IAAI,CACT,6DAA6D,EAC7DD,CACF,CAAC;YACH,CAAC,CAAC;UACN,CAAC;UACD,IAAI5C,QAAQ,CAACS,EAAE,KAAK,SAAS,EAAE;YAC7BV,aAAa,CAACwB,sBAAsB,CAACuB,qBAAqB,CAAC,CAAC,CAACrB,IAAI,CAC9DqB,qBAA8B,IAAK;cAClC,IAAI,CAACA,qBAAqB,EAAE;gBAC1BR,aAAa,CAAC,CAAC;cACjB;YACF,CACF,CAAC;UACH,CAAC,MAAM;YACLA,aAAa,CAAC,CAAC;UACjB;QACF,CAAC,MAAM;UACL,IAAIvB,wBAAwB,CAACkB,OAAO,EAAE;YACpCrB,IAAI,EAAEuB,MAAM,EACRY,MAAM,CAAC,CAAC,CACTtB,IAAI,CAAC,MAAM;cACVV,wBAAwB,CAACkB,OAAO,GAAG,KAAK;cACxCf,MAAM,CAACI,KAAK,CAAC,0CAA0C,CAAC;YAC1D,CAAC,CAAC,CACDqB,KAAK,CAAEC,CAAC,IAAK;cACZ1B,MAAM,CAAC2B,IAAI,CACT,mDAAmD,EACnDD,CACF,CAAC;YACH,CAAC,CAAC;UACN;QACF;QACA/B,QAAQ,CAACoB,OAAO,GAAGD,YAAY;MACjC,CAAC,MAAM,IACLnB,QAAQ,CAACoB,OAAO,KAAK,QAAQ,IAC7BD,YAAY,CAACE,KAAK,CAAC,YAAY,CAAC,EAChC;QACA,MAAMc,qBAAqB,GAAGA,CAAA,KAAM;UAClC,IAAIpC,IAAI,EAAEuB,MAAM,EAAEC,KAAK,CAACC,MAAM,KAAK,SAAS,EAAE;YAC5CzB,IAAI,EAAEuB,MAAM,EACRM,OAAO,CAAC,CAAC,CACVhB,IAAI,CAAC,MAAM;cACVV,wBAAwB,CAACkB,OAAO,GAAG,IAAI;cACvCf,MAAM,CAACI,KAAK,CAAC,4CAA4C,CAAC;YAC5D,CAAC,CAAC,CACDqB,KAAK,CAAEC,CAAC,IAAK;cACZ1B,MAAM,CAAC2B,IAAI,CACT,oDAAoD,EACpDD,CACF,CAAC;YACH,CAAC,CAAC;UACN;QACF,CAAC;QACD,IAAI5C,QAAQ,CAACS,EAAE,KAAK,SAAS,EAAE;UAC7B;UACA;UACA,IAAID,iBAAiB,EAAE;YACrB;YACA,MAAMQ,UAAU,GAAGZ,OAAO,CAACa,eAAe,CAACf,eAAe,CAAC;YAC3DC,YAAY,CAACkB,IAAI,CAAC,CAACL,UAAU,CAAC;YAC9B;YACAjB,aAAa,EAAEwB,sBAAsB,EAAEC,WAAW,CAAC,CAAC,CAACC,IAAI,CACtDC,OAAmC,IAAK;cACvCvB,YAAY,CAACkB,IAAI,CAAC,CAAC,CAACK,OAAO,CAAC;cAC5B,IAAI,CAACA,OAAO,EAAE;gBACZ,IAAI7B,QAAQ,CAACiB,YAAY,KAAK,QAAQ,EAAE;kBACtC;kBACA;kBACA;kBACA;gBACF;gBACA;gBACA;gBACAf,aAAa,CAACwB,sBAAsB,CAACuB,qBAAqB,CAAC,CAAC,CAACrB,IAAI,CAC9DqB,qBAA8B,IAAK;kBAClC,IAAI,CAACA,qBAAqB,EAAE;oBAC1BE,qBAAqB,CAAC,CAAC;kBACzB;gBACF,CACF,CAAC;cACH;YACF,CACF,CAAC;UACH,CAAC,MAAM;YACLA,qBAAqB,CAAC,CAAC;UACzB;QACF,CAAC,MAAM;UACL;UACA,IAAI/C,yCAAyC,CAACgC,OAAO,EAAE;YACrDe,qBAAqB,CAAC,CAAC;UACzB;QACF;QACAnC,QAAQ,CAACoB,OAAO,GAAGD,YAAY;MACjC;IACF,CAAC;;IAED;IACA;IACA,IAAIhC,QAAQ,CAACS,EAAE,KAAK,SAAS,EAAE;MAC7B,MAAMwC,YAAY,GAAGlD,aAAa,CAACmD,uBAAuB;MAC1D,MAAMvB,YAAY,GAAG,IAAI7B,kBAAkB,CAACmD,YAAY,CAAC;MACzD,IAAIE,SAAS,GAAG,KAAK;MAErBF,YAAY,CACTG,kBAAkB,CAAC,CAAC,CACpB3B,IAAI,CAAE4B,YAA+C,IAAK;QACzD,IAAIF,SAAS,EAAE;QACf,IAAIE,YAAY,KAAK,QAAQ,IAAIA,YAAY,KAAK,YAAY,EAAE;UAC9DxC,QAAQ,CAACoB,OAAO,GAAGoB,YAAY;QACjC;MACF,CAAC,CAAC,CACDV,KAAK,CAAC,MAAM;QACXzB,MAAM,CAAC2B,IAAI,CAAC,oDAAoD,CAAC;MACnE,CAAC,CAAC;MAEJ,MAAMS,YAAY,GAAG3B,YAAY,CAACE,WAAW,CAC3CtB,+BAA+B,EAC9ByB,YAA4B,IAAK;QAChC,IAAIA,YAAY,KAAK,QAAQ,IAAIA,YAAY,KAAK,YAAY,EAAE;UAC9DD,oBAAoB,CAACC,YAAY,CAAC;QACpC;MACF,CACF,CAAC;MAED,OAAO,MAAM;QACXmB,SAAS,GAAG,IAAI;QAChBG,YAAY,CAACxB,MAAM,CAAC,CAAC;MACvB,CAAC;IACH;;IAEA;IACA,MAAMwB,YAAY,GAAGzD,QAAQ,CAAC0D,gBAAgB,CAAC,QAAQ,EAAGvB,YAAY,IAAK;MACzED,oBAAoB,CAACC,YAAY,CAAC;IACpC,CAAC,CAAC;IAEF,OAAO,MAAM;MACXsB,YAAY,CAACxB,MAAM,CAAC,CAAC;IACvB,CAAC;EACH,CAAC,EAAE,CAAClB,IAAI,CAAC,CAAC;EAEV,OAAO,IAAI;AACb,CAAC","ignoreList":[]}
|
package/dist/module/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '1.27.
|
|
1
|
+
export const version = '1.27.4';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AppStateListener.d.ts","sourceRoot":"","sources":["../../../../src/providers/StreamCall/AppStateListener.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AppStateListener.d.ts","sourceRoot":"","sources":["../../../../src/providers/StreamCall/AppStateListener.tsx"],"names":[],"mappings":"AAsBA,eAAO,MAAM,gBAAgB,YAuN5B,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const version = "1.27.
|
|
1
|
+
export declare const version = "1.27.4";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stream-io/video-react-native-sdk",
|
|
3
|
-
"version": "1.27.
|
|
3
|
+
"version": "1.27.4",
|
|
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/",
|
|
@@ -50,8 +50,8 @@
|
|
|
50
50
|
"!**/.*"
|
|
51
51
|
],
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@stream-io/video-client": "1.40.
|
|
54
|
-
"@stream-io/video-react-bindings": "1.12.
|
|
53
|
+
"@stream-io/video-client": "1.40.3",
|
|
54
|
+
"@stream-io/video-react-bindings": "1.12.10",
|
|
55
55
|
"intl-pluralrules": "2.0.1",
|
|
56
56
|
"lodash.merge": "^4.6.2",
|
|
57
57
|
"react-native-url-polyfill": "^3.0.0",
|
|
@@ -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 () => {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.27.
|
|
1
|
+
export const version = '1.27.4';
|