@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 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
@@ -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(StreamVideoReactNativeModule(reactContext), StreamInCallManagerModule(reactContext))
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
- // due to strange behavior in iOS when app goes to "inactive" state
48
- // we dont check for inactive states
49
- // ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
50
- const subscription = _reactNative.AppState.addEventListener('change', nextAppState => {
51
- const logger = _videoClient.videoLoggerSystem.getLogger('AppStateListener');
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
- call?.camera?.disable(true).then(() => {
58
- call?.camera?.enable();
59
- });
60
- logger.debug('Disable and reenable camera as app came to foreground');
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
- cameraDisabledByAppState.current = false;
65
- logger.debug('Resume camera as app came to foreground');
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
- cameraDisabledByAppState.current = true;
73
- call?.camera?.disable();
74
- logger.debug('Camera disabled by app going to background');
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
- disableCameraIfNeeded();
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","subscription","addEventListener","nextAppState","current","match","camera","state","status","disable","enable","resume","disableCameraIfNeeded","shouldDisableIOSLocalVideoOnBackgroundRef","exports"],"sourceRoot":"../../../../src","sources":["providers/StreamCall/AppStateListener.tsx"],"mappings":";;;;;;AAAA,IAAAA,mBAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AACA,IAAAE,YAAA,GAAAF,OAAA;AAMA,IAAAG,uCAAA,GAAAH,OAAA;AACA,IAAAI,WAAA,GAAAJ,OAAA;AACA,IAAAK,YAAA,GAAAL,OAAA;AAEA,MAAMM,gBAAgB,GAAG,yCAAyC;AAElE,MAAMC,iBAAiB,GAAGC,qBAAQ,CAACC,EAAE,KAAK,SAAS,IAAID,qBAAQ,CAACE,OAAO,IAAI,EAAE;;AAE7E;AACA;AACA;AACO,MAAMC,gBAAgB,GAAGA,CAAA,KAAM;EACpC,MAAMC,IAAI,GAAG,IAAAC,2BAAO,EAAC,CAAC;EACtB,MAAMC,QAAQ,GAAG,IAAAC,aAAM,EAACC,qBAAQ,CAACC,YAAY,CAAC;EAC9C,MAAMC,wBAAwB,GAAG,IAAAH,aAAM,EAAU,KAAK,CAAC;;EAEvD;EACA,IAAAI,gBAAS,EAAC,MAAM;IACd,IAAI,CAACZ,iBAAiB,EAAE;MACtB;IACF;IAEA,MAAMa,UAAU,GAAGC,oBAAO,CAACC,eAAe,CAACC,2BAAe,CAAC;IAC3D,MAAMC,MAAM,GAAGC,8BAAiB,CAACC,SAAS,CAAC,kBAAkB,CAAC;IAC9D,MAAMC,cAAc,GAClB,CAACP,UAAU,IAAIJ,qBAAQ,CAACC,YAAY,KAAK,YAAY;IACvDW,wBAAY,CAACC,IAAI,CAACF,cAAc,CAAC;IACjCH,MAAM,CAACM,KAAK,CAAC,mCAAmC,EAAEH,cAAc,CAAC;IAEjEI,0BAAa,EAAEC,sBAAsB,EAAEC,WAAW,CAAC,CAAC,CAACC,IAAI,CACtDC,OAAmC,IAAK;MACvCP,wBAAY,CAACC,IAAI,CAAC,CAAC,CAACM,OAAO,CAAC;MAC5BX,MAAM,CAACM,KAAK,CACV,gEAAgE,EAChE,CAAC,CAACK,OACJ,CAAC;IACH,CACF,CAAC;IAED,MAAMC,YAAY,GAAG,IAAIC,+BAAkB,CACzCN,0BAAa,CAACC,sBAChB,CAAC;IAED,MAAMM,qBAAqB,GAAGF,YAAY,CAACG,WAAW,CACpDjC,gBAAgB,EACf2B,WAAoB,IAAK;MACxBL,wBAAY,CAACC,IAAI,CAACI,WAAW,CAAC;IAChC,CACF,CAAC;IAED,OAAO,MAAM;MACXK,qBAAqB,CAACE,MAAM,CAAC,CAAC;IAChC,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EAEN,IAAArB,gBAAS,EAAC,MAAM;IACd;IACA;IACA;IACA,MAAMsB,YAAY,GAAGzB,qBAAQ,CAAC0B,gBAAgB,CAAC,QAAQ,EAAGC,YAAY,IAAK;MACzE,MAAMnB,MAAM,GAAGC,8BAAiB,CAACC,SAAS,CAAC,kBAAkB,CAAC;MAC9D,IAAIZ,QAAQ,CAAC8B,OAAO,CAACC,KAAK,CAAC,YAAY,CAAC,IAAIF,YAAY,KAAK,QAAQ,EAAE;QACrE,IAAI/B,IAAI,EAAEkC,MAAM,EAAEC,KAAK,CAACC,MAAM,KAAK,SAAS,EAAE;UAC5C;UACA;UACA;UACApC,IAAI,EAAEkC,MAAM,EAAEG,OAAO,CAAC,IAAI,CAAC,CAACf,IAAI,CAAC,MAAM;YACrCtB,IAAI,EAAEkC,MAAM,EAAEI,MAAM,CAAC,CAAC;UACxB,CAAC,CAAC;UACF1B,MAAM,CAACM,KAAK,CAAC,uDAAuD,CAAC;QACvE,CAAC,MAAM;UACL,IAAIZ,wBAAwB,CAAC0B,OAAO,EAAE;YACpChC,IAAI,EAAEkC,MAAM,EAAEK,MAAM,CAAC,CAAC;YACtBjC,wBAAwB,CAAC0B,OAAO,GAAG,KAAK;YACxCpB,MAAM,CAACM,KAAK,CAAC,yCAAyC,CAAC;UACzD;QACF;QACAhB,QAAQ,CAAC8B,OAAO,GAAGD,YAAY;MACjC,CAAC,MAAM,IACL7B,QAAQ,CAAC8B,OAAO,KAAK,QAAQ,IAC7BD,YAAY,CAACE,KAAK,CAAC,YAAY,CAAC,EAChC;QACA,MAAMO,qBAAqB,GAAGA,CAAA,KAAM;UAClC,IAAIxC,IAAI,EAAEkC,MAAM,EAAEC,KAAK,CAACC,MAAM,KAAK,SAAS,EAAE;YAC5C9B,wBAAwB,CAAC0B,OAAO,GAAG,IAAI;YACvChC,IAAI,EAAEkC,MAAM,EAAEG,OAAO,CAAC,CAAC;YACvBzB,MAAM,CAACM,KAAK,CAAC,4CAA4C,CAAC;UAC5D;QACF,CAAC;QACD,IAAItB,qBAAQ,CAACC,EAAE,KAAK,SAAS,EAAE;UAC7B;UACA;UACA,IAAIF,iBAAiB,EAAE;YACrB;YACA,MAAMa,UAAU,GAAGC,oBAAO,CAACC,eAAe,CAACC,2BAAe,CAAC;YAC3DK,wBAAY,CAACC,IAAI,CAAC,CAACT,UAAU,CAAC;YAC9B;YACAW,0BAAa,EAAEC,sBAAsB,EAAEC,WAAW,CAAC,CAAC,CAACC,IAAI,CACtDC,OAAmC,IAAK;cACvCP,wBAAY,CAACC,IAAI,CAAC,CAAC,CAACM,OAAO,CAAC;cAC5B,IAAI,CAACA,OAAO,EAAE;gBACZ,IAAInB,qBAAQ,CAACC,YAAY,KAAK,QAAQ,EAAE;kBACtC;kBACA;kBACA;kBACA;gBACF;gBACAmC,qBAAqB,CAAC,CAAC;cACzB;YACF,CACF,CAAC;UACH,CAAC,MAAM;YACLA,qBAAqB,CAAC,CAAC;UACzB;QACF,CAAC,MAAM;UACL;UACA,IAAIC,iFAAyC,CAACT,OAAO,EAAE;YACrDQ,qBAAqB,CAAC,CAAC;UACzB;QACF;QACAtC,QAAQ,CAAC8B,OAAO,GAAGD,YAAY;MACjC;IACF,CAAC,CAAC;IAEF,OAAO,MAAM;MACXF,YAAY,CAACD,MAAM,CAAC,CAAC;IACvB,CAAC;EACH,CAAC,EAAE,CAAC5B,IAAI,CAAC,CAAC;EAEV,OAAO,IAAI;AACb,CAAC;AAAC0C,OAAA,CAAA3C,gBAAA,GAAAA,gBAAA","ignoreList":[]}
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":[]}
@@ -4,5 +4,5 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.version = void 0;
7
- const version = exports.version = '1.27.2';
7
+ const version = exports.version = '1.27.4';
8
8
  //# sourceMappingURL=version.js.map
@@ -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
- // due to strange behavior in iOS when app goes to "inactive" state
42
- // we dont check for inactive states
43
- // ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
44
- const subscription = AppState.addEventListener('change', nextAppState => {
45
- const logger = videoLoggerSystem.getLogger('AppStateListener');
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
- call?.camera?.disable(true).then(() => {
52
- call?.camera?.enable();
53
- });
54
- logger.debug('Disable and reenable camera as app came to foreground');
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
- cameraDisabledByAppState.current = false;
59
- logger.debug('Resume camera as app came to foreground');
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
- cameraDisabledByAppState.current = true;
67
- call?.camera?.disable();
68
- logger.debug('Camera disabled by app going to background');
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
- disableCameraIfNeeded();
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","subscription","addEventListener","nextAppState","current","match","camera","state","status","disable","enable","resume","disableCameraIfNeeded"],"sourceRoot":"../../../../src","sources":["providers/StreamCall/AppStateListener.tsx"],"mappings":"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","ignoreList":[]}
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":"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","ignoreList":[]}
@@ -1,2 +1,2 @@
1
- export const version = '1.27.2';
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":"AAmBA,eAAO,MAAM,gBAAgB,YAuH5B,CAAC"}
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.2";
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.2",
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.1",
54
- "@stream-io/video-react-bindings": "1.12.8",
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
- // due to strange behavior in iOS when app goes to "inactive" state
66
- // we dont check for inactive states
67
- // ref: https://www.reddit.com/r/reactnative/comments/15kib42/appstate_behavior_in_ios_when_swiping_down_to/
68
- const subscription = AppState.addEventListener('change', (nextAppState) => {
69
- const logger = videoLoggerSystem.getLogger('AppStateListener');
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
- call?.camera?.disable(true).then(() => {
76
- call?.camera?.enable();
77
- });
78
- logger.debug('Disable and reenable camera as app came to foreground');
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?.resume();
82
- cameraDisabledByAppState.current = false;
83
- logger.debug('Resume camera as app came to foreground');
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
- cameraDisabledByAppState.current = true;
94
- call?.camera?.disable();
95
- logger.debug('Camera disabled by app going to background');
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
- disableCameraIfNeeded();
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.2';
1
+ export const version = '1.27.4';