@stream-io/video-react-native-sdk 0.1.11 → 0.1.13

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 (88) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNative.kt +19 -1
  3. package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +82 -2
  4. package/dist/commonjs/components/Call/CallContent/CallContent.js +8 -6
  5. package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
  6. package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js +7 -2
  7. package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
  8. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -1
  9. package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
  10. package/dist/commonjs/components/Call/CallTopView/ParticipantsInfoBadge.js +4 -2
  11. package/dist/commonjs/components/Call/CallTopView/ParticipantsInfoBadge.js.map +1 -1
  12. package/dist/commonjs/constants/TestIds.js +2 -0
  13. package/dist/commonjs/constants/TestIds.js.map +1 -1
  14. package/dist/commonjs/hooks/index.js +22 -0
  15. package/dist/commonjs/hooks/index.js.map +1 -1
  16. package/dist/commonjs/hooks/useAutoEnterPiPEffect.js +20 -0
  17. package/dist/commonjs/hooks/useAutoEnterPiPEffect.js.map +1 -0
  18. package/dist/commonjs/hooks/useIsInPiPMode.js +25 -0
  19. package/dist/commonjs/hooks/useIsInPiPMode.js.map +1 -0
  20. package/dist/commonjs/providers/MediaStreamManagement.js +15 -2
  21. package/dist/commonjs/providers/MediaStreamManagement.js.map +1 -1
  22. package/dist/commonjs/utils/enterPiPAndroid.js +15 -0
  23. package/dist/commonjs/utils/enterPiPAndroid.js.map +1 -0
  24. package/dist/commonjs/utils/index.js +12 -0
  25. package/dist/commonjs/utils/index.js.map +1 -1
  26. package/dist/commonjs/version.js +1 -1
  27. package/dist/module/components/Call/CallContent/CallContent.js +9 -6
  28. package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
  29. package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js +7 -2
  30. package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
  31. package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -1
  32. package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
  33. package/dist/module/components/Call/CallTopView/ParticipantsInfoBadge.js +4 -2
  34. package/dist/module/components/Call/CallTopView/ParticipantsInfoBadge.js.map +1 -1
  35. package/dist/module/constants/TestIds.js +2 -0
  36. package/dist/module/constants/TestIds.js.map +1 -1
  37. package/dist/module/hooks/index.js +2 -0
  38. package/dist/module/hooks/index.js.map +1 -1
  39. package/dist/module/hooks/useAutoEnterPiPEffect.js +14 -0
  40. package/dist/module/hooks/useAutoEnterPiPEffect.js.map +1 -0
  41. package/dist/module/hooks/useIsInPiPMode.js +19 -0
  42. package/dist/module/hooks/useIsInPiPMode.js.map +1 -0
  43. package/dist/module/providers/MediaStreamManagement.js +15 -2
  44. package/dist/module/providers/MediaStreamManagement.js.map +1 -1
  45. package/dist/module/utils/enterPiPAndroid.js +9 -0
  46. package/dist/module/utils/enterPiPAndroid.js.map +1 -0
  47. package/dist/module/utils/index.js +1 -0
  48. package/dist/module/utils/index.js.map +1 -1
  49. package/dist/module/version.js +1 -1
  50. package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
  51. package/dist/typescript/components/Call/CallLayout/CallParticipantsGrid.d.ts.map +1 -1
  52. package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts.map +1 -1
  53. package/dist/typescript/components/Call/CallTopView/ParticipantsInfoBadge.d.ts.map +1 -1
  54. package/dist/typescript/constants/TestIds.d.ts +2 -0
  55. package/dist/typescript/constants/TestIds.d.ts.map +1 -1
  56. package/dist/typescript/hooks/index.d.ts +2 -0
  57. package/dist/typescript/hooks/index.d.ts.map +1 -1
  58. package/dist/typescript/hooks/useAutoEnterPiPEffect.d.ts +2 -0
  59. package/dist/typescript/hooks/useAutoEnterPiPEffect.d.ts.map +1 -0
  60. package/dist/typescript/hooks/useIsInPiPMode.d.ts +2 -0
  61. package/dist/typescript/hooks/useIsInPiPMode.d.ts.map +1 -0
  62. package/dist/typescript/providers/MediaStreamManagement.d.ts.map +1 -1
  63. package/dist/typescript/utils/enterPiPAndroid.d.ts +2 -0
  64. package/dist/typescript/utils/enterPiPAndroid.d.ts.map +1 -0
  65. package/dist/typescript/utils/index.d.ts +1 -0
  66. package/dist/typescript/utils/index.d.ts.map +1 -1
  67. package/dist/typescript/version.d.ts +1 -1
  68. package/expo-config-plugin/dist/common/addNewLinesToMainActivity.d.ts +1 -0
  69. package/expo-config-plugin/dist/common/addNewLinesToMainActivity.js +16 -0
  70. package/expo-config-plugin/dist/common/types.d.ts +4 -0
  71. package/expo-config-plugin/dist/index.js +6 -2
  72. package/expo-config-plugin/dist/withAndroidManifest.d.ts +2 -1
  73. package/expo-config-plugin/dist/withAndroidManifest.js +20 -2
  74. package/expo-config-plugin/dist/withMainActivity.d.ts +4 -0
  75. package/expo-config-plugin/dist/withMainActivity.js +72 -0
  76. package/package.json +3 -3
  77. package/src/components/Call/CallContent/CallContent.tsx +14 -6
  78. package/src/components/Call/CallLayout/CallParticipantsGrid.tsx +16 -2
  79. package/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx +4 -1
  80. package/src/components/Call/CallTopView/ParticipantsInfoBadge.tsx +2 -0
  81. package/src/constants/TestIds.ts +2 -0
  82. package/src/hooks/index.ts +2 -0
  83. package/src/hooks/useAutoEnterPiPEffect.tsx +16 -0
  84. package/src/hooks/useIsInPiPMode.tsx +29 -0
  85. package/src/providers/MediaStreamManagement.tsx +14 -1
  86. package/src/utils/enterPiPAndroid.ts +11 -0
  87. package/src/utils/index.ts +1 -0
  88. package/src/version.ts +1 -1
@@ -10,20 +10,24 @@ const withMainApplication_1 = __importDefault(require("./withMainApplication"));
10
10
  const withAndroidPermissions_1 = __importDefault(require("./withAndroidPermissions"));
11
11
  const withAndroidManifest_1 = __importDefault(require("./withAndroidManifest"));
12
12
  const withiOSInfoPlist_1 = __importDefault(require("./withiOSInfoPlist"));
13
+ const withMainActivity_1 = __importDefault(require("./withMainActivity"));
13
14
  const withBuildProperties_1 = __importDefault(require("./withBuildProperties"));
14
15
  const withAppBuildGradle_1 = __importDefault(require("./withAppBuildGradle"));
15
16
  // path should be relative to dist
16
17
  const pkg = require('../../package.json');
17
18
  const withStreamVideoReactNativeSDK = (config, props) => {
18
19
  return (0, config_plugins_1.withPlugins)(config, [
20
+ // ios
19
21
  () => (0, withPushAppDelegate_1.default)(config, props),
20
22
  withStreamVideoReactNativeSDKAppDelegate_1.default,
23
+ () => (0, withiOSInfoPlist_1.default)(config, props),
24
+ // android
21
25
  withMainApplication_1.default,
22
26
  withAndroidPermissions_1.default,
23
- withAndroidManifest_1.default,
24
27
  withAppBuildGradle_1.default,
25
28
  withBuildProperties_1.default,
26
- () => (0, withiOSInfoPlist_1.default)(config, props),
29
+ () => (0, withAndroidManifest_1.default)(config, props),
30
+ () => (0, withMainActivity_1.default)(config, props),
27
31
  ]);
28
32
  };
29
33
  exports.default = (0, config_plugins_1.createRunOncePlugin)(withStreamVideoReactNativeSDK, pkg.name, pkg.version);
@@ -1,3 +1,4 @@
1
1
  import { ConfigPlugin } from '@expo/config-plugins';
2
- declare const withStreamVideoReactNativeSDKManifest: ConfigPlugin;
2
+ import { ConfigProps } from './common/types';
3
+ declare const withStreamVideoReactNativeSDKManifest: ConfigPlugin<ConfigProps>;
3
4
  export default withStreamVideoReactNativeSDKManifest;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const config_plugins_1 = require("@expo/config-plugins");
4
- const { prefixAndroidKeys, getMainApplicationOrThrow } = config_plugins_1.AndroidConfig.Manifest;
4
+ const { prefixAndroidKeys, getMainApplicationOrThrow, getMainActivityOrThrow } = config_plugins_1.AndroidConfig.Manifest;
5
5
  function getNotifeeService() {
6
6
  /*
7
7
  <service
@@ -18,19 +18,37 @@ function getNotifeeService() {
18
18
  $: head,
19
19
  };
20
20
  }
21
- const withStreamVideoReactNativeSDKManifest = (configuration) => {
21
+ const withStreamVideoReactNativeSDKManifest = (configuration, props) => {
22
22
  return (0, config_plugins_1.withAndroidManifest)(configuration, (config) => {
23
23
  try {
24
24
  const androidManifest = config.modResults;
25
25
  const mainApplication = getMainApplicationOrThrow(androidManifest);
26
+ /* Add the notifeee Service */
26
27
  let services = mainApplication.service ?? [];
27
28
  // we filter out the existing notifee service (if any) so that we can override it
28
29
  services = services.filter((service) => service.$['android:name'] !== 'app.notifee.core.ForegroundService');
29
30
  services.push(getNotifeeService());
30
31
  mainApplication.service = services;
32
+ if (props?.androidPictureInPicture) {
33
+ const mainActivity = getMainActivityOrThrow(androidManifest);
34
+ ('keyboard|keyboardHidden|orientation|screenSize|uiMode');
35
+ const currentConfigChangesArray = mainActivity.$['android:configChanges']
36
+ ? mainActivity.$['android:configChanges'].split('|')
37
+ : [];
38
+ const neededConfigChangesArray = 'screenSize|smallestScreenSize|screenLayout|orientation'.split('|');
39
+ // Create a Set from the two arrays.
40
+ const set = new Set([
41
+ ...currentConfigChangesArray,
42
+ ...neededConfigChangesArray,
43
+ ]);
44
+ const mergedConfigChanges = [...set];
45
+ mainActivity.$['android:configChanges'] = mergedConfigChanges.join('|');
46
+ mainActivity.$['android:supportsPictureInPicture'] = 'true';
47
+ }
31
48
  config.modResults = androidManifest;
32
49
  }
33
50
  catch (error) {
51
+ console.log(error);
34
52
  throw new Error('Cannot setup StreamVideoReactNativeSDK because the AndroidManifest is malformed');
35
53
  }
36
54
  return config;
@@ -0,0 +1,4 @@
1
+ import { ConfigPlugin } from '@expo/config-plugins';
2
+ import { ConfigProps } from './common/types';
3
+ declare const withStreamVideoReactNativeSDKMainActivity: ConfigPlugin<ConfigProps>;
4
+ export default withStreamVideoReactNativeSDKMainActivity;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const config_plugins_1 = require("@expo/config-plugins");
7
+ const codeMod_1 = require("@expo/config-plugins/build/android/codeMod");
8
+ const addNewLinesToMainActivity_1 = __importDefault(require("./common/addNewLinesToMainActivity"));
9
+ const withStreamVideoReactNativeSDKMainActivity = (configuration, props) => {
10
+ return (0, config_plugins_1.withMainActivity)(configuration, (config) => {
11
+ if (['java'].includes(config.modResults.language)) {
12
+ try {
13
+ /*
14
+ import com.streamvideo.reactnative.StreamVideoReactNative;
15
+ import android.util.Rational;
16
+ import androidx.lifecycle.Lifecycle;
17
+ import android.app.PictureInPictureParams;
18
+ */
19
+ config.modResults.contents = (0, codeMod_1.addImports)(config.modResults.contents, [
20
+ 'com.streamvideo.reactnative.StreamVideoReactNative',
21
+ 'android.util.Rational',
22
+ 'androidx.lifecycle.Lifecycle',
23
+ 'android.app.PictureInPictureParams',
24
+ ], config.modResults.language === 'java');
25
+ config.modResults.contents = addOnPictureInPictureModeChanged(config.modResults.contents);
26
+ if (props?.androidPictureInPicture?.enableAutomaticEnter) {
27
+ config.modResults.contents = addOnUserLeaveHint(config.modResults.contents);
28
+ }
29
+ }
30
+ catch (error) {
31
+ throw new Error("Cannot add StreamVideoReactNativeSDK to the project's MainApplication because it's malformed.");
32
+ }
33
+ }
34
+ else {
35
+ throw new Error('Cannot setup StreamVideoReactNativeSDK because the MainApplication is not in Java');
36
+ }
37
+ return config;
38
+ });
39
+ };
40
+ function addOnPictureInPictureModeChanged(contents) {
41
+ if (!contents.includes('StreamVideoReactNative.onPictureInPictureModeChanged')) {
42
+ const statementToInsert = `
43
+ @Override
44
+ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
45
+ super.onPictureInPictureModeChanged(isInPictureInPictureMode);
46
+ if (getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
47
+ // when user clicks on Close button of PIP
48
+ finishAndRemoveTask();
49
+ } else {
50
+ StreamVideoReactNative.onPictureInPictureModeChanged(isInPictureInPictureMode);
51
+ }
52
+ }`;
53
+ contents = (0, addNewLinesToMainActivity_1.default)(contents, statementToInsert.trim().split('\n'));
54
+ }
55
+ return contents;
56
+ }
57
+ function addOnUserLeaveHint(contents) {
58
+ if (!contents.includes('StreamVideoReactNative.canAutoEnterPictureInPictureMode')) {
59
+ const statementToInsert = `
60
+ @Override
61
+ public void onUserLeaveHint () {
62
+ if (StreamVideoReactNative.canAutoEnterPictureInPictureMode) {
63
+ PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();
64
+ builder.setAspectRatio(new Rational(480, 640));
65
+ enterPictureInPictureMode(builder.build());
66
+ }
67
+ }`;
68
+ contents = (0, addNewLinesToMainActivity_1.default)(contents, statementToInsert.trim().split('\n'));
69
+ }
70
+ return contents;
71
+ }
72
+ exports.default = withStreamVideoReactNativeSDKMainActivity;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-native-sdk",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/commonjs/index.js",
6
6
  "module": "dist/module/index.js",
@@ -45,8 +45,8 @@
45
45
  "!**/.*"
46
46
  ],
47
47
  "dependencies": {
48
- "@stream-io/video-client": "^0.3.35",
49
- "@stream-io/video-react-bindings": "^0.2.36",
48
+ "@stream-io/video-client": "^0.3.36",
49
+ "@stream-io/video-react-bindings": "^0.2.37",
50
50
  "intl-pluralrules": "2.0.1",
51
51
  "lodash.merge": "^4.6.2",
52
52
  "react-native-url-polyfill": "1.3.0",
@@ -30,6 +30,7 @@ import {
30
30
  CallParticipantsListComponentProps,
31
31
  CallParticipantsListProps,
32
32
  } from '../CallParticipantsList';
33
+ import { useIsInPiPMode, useAutoEnterPiPEffect } from '../../../hooks';
33
34
 
34
35
  export type StreamReactionType = StreamReaction & {
35
36
  icon: string;
@@ -111,19 +112,24 @@ export const CallContent = ({
111
112
  useLocalParticipant,
112
113
  } = useCallStateHooks();
113
114
 
115
+ useAutoEnterPiPEffect();
116
+
114
117
  const _remoteParticipants = useRemoteParticipants();
115
118
  const remoteParticipants = useDebouncedValue(_remoteParticipants, 300); // we debounce the remote participants to avoid unnecessary rerenders that happen when participant tracks are all subscribed simultaneously
116
119
  const localParticipant = useLocalParticipant();
117
-
120
+ const isInPiPMode = useIsInPiPMode();
118
121
  const hasScreenShare = useHasOngoingScreenShare();
119
122
  const showSpotlightLayout = hasScreenShare || layout === 'spotlight';
120
123
 
121
124
  const showFloatingView =
122
125
  !showSpotlightLayout &&
126
+ !isInPiPMode &&
123
127
  remoteParticipants.length > 0 &&
124
128
  remoteParticipants.length < 3;
125
129
  const isRemoteParticipantInFloatingView =
126
- showRemoteParticipantInFloatingView && remoteParticipants.length === 1;
130
+ showFloatingView &&
131
+ showRemoteParticipantInFloatingView &&
132
+ remoteParticipants.length === 1;
127
133
 
128
134
  /**
129
135
  * This hook is used to handle IncallManager specs of the application.
@@ -150,8 +156,10 @@ export const CallContent = ({
150
156
  }, []);
151
157
 
152
158
  const participantViewProps: ParticipantViewComponentProps = {
153
- ParticipantLabel,
154
- ParticipantNetworkQualityIndicator,
159
+ ParticipantLabel: isInPiPMode ? null : ParticipantLabel,
160
+ ParticipantNetworkQualityIndicator: isInPiPMode
161
+ ? null
162
+ : ParticipantNetworkQualityIndicator,
155
163
  ParticipantReaction,
156
164
  ParticipantVideoFallback,
157
165
  VideoRenderer,
@@ -187,7 +195,7 @@ export const CallContent = ({
187
195
  // and allows only the top and floating view (its child views) to take up the touches
188
196
  pointerEvents="box-none"
189
197
  >
190
- {CallTopView && (
198
+ {!isInPiPMode && CallTopView && (
191
199
  <CallTopView
192
200
  onBackPressed={onBackPressed}
193
201
  onParticipantInfoPress={onParticipantInfoPress}
@@ -214,7 +222,7 @@ export const CallContent = ({
214
222
  )}
215
223
  </View>
216
224
 
217
- {CallControls && (
225
+ {!isInPiPMode && CallControls && (
218
226
  <CallControls
219
227
  onHangupCallHandler={onHangupCallHandler}
220
228
  landscape={landscape}
@@ -10,6 +10,7 @@ import { ComponentTestIds } from '../../../constants/TestIds';
10
10
  import { useTheme } from '../../../contexts/ThemeContext';
11
11
  import { CallContentProps } from '../CallContent';
12
12
  import { ParticipantViewComponentProps } from '../../Participant';
13
+ import { useIsInPiPMode } from '../../../hooks';
13
14
 
14
15
  /**
15
16
  * Props for the CallParticipantsGrid component.
@@ -58,15 +59,28 @@ export const CallParticipantsGrid = ({
58
59
  flexDirection: landscape ? 'row' : 'column',
59
60
  };
60
61
 
62
+ const isInPiPMode = useIsInPiPMode();
63
+
61
64
  const showFloatingView =
62
- remoteParticipants.length > 0 && remoteParticipants.length < 3;
65
+ !isInPiPMode &&
66
+ remoteParticipants.length > 0 &&
67
+ remoteParticipants.length < 3;
63
68
 
64
- const participants = showFloatingView
69
+ let participants = showFloatingView
65
70
  ? showLocalParticipant && localParticipant
66
71
  ? [localParticipant]
67
72
  : remoteParticipants
68
73
  : allParticipants;
69
74
 
75
+ if (isInPiPMode) {
76
+ participants =
77
+ remoteParticipants.length > 0
78
+ ? [remoteParticipants[0]]
79
+ : localParticipant
80
+ ? [localParticipant]
81
+ : [];
82
+ }
83
+
70
84
  const participantViewProps: CallParticipantsListComponentProps = {
71
85
  ParticipantView,
72
86
  ParticipantLabel,
@@ -18,6 +18,7 @@ import {
18
18
  } from '../../Participant';
19
19
  import { useTheme } from '../../../contexts/ThemeContext';
20
20
  import { CallContentProps } from '../CallContent';
21
+ import { useIsInPiPMode } from '../../../hooks';
21
22
 
22
23
  /**
23
24
  * Props for the CallParticipantsSpotlight component.
@@ -62,6 +63,8 @@ export const CallParticipantsSpotlight = ({
62
63
  const isScreenShareOnSpotlight = hasScreenShare(participantInSpotlight);
63
64
  const isUserAloneInCall = _allParticipants?.length === 1;
64
65
 
66
+ const isInPiP = useIsInPiPMode();
67
+
65
68
  const participantViewProps: ParticipantViewComponentProps = {
66
69
  ParticipantLabel,
67
70
  ParticipantNetworkQualityIndicator,
@@ -117,7 +120,7 @@ export const CallParticipantsSpotlight = ({
117
120
  {...participantViewProps}
118
121
  />
119
122
  )}
120
- {!isUserAloneInCall && (
123
+ {!isInPiP && !isUserAloneInCall && (
121
124
  <View
122
125
  style={[
123
126
  styles.callParticipantsListContainer,
@@ -80,6 +80,7 @@ export const ParticipantsInfoBadge = ({
80
80
  },
81
81
  participantInfoBadge.participantCountContainer,
82
82
  ]}
83
+ testID={ButtonTestIds.PARTICIPANTS_COUNT_VIEW}
83
84
  >
84
85
  <Text
85
86
  style={[
@@ -90,6 +91,7 @@ export const ParticipantsInfoBadge = ({
90
91
  typefaces.subtitle,
91
92
  participantInfoBadge.participantsCountText,
92
93
  ]}
94
+ testID={ButtonTestIds.PARTICIPANTS_COUNT_TEXT}
93
95
  >
94
96
  {count}
95
97
  </Text>
@@ -19,6 +19,8 @@ export enum ComponentTestIds {
19
19
 
20
20
  export enum ButtonTestIds {
21
21
  PARTICIPANTS_INFO = 'participants-info-button',
22
+ PARTICIPANTS_COUNT_VIEW = 'participants-count-view',
23
+ PARTICIPANTS_COUNT_TEXT = 'participants-count-text',
22
24
  HANG_UP_CALL = 'hang-up-call',
23
25
  REACTION = 'call-controls-reaction',
24
26
  }
@@ -3,3 +3,5 @@ export * from './usePermissionRequest';
3
3
  export * from './usePermissionNotification';
4
4
  export * from './push';
5
5
  export * from './useAndroidKeepCallAliveEffect';
6
+ export * from './useIsInPiPMode';
7
+ export * from './useAutoEnterPiPEffect';
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react';
2
+ import { NativeModules, Platform } from 'react-native';
3
+
4
+ export function useAutoEnterPiPEffect() {
5
+ useEffect(() => {
6
+ if (Platform.OS !== 'android') {
7
+ return;
8
+ }
9
+
10
+ NativeModules.StreamVideoReactNative.canAutoEnterPipMode(true);
11
+
12
+ return () => {
13
+ NativeModules.StreamVideoReactNative.canAutoEnterPipMode(false);
14
+ };
15
+ }, []);
16
+ }
@@ -0,0 +1,29 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
3
+
4
+ export function useIsInPiPMode() {
5
+ const [isInPiPMode, setIsInPiPMode] = useState(false);
6
+
7
+ useEffect(() => {
8
+ if (Platform.OS !== 'android') {
9
+ return;
10
+ }
11
+
12
+ const eventEmitter = new NativeEventEmitter(
13
+ NativeModules.StreamVideoReactNative,
14
+ );
15
+
16
+ const subscription = eventEmitter.addListener(
17
+ 'StreamVideoReactNative_PIP_CHANGE_EVENT',
18
+ (isPiPEnabled: boolean) => {
19
+ setIsInPiPMode(isPiPEnabled);
20
+ },
21
+ );
22
+
23
+ return () => {
24
+ subscription.remove();
25
+ };
26
+ }, []);
27
+
28
+ return isInPiPMode;
29
+ }
@@ -1,6 +1,7 @@
1
1
  import React, { PropsWithChildren, useEffect, useState } from 'react';
2
2
  import { useCall, useCallStateHooks } from '@stream-io/video-react-bindings';
3
3
  import { useAppStateListener } from '../utils/hooks';
4
+ import { NativeModules, Platform } from 'react-native';
4
5
 
5
6
  export type MediaDevicesInitialState = {
6
7
  /**
@@ -38,7 +39,19 @@ export const MediaStreamManagement = ({
38
39
  call?.camera?.resume();
39
40
  },
40
41
  () => {
41
- call?.camera?.disable();
42
+ if (Platform.OS === 'android') {
43
+ // in Android, we need to check if we are in PiP mode
44
+ // in PiP mode, we don't want to disable the camera
45
+ NativeModules?.StreamVideoReactNative?.isInPiPMode().then(
46
+ (isInPiP: boolean) => {
47
+ if (!isInPiP) {
48
+ call?.camera?.disable();
49
+ }
50
+ },
51
+ );
52
+ } else {
53
+ call?.camera?.disable();
54
+ }
42
55
  },
43
56
  );
44
57
 
@@ -0,0 +1,11 @@
1
+ import { NativeModules, Platform } from 'react-native';
2
+
3
+ export function enterPiPAndroid(width?: number, height?: number) {
4
+ if (Platform.OS !== 'android') {
5
+ return;
6
+ }
7
+ return NativeModules?.StreamVideoReactNative?.enterPipMode(
8
+ width ? Math.floor(width) : 0,
9
+ height ? Math.floor(height) : 0,
10
+ );
11
+ }
@@ -36,4 +36,5 @@ export const getInitialsOfName = (name: string) => {
36
36
  return initials;
37
37
  };
38
38
 
39
+ export * from './enterPiPAndroid';
39
40
  export * from './StreamVideoRN';
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '0.1.11';
1
+ export const version = '0.1.13';