Skip to main content
Version: 3.x.x

Quick Start for Conference in Android

This quick start shows how to build a group video calling app with the VideoSDK Android SDK, step by step.

Prerequisites​

Before proceeding, ensure that your development environment meets the following requirements:

  • Java Development Kit.
  • Android Studio Narwhal 3 Feature Drop (2025.1.3) or later.
  • Android SDK API Level 24 or higher.
  • A mobile device that runs Android 7.0 or later with an ARM (arm64-v8a or armeabi-v7a) processor, or an emulator with an arm64-v8a system image.
important

You need a VideoSDK account to generate a token. Generate the token on the VideoSDK dashboard.

Getting Started with the Code!​

Follow the steps to create the environment necessary to add video calls into your app.

Create new Android Project​

For a new project in Android Studio, create a Phone and Tablet Android project with an Empty Views Activity. This template adds AppCompat and Material Components, which this quick start uses.

VideoSDK Android Quick Start New Project

caution

After creating the project, Android Studio automatically starts gradle sync. Ensure that the sync succeeds before you continue.

Integrate Video SDK​

settings.gradle.kts
dependencyResolutionManagement {
repositories {
// ...
google()
mavenCentral()
}
}
  • Add the following dependency in your app's app/build.gradle.kts.
app/build.gradle.kts
dependencies {
implementation("live.videosdk:rtc-android-sdk:3.0.0")

// library to perform Network call to generate a room id
implementation("com.squareup.okhttp3:okhttp:4.12.0")

// lifecycleScope, to call the SDK's suspend functions
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")

// RecyclerView, to show the participants
implementation("androidx.recyclerview:recyclerview:1.3.2")

// other app dependencies
}
important

Android SDK runs on arm64-v8a and armeabi-v7a devices only. If you want to run the application in an emulator, choose an arm64-v8a system image when creating a device. x86 / x86_64 emulator images are not supported.

Add permissions into your project​

  • In /app/Manifests/AndroidManifest.xml, add the following permissions after </application>. The SDK doesn't add INTERNET or MODIFY_AUDIO_SETTINGS to your app, so declare them here.
AndroidManifest.xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

Structure of the project​

Your project structure should look like this.

Project Structure
app
├── java
│ ├── packagename
│ ├── JoinActivity
│ ├── MainApplication
│ ├── RoomActivity
│ ├── ParticipantAdapter
├── res
│ ├── layout
│ │ ├── activity_join.xml
│ │ ├── activity_room.xml
│ │ ├── item_remote_peer.xml
note

You have to set JoinActivity as Launcher activity.

App Architecture​

Step 1: Initialize VideoSDK​

  1. Create MainApplication class which will extend the android.app.Application.
MainApplication.kt
import android.app.Application
import live.videosdk.rtc.android.VideoSDK

class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
VideoSDK.initialize(applicationContext)
}
}
  1. Add MainApplication to AndroidManifest.xml
AndroidManifest.xml
<application
android:name=".MainApplication" >
<!-- ... -->
<activity
android:name=".RoomActivity"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboardHidden" />
</application>

android:configChanges keeps RoomActivity, and its room, alive when the device rotates. A recreated RoomActivity would call join() again, which the SDK refuses while the first room is still joined.

Step 2: Creating Joining Screen​

Create a new Activity named JoinActivity. The Joining screen will include:

  1. Create Button - This button will create a new room for you.
  2. TextField for Room ID - This text field will contain the room ID you want to join.
  3. Join Button - This button will join the room with the roomId you provided.

In /app/res/layout/activity_join.xml file, replace the content with the following.

activity_join.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".JoinActivity">

<Button
android:id="@+id/btnCreateRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="Create Room" />

<TextView
style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="OR" />

<com.google.android.material.textfield.TextInputLayout
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:hint="Enter Room ID">

<EditText
android:id="@+id/etRoomId"
android:layout_width="250dp"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>

<Button
android:id="@+id/btnJoinRoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Join Room" />
</LinearLayout>

Here is the implementation for the Join Activity:

  1. Create field sampleToken in JoinActivity which will hold the generated token from the VideoSDK dashboard. This token is passed to VideoSDK.createRoom() and is also used to generate a roomId.
JoinActivity.kt
class JoinActivity : AppCompatActivity() {

//Replace with the token you generated from the VideoSDK Dashboard
private var sampleToken = ""

override fun onCreate(savedInstanceState: Bundle?) {
//...
}
}
  1. On Join Button onClick events, we will navigate to RoomActivity with the token and roomId.
JoinActivity.kt
class JoinActivity : AppCompatActivity() {

//Replace with the token you generated from the VideoSDK Dashboard
private var sampleToken = ""

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_join)

val btnCreate = findViewById<Button>(R.id.btnCreateRoom)
val btnJoin = findViewById<Button>(R.id.btnJoinRoom)
val etRoomId = findViewById<EditText>(R.id.etRoomId)

btnCreate.setOnClickListener { v: View? ->
createRoomId(sampleToken)
}
btnJoin.setOnClickListener { v: View? ->
val intent = Intent(this@JoinActivity, RoomActivity::class.java)
intent.putExtra("token", sampleToken)
intent.putExtra("roomId", etRoomId.text.toString())
startActivity(intent)
}
}

private fun createRoomId(token: String) {
// we will explore this method in the next step
}
}
  1. For Create Button, under createRoomId method we will generate a roomId by calling the API and navigate to RoomActivity with the token and generated roomId.
JoinActivity.kt
class JoinActivity : AppCompatActivity() {
//...onCreate
private fun createRoomId(token: String) {
// we will make an API call to VideoSDK Server to get a roomId
lifecycleScope.launch {
try {
val roomId = withContext(Dispatchers.IO) {
val request = Request.Builder()
.url("https://api.videosdk.live/v2/rooms")
.header("Authorization", token) //we will pass the token in the Headers
.post("".toRequestBody())
.build()
OkHttpClient().newCall(request).execute().use { response ->
val body = response.body?.string().orEmpty()
check(response.isSuccessful) { "HTTP ${response.code}: $body" }
// response will contain `roomId`
JSONObject(body).getString("roomId")
}
}

// starting the RoomActivity with received roomId and our sampleToken
val intent = Intent(this@JoinActivity, RoomActivity::class.java)
intent.putExtra("token", sampleToken)
intent.putExtra("roomId", roomId)
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this@JoinActivity, e.message, Toast.LENGTH_SHORT).show()
}
}
}
}
note

The VideoSDK dashboard and some REST API fields still say "meeting" (for example meetingId). A meeting there is the same thing as a room.

  1. Our App is completely based on audio and video communication, that's why we need to ask for runtime permissions RECORD_AUDIO and CAMERA. So, we will implement permission logic on JoinActivity.
JoinActivity.kt
class JoinActivity : AppCompatActivity() {
companion object {
private const val PERMISSION_REQ_ID = 22
private val REQUESTED_PERMISSIONS = arrayOf(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.CAMERA
)
}

private fun checkSelfPermission(permission: String, requestCode: Int): Boolean {
if (ContextCompat.checkSelfPermission(this, permission) !=
PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode)
return false
}
return true
}

override fun onCreate(savedInstanceState: Bundle?) {
//... button listeners
checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID)
checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID)
}
}

Output​

VideoSDK Android Quick Start joining Screen

Step 3: Creating Room Screen​

Create a new Activity named RoomActivity.

In /app/res/layout/activity_room.xml file, replace the content with the following.

activity_room.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".RoomActivity">

<TextView
android:id="@+id/tvRoomId"
style="@style/TextAppearance.AppCompat.Display1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Room ID" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvParticipants"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<Button
android:id="@+id/btnMic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:text="Mic"/>

<Button
android:id="@+id/btnLeave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:layout_marginHorizontal="8dp"
android:text="Leave"/>

<Button
android:id="@+id/btnWebcam"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginVertical="8dp"
android:text="Webcam" />

</LinearLayout>

</LinearLayout>

The next step is to create the room, after getting the token and roomId from JoinActivity:

  1. Create the room with VideoSDK.createRoom(), passing roomId, token, participantName, micEnabled, camEnabled and more. It throws VideoSDKException for an empty token or roomId.
  2. Add a RoomEventListener to listen for events such as Room Joined/Left and Participant Joined/Left.
  3. Join the room with room.join() method. It is a suspend function, so call it from a coroutine and catch VideoSDKException.
RoomActivity.kt
class RoomActivity : AppCompatActivity() {
// declare the variables we will be using to handle the room
private var room: Room? = null
private var micEnabled = true
private var camEnabled = true

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_room)

val token = intent.getStringExtra("token").orEmpty()
val roomId = intent.getStringExtra("roomId").orEmpty()
val participantName = "John Doe"

try {
// 1. Create the VideoSDK room
room = VideoSDK.createRoom(
roomId = roomId,
token = token,
participantName = participantName,
micEnabled = micEnabled,
camEnabled = camEnabled,
)
} catch (e: VideoSDKException) {
// thrown for an empty token or room id
Toast.makeText(this, e.message, Toast.LENGTH_LONG).show()
finish()
return
}

// 2. Add event listener to listen for events
room!!.addEventListener(roomEventListener)

// 3. Join the room. join() is a suspend function, so call it from a coroutine.
lifecycleScope.launch {
try {
room?.join()
} catch (e: VideoSDKException) {
Toast.makeText(this@RoomActivity, "Could not join: ${e.message}", Toast.LENGTH_LONG).show()
finish()
}
}

(findViewById<View>(R.id.tvRoomId) as TextView).text = roomId
}

// creating the RoomEventListener
private val roomEventListener: RoomEventListener = object : RoomEventListener() {
override fun onRoomJoined(switchRoomId: String?) {
Log.d("#room", "onRoomJoined()")
}

override fun onRoomLeft(reason: LeaveReason) {
Log.d("#room", "onRoomLeft()")
room = null
if (!isDestroyed) finish()
}

override fun onParticipantJoined(participant: Participant) {
Toast.makeText(
this@RoomActivity, participant.displayName + " joined",
Toast.LENGTH_SHORT
).show()
}

override fun onParticipantLeft(participant: Participant, reason: LeaveReason) {
Toast.makeText(
this@RoomActivity, participant.displayName + " left",
Toast.LENGTH_SHORT
).show()
}
}
}

Step 4: Handle Local Participant Media​

After successfully joining the room, it's time to enable/disable the local participant's (your) camera and mic. For that we will use the Room class methods enableCam / disableCam for the camera and muteMic / unmuteMic for the mic. These are suspend functions, so each button launches a coroutine. room.isMicEnabled and room.isCamEnabled tell you the current state.

RoomActivity.kt
class RoomActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_room)
//...Room Setup is Here

// actions
setActionListeners()
}

private fun setActionListeners() {
// toggle mic
findViewById<View>(R.id.btnMic).setOnClickListener {
val room = room ?: return@setOnClickListener
lifecycleScope.launch {
try {
if (room.isMicEnabled) {
// this will mute the local participant's mic
room.muteMic()
Toast.makeText(this@RoomActivity, "Mic Muted", Toast.LENGTH_SHORT).show()
} else {
// this will unmute the local participant's mic
room.unmuteMic()
Toast.makeText(this@RoomActivity, "Mic Enabled", Toast.LENGTH_SHORT).show()
}
} catch (e: VideoSDKException) {
Toast.makeText(this@RoomActivity, e.message, Toast.LENGTH_SHORT).show()
}
}
}

// toggle camera
findViewById<View>(R.id.btnWebcam).setOnClickListener {
val room = room ?: return@setOnClickListener
lifecycleScope.launch {
try {
if (room.isCamEnabled) {
// this will disable the local participant's camera
room.disableCam()
Toast.makeText(this@RoomActivity, "Camera Disabled", Toast.LENGTH_SHORT).show()
} else {
// this will enable the local participant's camera
room.enableCam()
Toast.makeText(this@RoomActivity, "Camera Enabled", Toast.LENGTH_SHORT).show()
}
} catch (e: VideoSDKException) {
Toast.makeText(this@RoomActivity, e.message, Toast.LENGTH_SHORT).show()
}
}
}

// leave the room
findViewById<View>(R.id.btnLeave).setOnClickListener {
leaveRoom()
}

// the Back button leaves the room too, instead of leaving it running
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
leaveRoom()
}
})
}

private fun leaveRoom() {
lifecycleScope.launch {
try {
// this will make the local participant leave the room;
// onRoomLeft then closes this screen
room?.leave()
} catch (e: VideoSDKException) {
// the join never completed, so there is nothing to leave
finish()
}
}
}
}

Output​

Step 5: Handling the Participants View​

We will show the list of participants in a RecyclerView.

info
  • Here the participant's video is displayed using VideoView.
  • To know more about VideoView, please visit here
  1. Create a new layout for the participant view named item_remote_peer.xml in the res/layout folder.
item_remote_peer.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@color/cardview_dark_background"
tools:layout_height="200dp">

<live.videosdk.rtc.android.VideoView
android:id="@+id/participantView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#99000000"
android:orientation="horizontal">

<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="4dp"
android:textColor="@color/white" />

</LinearLayout>

</FrameLayout>
  1. Create a recycler view adapter named ParticipantAdapter which will show the participant list. Create PeerViewHolder in the adapter which will extend RecyclerView.ViewHolder.
ParticipantAdapter.kt
class ParticipantAdapter(room: Room) : RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PeerViewHolder {
return PeerViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_remote_peer, parent, false)
)
}

override fun onBindViewHolder(holder: PeerViewHolder, position: Int) {
}

override fun getItemCount(): Int {
return 0
}

class PeerViewHolder(view: View) : RecyclerView.ViewHolder(view) {
// 'VideoView' to show Video Stream
var participantView: VideoView
var tvName: TextView

init {
tvName = view.findViewById(R.id.tvName)
participantView = view.findViewById(R.id.participantView)
}
}
}
  1. Now, we will render a list of Participant for the room. We will initialize this list in the constructor of the ParticipantAdapter
ParticipantAdapter.kt
class ParticipantAdapter(room: Room) :
RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

// creating an empty list which will store all participants
private val participants: MutableList<Participant> = ArrayList()

init {
// adding the local participant(You) to the list
participants.add(room.localParticipant)

// adding Room Event listener to get the participant join/leave event in the room.
room.addEventListener(object : RoomEventListener() {
override fun onParticipantJoined(participant: Participant) {
// add participant to the list
participants.add(participant)
notifyItemInserted(participants.size - 1)
}

override fun onParticipantLeft(participant: Participant, reason: LeaveReason) {
var pos = -1
for (i in participants.indices) {
if (participants[i].id == participant.id) {
pos = i
break
}
}
// remove participant from the list
participants.remove(participant)
if (pos >= 0) {
notifyItemRemoved(pos)
}
}
})
}

// replace getItemCount() method with following.
// this method returns the size of total number of participants
override fun getItemCount(): Int {
return participants.size
}
//...
}
  1. We have listed our participants. Let's set up the view holder to display a participant video.
ParticipantAdapter.kt
import live.videosdk.rtc.android.MediaType
import videosdk.org.webrtc.VideoTrack

class ParticipantAdapter(room: Room) :
RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder>() {

// replace onBindViewHolder() method with following.
override fun onBindViewHolder(holder: PeerViewHolder, position: Int) {
val participant = participants[position]

holder.tvName.text = participant.displayName

// add Listener to the participant which will update start or stop the video stream of that participant.
// Adding it also calls onStreamEnabled for the streams the participant already has.
participant.addEventListener(object : ParticipantEventListener() {
override fun onStreamEnabled(stream: RTCStream) {
if (stream.kind == MediaType.VIDEO) {
holder.participantView.visibility = View.VISIBLE
val videoTrack = stream.track as VideoTrack
holder.participantView.addTrack(videoTrack)
}
}

override fun onStreamDisabled(stream: RTCStream) {
if (stream.kind == MediaType.VIDEO) {
holder.participantView.removeTrack()
holder.participantView.visibility = View.GONE
}
}
})
}
}
  1. Add this adapter to the RoomActivity
RoomActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
//Room Setup...
//...
val rvParticipants = findViewById<RecyclerView>(R.id.rvParticipants)
rvParticipants.layoutManager = GridLayoutManager(this, 2)
rvParticipants.adapter = ParticipantAdapter(room!!)
}

Final Output​

You have built a video calling app in Android with VideoSDK. To explore more features, go through Basic and Advanced features.

Got a Question? Ask us on discord