-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(react-native): add expo video sample app (#974)
This PR focuses on adding expo video sample app for React Native SDK --------- Co-authored-by: Santhosh Vaiyapuri <[email protected]> Co-authored-by: Santhosh Vaiyapuri <[email protected]> Co-authored-by: Vishal Narkhede <[email protected]>
- Loading branch information
1 parent
ae25639
commit 3c61756
Showing
134 changed files
with
7,502 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./expo-config-plugin/build'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
...act-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/02-expo.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
--- | ||
title: Expo | ||
description: Install the SDK in Expo Development Builds | ||
--- | ||
|
||
Our SDK is not available on Expo Go due to native code being required, but you can use the [expo-dev-client](https://docs.expo.dev/development/create-development-builds/) library to run your Expo app with a development build. | ||
|
||
## Development Build | ||
|
||
If you haven't already, prepare your project for [expo development builds](https://docs.expo.dev/develop/development-builds/installation/). | ||
|
||
## SDK Installation | ||
|
||
Add the SDK and its required dependencies to your project: | ||
|
||
```bash title=Terminal | ||
npx expo install @stream-io/video-react-native-sdk | ||
npx expo install @stream-io/react-native-webrtc | ||
npx expo install @config-plugins/react-native-webrtc | ||
npx expo install react-native-incall-manager | ||
npx expo install react-native-svg | ||
npx expo install @react-native-community/netinfo | ||
npx expo install @notifee/react-native | ||
``` | ||
|
||
So what did we install precisely? | ||
|
||
- `@stream-io/video-react-native-sdk` (SVRN) is Stream's Video SDK which contains UI components, hooks and util functions that will enable audio/video calls. | ||
- `@stream-io/react-native-webrtc` is a WebRTC module for React Native, SVRN depends on this dependency, it's components and utilities to render audio/video tracks and interact with the phone's media devices. | ||
- `react-native-incall-manager` handles media-routes/sensors/events during an audio/video call. | ||
- `react-native-svg` provides SVG support to React Native, SVRN's components and it's icons are reliant on this dependency. | ||
- `@react-native-community/netinfo` - is used to detect the device's connectivity state, type and quality. | ||
- `@notifee/react-native` - is used to keep calls alive in the background on Android. | ||
|
||
## Add config plugin | ||
|
||
Add the config plugin for [`@stream-io/video-react-native-sdk`](https://github.com/GetStream/stream-video-js/tree/main/packages/react-native-sdk/expo-config-plugin/README.md) and [`react-native-webrtc`](https://www.npmjs.com/package/@config-plugins/react-native-webrtc) to your `app.json` file: | ||
|
||
```js title=app.json | ||
{ | ||
"expo": { | ||
... | ||
"plugins": [ | ||
// highlight-start | ||
"@stream-io/video-react-native-sdk", | ||
[ | ||
"@config-plugins/react-native-webrtc", | ||
{ | ||
// optionally you can add your own explanations for permissions on iOS | ||
"cameraPermission": "Allow $(PRODUCT_NAME) to access your camera", | ||
"microphonePermission": "Allow $(PRODUCT_NAME) to access your microphone" | ||
} | ||
] | ||
// highlight-end | ||
] | ||
} | ||
} | ||
``` | ||
|
||
:::note | ||
The `POST_NOTIFICATIONS`, `BLUETOOTH_CONNECT`, `BLUETOOTH` and `BLUETOOTH_ADMIN` permissions need to be requested and granted by the user as well. [PermissionsAndroid](https://reactnative.dev/docs/permissionsandroid) module can be used to request permissions in Android. For example, below is a way to request Bluetooth permissions in Android according to the OS version: | ||
|
||
```js | ||
import { useEffect } from 'react'; | ||
import { PermissionsAndroid, Platform } from 'react-native'; | ||
|
||
useEffect(() => { | ||
const run = async () => { | ||
if (Platform.OS === 'android' && Platform.Version <= 30) { | ||
// highlight-start | ||
await PermissionsAndroid.requestMultiple([ | ||
PermissionsAndroid.PERMISSIONS.BLUETOOTH, | ||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_ADMIN, | ||
]); | ||
} else { | ||
await PermissionsAndroid.request( | ||
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT, | ||
); | ||
// highlight-end | ||
} | ||
}; | ||
run(); | ||
}, []); | ||
``` | ||
|
||
::: |
3 changes: 3 additions & 0 deletions
3
...ges/react-native-sdk/docusaurus/docs/reactnative/01-setup/02-installation/_category_.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"label": "Installation" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
This config plugin is built to auto configure the `@stream-io/video-react-native-sdk` with the native changes. | ||
|
||
After installing the `@stream-io/video-react-native-sdk` you can simply add the plugin in the `app.json` or `app.config.js` of your project as: | ||
|
||
```json | ||
{ | ||
"expo": { | ||
"plugins": ["@stream-io/video-react-native-sdk"] | ||
} | ||
} | ||
``` | ||
|
||
Next you can run the code using `yarn run android` and `yarn run ios`. | ||
|
||
## Changes | ||
|
||
The plugin adds the following native changes to the code. | ||
|
||
### Android | ||
|
||
#### `MainApplication.java` | ||
|
||
Adds the import and setup for StreamVideoReactNative in your `MainApplication.java` file: | ||
|
||
Read more about it [here](https://getstream.io/video/docs/reactnative/setup/installation/react-native/#add-stream-video-sdks-setup-method). | ||
|
||
```java | ||
// Adds this | ||
import com.streamvideo.reactnative.StreamVideoReactNative; | ||
|
||
public class MainApplication extends Application implements ReactApplication { | ||
|
||
@Override | ||
public void onCreate() { | ||
super.onCreate(); | ||
// Adds this | ||
StreamVideoReactNative.setup(); | ||
// the rest.. | ||
} | ||
} | ||
``` | ||
|
||
#### `AndroidManifest.xml` | ||
|
||
Add service named `app.notifee.core.ForegroundService`. | ||
|
||
```xml | ||
<service android:name="app.notifee.core.ForegroundService" android:stopWithTask="true" android:foregroundServiceType="microphone"/> | ||
``` | ||
|
||
The `@stream-io/video-react-native-sdk` also adds the appropriate android permissions such as `POST_NOTIFICATIONS`, `FOREGROUND_SERVICE`, `FOREGROUND_SERVICE_MICROPHONE`, `BLUETOOTH`, `BLUETOOTH_ADMIN` and `BLUETOOTH_CONNECT` to the `AndroidManifest.xml`. | ||
|
||
### iOS | ||
|
||
#### `AppDelegate.mm` | ||
|
||
Adds the import and setup for StreamVideoReactNative in your `AppDelegate.mm` file: | ||
|
||
Read more about it [here](https://getstream.io/video/docs/reactnative/setup/installation/react-native/#add-stream-video-sdks-setup-method). | ||
|
||
```c | ||
// Adds this | ||
#import "StreamVideoReactNative.h" | ||
|
||
@implementation AppDelegate | ||
|
||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | ||
{ | ||
// Adds this | ||
[StreamVideoReactNative setup]; | ||
|
||
// the rest.. | ||
} | ||
``` | ||
|
||
### `Info.plist` | ||
|
||
Adds `audio` to the `UIBackgroundModes` in Info.plist as: | ||
|
||
```xml | ||
<key>UIBackgroundModes</key> | ||
<array> | ||
<string>audio</string> | ||
</array> | ||
``` |
92 changes: 92 additions & 0 deletions
92
packages/react-native-sdk/expo-config-plugin/__tests__/withAndroidManifest.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import withStreamVideoReactNativeSDKManifest from '../src/withAndroidManifest'; | ||
import { ExpoConfig } from '@expo/config-types'; | ||
import { AndroidConfig } from '@expo/config-plugins'; | ||
import { getFixturePath } from '../fixtures'; | ||
|
||
// Define a custom type that extends ExpoConfig | ||
interface CustomExpoConfig extends ExpoConfig { | ||
modResults: AndroidConfig.Manifest.AndroidManifest; | ||
} | ||
|
||
// the real withAndroidManifest doesnt return the updated config | ||
// so we mock it to return the updated config using the callback we pass in the actual implementation | ||
jest.mock('@expo/config-plugins', () => { | ||
const originalModule = jest.requireActual('@expo/config-plugins'); | ||
return { | ||
...originalModule, | ||
withAndroidManifest: jest.fn((config, callback) => { | ||
const updatedConfig: CustomExpoConfig = callback( | ||
config as CustomExpoConfig, | ||
); | ||
return updatedConfig; | ||
}), | ||
}; | ||
}); | ||
|
||
const readAndroidManifestAsync = | ||
AndroidConfig.Manifest.readAndroidManifestAsync; | ||
|
||
const getMainApplicationOrThrow = | ||
AndroidConfig.Manifest.getMainApplicationOrThrow; | ||
|
||
const sampleManifestPath = getFixturePath('AndroidManifest.xml'); | ||
|
||
describe('withStreamVideoReactNativeSDKManifest', () => { | ||
let modifiedConfig: CustomExpoConfig | undefined; | ||
it('should modify Android Manifest', async () => { | ||
const manifest = await readAndroidManifestAsync(sampleManifestPath); | ||
// Prepare a mock config | ||
const config: CustomExpoConfig = { | ||
name: 'test-app', | ||
slug: 'test-app', | ||
modResults: manifest, | ||
}; | ||
|
||
const updatedConfig = withStreamVideoReactNativeSDKManifest( | ||
config, | ||
) as CustomExpoConfig; | ||
|
||
const mainApp = getMainApplicationOrThrow(updatedConfig.modResults); | ||
|
||
expect( | ||
mainApp.service?.some( | ||
(service) => | ||
service.$['android:name'] === 'app.notifee.core.ForegroundService', | ||
), | ||
).toBe(true); | ||
|
||
modifiedConfig = updatedConfig; | ||
}); | ||
|
||
it('should not create duplicates', () => { | ||
expect(modifiedConfig?.modResults).toBeDefined(); | ||
|
||
const updatedConfig = withStreamVideoReactNativeSDKManifest( | ||
modifiedConfig!, | ||
) as CustomExpoConfig; | ||
|
||
const mainApp = getMainApplicationOrThrow(updatedConfig.modResults); | ||
|
||
expect( | ||
mainApp.service?.filter( | ||
(service) => | ||
service.$['android:name'] === 'app.notifee.core.ForegroundService', | ||
).length, | ||
).toBe(1); | ||
|
||
modifiedConfig = updatedConfig; | ||
}); | ||
|
||
it('should throw error for malformed manifest', () => { | ||
// Prepare a mock config | ||
const config: CustomExpoConfig = { | ||
name: 'test-app', | ||
slug: 'test-app', | ||
modResults: { | ||
// @ts-expect-error: we are testing malformed manifest | ||
bla: 'blabla', | ||
}, | ||
}; | ||
expect(() => withStreamVideoReactNativeSDKManifest(config)).toThrow(); | ||
}); | ||
}); |
30 changes: 30 additions & 0 deletions
30
packages/react-native-sdk/expo-config-plugin/__tests__/withAndroidPermissions.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import withStreamVideoReactNativeSDKAndroidPermissions from '../src/withAndroidPermissions'; | ||
import { ExpoConfig } from '@expo/config-types'; | ||
|
||
describe('withStreamVideoReactNativeSDKAndroidPermissions', () => { | ||
it('should add specified permissions to Android config', () => { | ||
const inputConfig: ExpoConfig = { | ||
// Your initial configuration here | ||
name: 'test-app', | ||
slug: 'test-app', | ||
android: { | ||
permissions: [], | ||
}, | ||
}; | ||
|
||
const updatedConfig = | ||
withStreamVideoReactNativeSDKAndroidPermissions(inputConfig); | ||
|
||
// Assert that the necessary permissions are added to the Android config | ||
expect(updatedConfig?.android?.permissions).toEqual( | ||
expect.arrayContaining([ | ||
'android.permission.POST_NOTIFICATIONS', | ||
'android.permission.FOREGROUND_SERVICE', | ||
'android.permission.FOREGROUND_SERVICE_MICROPHONE', | ||
'android.permission.BLUETOOTH', | ||
'android.permission.BLUETOOTH_CONNECT', | ||
'android.permission.BLUETOOTH_ADMIN', | ||
]), | ||
); | ||
}); | ||
}); |
Oops, something went wrong.