Create a React Native Video Calling App Using Twilio
By: Engineering Zenofai
Tags: JavaScript, Software Development, Android, React, React Native

Create a React Native Video Calling App Using Twilio Engineering@ZenOfAI Follow Jun 26 · 7 min read

In this post, we are going to build a React Native Android app for Video calls using Twilio.

Let’s get started with Twilio.

Twilio is a cloud based service that enables powerful communication between mobile devices, applications, services, and systems throughout the business in order to bridge the gap between conventional communication.

For that, please create a Twilio account by clicking here.

When you create your account successfully you will be redirected to Dashboard. Now we need the ACCOUNT SID for our application to generate a token as shown below.

To generate a token we also need an API key and a secret key. For that go to Settings > API keys.

Click on plus icon to generate API keys as shown below.

Provide a name and click on Create API Key.

You will get the API key and Secret key as shown below.

You can generate a token by using the code below which uses an npm package.

npm install twilio --save

We can also generate tokens in different languages. For that, we can follow this link. We need to select the video option on the page there. We are using the same code here.

const AccessToken = require('twilio').jwt.AccessToken;

const VideoGrant = AccessToken.VideoGrant; var account_sid = '<Your-Account-SID>'

var api_key = '<Generated-API-Key>'

var api_secret = '<Generated-Secret-Key>' var identity = <New-Identity> const videoGrant = new VideoGrant({

room: '<Room-Name>',

}); const token = new AccessToken(account_sid, api_key, api_secret);

token.addGrant(videoGrant);

token.identity = identity; console.log(token.toJwt());

For every token, we need to change the identity value. We cannot use the same token at two different places.

The other way to create a token is by using Twilio tools by following this link, which resembles the image shown below.

To fetch the token we must provide an identity and a room name.

This was all about the Twilio part of the app.

Installing Dependencies:

Here we are using the React Native starter kit which we can clone by using this github link.

Run the below command in the terminal project directory.

npm install https://github.com/blackuy/react-native-twilio-video-webrtc --save

And replace the code in App.js file with the below code.

App.js:

import React, {

Component

} from 'react';

import {

StyleSheet,

Text,

TextInput,

View,

Button,

TouchableOpacity,

PermissionsAndroid, TouchableHighlight

} from 'react-native';

import {

TwilioVideoLocalView, // to get local view

TwilioVideoParticipantView, //to get participant view

TwilioVideo

} from 'react-native-twilio-video-webrtc';

// make sure you install vector icons and its dependencies

import MIcon from 'react-native-vector-icons/MaterialIcons';

import normalize from 'react-native-normalize';

import MCIcon from 'react-native-vector-icons/MaterialCommunityIcons';

import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';

export async function GetAllPermissions() {

// it will ask the permission for user

try {

// if (Platform.OS === "android") {

const userResponse = await PermissionsAndroid.requestMultiple([

PermissionsAndroid.PERMISSIONS.CAMERA,

PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,

PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE

]);

return userResponse;

// }

} catch (err) {

console.log(err);

}

return null;

}

export default class Example extends Component {

state = {

isAudioEnabled: true,

isVideoEnabled: true,

isButtonDisplay: true,

status: 'disconnected',

participants: new Map(),

videoTracks: new Map(),

roomName: '',

token: '',

}



componentDidMount() {

// on start we are asking the permisions

GetAllPermissions();

} _onConnectButtonPress = () => {

console.log("in on connect button preess");

this.refs.twilioVideo.connect({ roomName: this.state.roomName, accessToken: this.state.token})

this.setState({status: 'connecting'})

console.log(this.state.status);

} _onEndButtonPress = () => {

this.refs.twilioVideo.disconnect()

} _onMuteButtonPress = () => {

// on cliking the mic button we are setting it to mute or viceversa

this.refs.twilioVideo.setLocalAudioEnabled(!this.state.isAudioEnabled)

.then(isEnabled => this.setState({isAudioEnabled: isEnabled}))

} _onFlipButtonPress = () => {

// switches between fronst camera and Rare camera

this.refs.twilioVideo.flipCamera()

}

_onRoomDidConnect = () => {

console.log("room did connected");

this.setState({status: 'connected'})

// console.log("over");

} _onRoomDidDisconnect = ({roomName, error}) => {

console.log("ERROR: ", JSON.stringify(error))

console.log("disconnected")



this.setState({status: 'disconnected'})

} _onRoomDidFailToConnect = (error) => {

console.log("ERROR: ", JSON.stringify(error));

console.log("failed to connect");

this.setState({status: 'disconnected'})

} _onParticipantAddedVideoTrack = ({participant, track}) => {

// call everytime a participant joins the same room

console.log("onParticipantAddedVideoTrack: ", participant, track) this.setState({

videoTracks: new Map([

...this.state.videoTracks,

[track.trackSid, { participantSid: participant.sid, videoTrackSid: track.trackSid }]

]),

});



console.log("this.state.videoTracks", this.state.videoTracks);

} _onParticipantRemovedVideoTrack = ({participant, track}) => {

// gets called when a participant disconnects.

console.log("onParticipantRemovedVideoTrack: ", participant, track) const videoTracks = this.state.videoTracks

videoTracks.delete(track.trackSid) this.setState({videoTracks: { ...videoTracks }})

}

render() {

return (

<View style={styles.container} >

{

this.state.status === 'disconnected' &&

<View>

<Text style={styles.welcome}>

React Native Twilio Video

</Text>

<View style={styles.spacing}>

<Text style={styles.inputLabel}>Room Name</Text>

<TextInput style={styles.inputBox}

placeholder="Room Name"

defaultValue={this.state.roomName}

onChangeText={(text) => this.setState({roomName: text})}

/>

</View>

<View style={styles.spacing}>

<Text style={styles.inputLabel}>Token</Text>

<TextInput style={styles.inputBox}

placeholder="Token"

defaultValue={this.state.token}

onChangeText={(text) => this.setState({token: text})}

/>

</View>

<TouchableHighlight style={[styles.buttonContainer, styles.loginButton]} onPress={this._onConnectButtonPress}>

<Text style={styles.Buttontext}>Connect</Text>

</TouchableHighlight>

</View>

} {

(this.state.status === 'connected' || this.state.status === 'connecting') &&

<View style={styles.callContainer}>

{

this.state.status === 'connected' &&

<View style={styles.remoteGrid}>

<TouchableOpacity style = {styles.remoteVideo} onPress={()=>{this.setState({isButtonDisplay:!this.state.isButtonDisplay})}} >

{

Array.from(this.state.videoTracks, ([trackSid, trackIdentifier]) => {

return (

<TwilioVideoParticipantView

style={styles.remoteVideo}

key={trackSid}

trackIdentifier={trackIdentifier}

/>

)

})

}

</TouchableOpacity>

<TwilioVideoLocalView

enabled={true}

style = {this.state.isButtonDisplay ? styles.localVideoOnButtonEnabled : styles.localVideoOnButtonDisabled}

/>

</View>

}

<View

style = {

{

display: this.state.isButtonDisplay ? "flex" : "none",

position: "absolute",

left: 0,

bottom: 0,

right: 0,

height: 100,

flexDirection: "row",

alignItems: "center",

justifyContent: "space-evenly",

// backgroundColor:"blue",

// zIndex: 2,

zIndex: this.state.isButtonDisplay ? 2 : 0,

}

} >

<TouchableOpacity

style={

{

display: this.state.isButtonDisplay ? "flex" : "none",

width: 60,

height: 60,

marginLeft: 10,

marginRight: 10,

borderRadius: 100 / 2,

backgroundColor: 'grey',

justifyContent: 'center',

alignItems: "center"

}

}

onPress={this._onMuteButtonPress}>

< MIcon name ={this.state.isAudioEnabled ? "mic" : "mic-off"} size={24} color='#fff' />

</TouchableOpacity>

<TouchableOpacity

style={

{

display: this.state.isButtonDisplay ? "flex" : "none",

width: 60,

height: 60,

marginLeft: 10,

marginRight: 10,

borderRadius: 100 / 2,

backgroundColor: 'grey',

justifyContent: 'center',

alignItems: "center"

}

}

onPress={this._onEndButtonPress}>

{/* <Text style={{fontSize: 12}}>End</Text> */}

< MIcon name = "call-end" size={28} color='#fff' />

</TouchableOpacity>

<TouchableOpacity

style={

{

display: this.state.isButtonDisplay ? "flex" : "none",

width: 60,

height: 60,

marginLeft: 10,

marginRight: 10,

borderRadius: 100 / 2,

backgroundColor: 'grey',

justifyContent: 'center',

alignItems: "center"

}

}

onPress={this._onFlipButtonPress}>

{/* <Text style={{fontSize: 12}}>Flip</Text> */}

< MCIcon name = "rotate-3d" size={28} color='#fff' />

</TouchableOpacity>

</View>



</View>

}

<TwilioVideo

ref="twilioVideo"

onRoomDidConnect={ this._onRoomDidConnect }

onRoomDidDisconnect={ this._onRoomDidDisconnect }

onRoomDidFailToConnect= { this._onRoomDidFailToConnect }

onParticipantAddedVideoTrack={ this._onParticipantAddedVideoTrack }

onParticipantRemovedVideoTrack= { this._onParticipantRemovedVideoTrack }

/>

</View>

)

}

} const styles = StyleSheet.create({

container: {

flex: 1,

backgroundColor: 'white'

},

callContainer: {

flex: 1,

position: "absolute",

bottom: 0,

top: 0,

left: 0,

right: 0,

minHeight:"100%"

},

welcome: {

fontSize: 30,

textAlign: 'center',

paddingTop: 40

},

input: {

height: 50,

borderWidth: 1,

marginRight: 70,

marginLeft: 70,

marginTop: 50,

textAlign: 'center',

backgroundColor: 'white'

},

button: {

marginTop: 100

},

localVideoOnButtonEnabled: {

bottom: ("40%"),

width: "35%",

left: "64%",

height: "25%",

zIndex: 2,

},

localVideoOnButtonDisabled: {

bottom: ("30%"),

width: "35%",

left: "64%",

height: "25%",

zIndex: 2,

},

remoteGrid: {

flex: 1,

flexDirection: "column",

},

remoteVideo: {

width: wp("100%"),

height: hp("100%"),

zIndex: 1,

},

optionsContainer: {

position: "absolute",

left: 0,

bottom: 0,

right: 0,

height: 100,

flexDirection: "row",

alignItems: "center",

justifyContent: "space-evenly",

zIndex: 2,

},

optionButton: {

width: 60,

height: 60,

marginLeft: 10,

marginRight: 10,

borderRadius: 100 / 2,

backgroundColor: 'grey',

justifyContent: 'center',

alignItems: "center"

},

spacing: {

padding: 10

},

inputLabel: {

fontSize: 18

},

buttonContainer: {

height: normalize(45),

flexDirection: 'row',

justifyContent: 'center',

alignItems: 'center',

marginBottom: 20,

width: wp('90%'),

borderRadius: 30,

},

loginButton: {

backgroundColor: "#1E3378",

width: wp('90%'),

justifyContent: 'center',

alignItems: 'center',

marginLeft: 20,

marginTop: 10

},

Buttontext: {

color: 'white',

fontWeight: '500',

fontSize: 18

},

inputBox: {

borderBottomColor: '#cccccc',

fontSize: 16,

width: wp("95%"),

borderBottomWidth:1

},

});

We must make sure that we install all the dependencies required by executing the following command inside the project directory.

npm install

Before running this app we need to make some configurations to use Twilio and use camera and audio.

To make those goto android folder and in settings.gradle file add the following two lines.

include ':react-native-twilio-video-webrtc'

project(':react-native-twilio-video-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-twilio-video-webrtc/android')

In android > app > build.gradle file , Search for dependencies and add the below code inside the block.

compile project(':react-native-twilio-video-webrtc')

In android > app > src > main > java > com > reactnativestarter > MainApplication.java add the below line.

import com.twiliorn.library.TwilioPackage;

And replace the getPackages() method with below code.

@Override

protected List<ReactPackage> getPackages() {



List<ReactPackage> packages = new PackageList(this).getPackages();



// Packages that cannot be autolinked yet can be added manually here, for example:

// packages.add(new MyReactNativePackage());

packages.add(new TwilioPackage());



return packages;

} protected List getPackages() { @SuppressWarnings ("UnnecessaryLocalVariable")List packages = new PackageList(this).getPackages();// Packages that cannot be autolinked yet can be added manually here, for example:// packages.add(new MyReactNativePackage());packages.add(new TwilioPackage());return packages;

Now we need to modify AndroidManifest.xml for requesting the permissions to the user.

Add the below code in AndroidManifest.xml file.

And also make sure that the client side room creation is enabled in your twilio account as shown in the below image.

Running the App:

Run the application by executing the react-native run-android command from the terminal window.

Below are the screenshots of the app running on two different android devices.

Thanks for the read!

This story is authored by Dheeraj Kumar and Santosh Kumar. Dheeraj is a software engineer specializing in React Native and React based frontend development. Santosh specializes in Cloud Services based development.