@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.
- package/CHANGELOG.md +13 -0
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNative.kt +19 -1
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +82 -2
- package/dist/commonjs/components/Call/CallContent/CallContent.js +8 -6
- package/dist/commonjs/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js +7 -2
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -1
- package/dist/commonjs/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
- package/dist/commonjs/components/Call/CallTopView/ParticipantsInfoBadge.js +4 -2
- package/dist/commonjs/components/Call/CallTopView/ParticipantsInfoBadge.js.map +1 -1
- package/dist/commonjs/constants/TestIds.js +2 -0
- package/dist/commonjs/constants/TestIds.js.map +1 -1
- package/dist/commonjs/hooks/index.js +22 -0
- package/dist/commonjs/hooks/index.js.map +1 -1
- package/dist/commonjs/hooks/useAutoEnterPiPEffect.js +20 -0
- package/dist/commonjs/hooks/useAutoEnterPiPEffect.js.map +1 -0
- package/dist/commonjs/hooks/useIsInPiPMode.js +25 -0
- package/dist/commonjs/hooks/useIsInPiPMode.js.map +1 -0
- package/dist/commonjs/providers/MediaStreamManagement.js +15 -2
- package/dist/commonjs/providers/MediaStreamManagement.js.map +1 -1
- package/dist/commonjs/utils/enterPiPAndroid.js +15 -0
- package/dist/commonjs/utils/enterPiPAndroid.js.map +1 -0
- package/dist/commonjs/utils/index.js +12 -0
- package/dist/commonjs/utils/index.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/module/components/Call/CallContent/CallContent.js +9 -6
- package/dist/module/components/Call/CallContent/CallContent.js.map +1 -1
- package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js +7 -2
- package/dist/module/components/Call/CallLayout/CallParticipantsGrid.js.map +1 -1
- package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js +3 -1
- package/dist/module/components/Call/CallLayout/CallParticipantsSpotlight.js.map +1 -1
- package/dist/module/components/Call/CallTopView/ParticipantsInfoBadge.js +4 -2
- package/dist/module/components/Call/CallTopView/ParticipantsInfoBadge.js.map +1 -1
- package/dist/module/constants/TestIds.js +2 -0
- package/dist/module/constants/TestIds.js.map +1 -1
- package/dist/module/hooks/index.js +2 -0
- package/dist/module/hooks/index.js.map +1 -1
- package/dist/module/hooks/useAutoEnterPiPEffect.js +14 -0
- package/dist/module/hooks/useAutoEnterPiPEffect.js.map +1 -0
- package/dist/module/hooks/useIsInPiPMode.js +19 -0
- package/dist/module/hooks/useIsInPiPMode.js.map +1 -0
- package/dist/module/providers/MediaStreamManagement.js +15 -2
- package/dist/module/providers/MediaStreamManagement.js.map +1 -1
- package/dist/module/utils/enterPiPAndroid.js +9 -0
- package/dist/module/utils/enterPiPAndroid.js.map +1 -0
- package/dist/module/utils/index.js +1 -0
- package/dist/module/utils/index.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/typescript/components/Call/CallContent/CallContent.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallLayout/CallParticipantsGrid.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallLayout/CallParticipantsSpotlight.d.ts.map +1 -1
- package/dist/typescript/components/Call/CallTopView/ParticipantsInfoBadge.d.ts.map +1 -1
- package/dist/typescript/constants/TestIds.d.ts +2 -0
- package/dist/typescript/constants/TestIds.d.ts.map +1 -1
- package/dist/typescript/hooks/index.d.ts +2 -0
- package/dist/typescript/hooks/index.d.ts.map +1 -1
- package/dist/typescript/hooks/useAutoEnterPiPEffect.d.ts +2 -0
- package/dist/typescript/hooks/useAutoEnterPiPEffect.d.ts.map +1 -0
- package/dist/typescript/hooks/useIsInPiPMode.d.ts +2 -0
- package/dist/typescript/hooks/useIsInPiPMode.d.ts.map +1 -0
- package/dist/typescript/providers/MediaStreamManagement.d.ts.map +1 -1
- package/dist/typescript/utils/enterPiPAndroid.d.ts +2 -0
- package/dist/typescript/utils/enterPiPAndroid.d.ts.map +1 -0
- package/dist/typescript/utils/index.d.ts +1 -0
- package/dist/typescript/utils/index.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/expo-config-plugin/dist/common/addNewLinesToMainActivity.d.ts +1 -0
- package/expo-config-plugin/dist/common/addNewLinesToMainActivity.js +16 -0
- package/expo-config-plugin/dist/common/types.d.ts +4 -0
- package/expo-config-plugin/dist/index.js +6 -2
- package/expo-config-plugin/dist/withAndroidManifest.d.ts +2 -1
- package/expo-config-plugin/dist/withAndroidManifest.js +20 -2
- package/expo-config-plugin/dist/withMainActivity.d.ts +4 -0
- package/expo-config-plugin/dist/withMainActivity.js +72 -0
- package/package.json +3 -3
- package/src/components/Call/CallContent/CallContent.tsx +14 -6
- package/src/components/Call/CallLayout/CallParticipantsGrid.tsx +16 -2
- package/src/components/Call/CallLayout/CallParticipantsSpotlight.tsx +4 -1
- package/src/components/Call/CallTopView/ParticipantsInfoBadge.tsx +2 -0
- package/src/constants/TestIds.ts +2 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useAutoEnterPiPEffect.tsx +16 -0
- package/src/hooks/useIsInPiPMode.tsx +29 -0
- package/src/providers/MediaStreamManagement.tsx +14 -1
- package/src/utils/enterPiPAndroid.ts +11 -0
- package/src/utils/index.ts +1 -0
- 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,
|
|
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
|
-
|
|
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,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.
|
|
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.
|
|
49
|
-
"@stream-io/video-react-bindings": "^0.2.
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
!isInPiPMode &&
|
|
66
|
+
remoteParticipants.length > 0 &&
|
|
67
|
+
remoteParticipants.length < 3;
|
|
63
68
|
|
|
64
|
-
|
|
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>
|
package/src/constants/TestIds.ts
CHANGED
|
@@ -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
|
}
|
package/src/hooks/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
+
}
|
package/src/utils/index.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '0.1.
|
|
1
|
+
export const version = '0.1.13';
|