@stream-io/video-react-native-sdk 0.1.11 → 0.1.12
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 +7 -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/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/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/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 +1 -1
- 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/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
|
@@ -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
|
@@ -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,
|
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.12';
|