Introduction

Integrating real-time communication features like chat functionality adds an active layer to video applications. Features like video chat have become imperative for engaging user experiences in Android apps. This article aims to guide you through the process of integrating chat leveraging the capabilities of the VideoSDK.

Goals

By the End of this Article:

  1. Create a VideoSDK account and generate your VideoSDK auth token.
  2. Integrate the VideoSDK library and dependencies into your project.
  3. Implement core functionalities for video calls using VideoSDK.
  4. Integrate chat feature.

Getting Started with VideoSDK

To take advantage of the chat functionality, we will need to use the capabilities that the VideoSDK offers. Before we dive into the implementation steps, let's make sure you complete the necessary prerequisites.

Create a VideoSDK Account

Go to your VideoSDK dashboard and sign up if you don't have an account. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.

Generate your Auth Token

Visit your VideoSDK dashboard and navigate to the "API Key" section to generate your auth token. This token plays a crucial role in authorizing your application to use VideoSDK features.

For a more visual understanding of the account creation and token generation process, consider the provided tutorial.

Prerequisites and Setup

Make sure your development environment meets the following requirements:

  • Java Development Kit is supported.
  • Android Studio version 3.0 or later.
  • Android SDK API level 21 or higher.
  • A mobile device with Android 5.0 or later version.

Integrate VideoSDK

Following the account creation and token generation steps, we'll guide you through the process of adding the VideoSDK library and other dependencies to your project. We'll also ensure your app has the required permissions to access features like audio recording, camera usage, and internet connectivity, all crucial for a seamless video experience.

Step (a): Add the repositories to the project's settings.gradle file

dependencyResolutionManagement{
  repositories {
    // ...
    google()
    mavenCentral()
    maven { url 'https://jitpack.io' }
    maven { url "https://maven.aliyun.com/repository/jcenter" }
  }
}

Step (b): Include the following dependency within your application's build.gradle file

dependencies {
  implementation 'live.videosdk:rtc-android-sdk:0.1.26'

  // library to perform Network call to generate a meeting id
  implementation 'com.amitshekhar.android:android-networking:1.0.2'

  // Other dependencies specific to your app
}
If your project has set android.useAndroidX=true, then set android.enableJetifier=true in the gradle.properties file to migrate your project to AndroidX and avoid duplicate class conflict.

Step (c): Add permissions to your project

In /app/Manifests/AndroidManifest.xml, add the following permissions after </application>.

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />

These permissions are essential for enabling core functionalities like audio recording, internet connectivity for real-time communication, and camera access for video streams within your Android video chat application.

Essential Steps for Building the Video Calling Functionality

We'll now add the functionalities that make your Android video chat application more exciting after setting up your project with VideoSDK. This section outlines the essential steps for implementing core functionalities within your app.

This section will guide you through four key aspects:

Step 1: Generate a meetingId

Now, we can create the meetingId from the VideoSDK's rooms API. You can refer to this documentation to generate meetingId.

Step 2: Initializing the Meeting

After getting meetingId , the next step involves initializing the meeting for that we need to,

  1. Initialize VideoSDK.
  2. Configure VideoSDK with a token.
  3. Initialize the meeting with required params such as meetingId, participantName, micEnabled, webcamEnabled and more.
  4. Add MeetingEventListener for listening events such as Meeting Join/Left and Participant Join/Left.
  5. Join the room with meeting.join() a method.

Please copy the .xml file of the MeetingActivity from here.

public class MeetingActivity extends AppCompatActivity {
  // declare the variables we will be using to handle the meeting
  private Meeting meeting;
  private boolean micEnabled = true;
  private boolean webcamEnabled = true;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);

    final String token = ""; // Replace with the token you generated from the VideoSDK Dashboard
    final String meetingId = ""; // Replace with the meetingId you have generated
    final String participantName = "John Doe";

    // 1. Initialize VideoSDK
    VideoSDK.initialize(applicationContext);

    // 2. Configuration VideoSDK with Token
    VideoSDK.config(token);

    // 3. Initialize VideoSDK Meeting
    meeting = VideoSDK.initMeeting(
            MeetingActivity.this, meetingId, participantName,
            micEnabled, webcamEnabled,null, null, false, null, null);

    // 4. Add event listener for listening upcoming events
    meeting.addEventListener(meetingEventListener);

    // 5. Join VideoSDK Meeting
    meeting.join();

    ((TextView)findViewById(R.id.tvMeetingId)).setText(meetingId);
  }

  // creating the MeetingEventListener
  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
    @Override
    public void onMeetingJoined() {
      Log.d("#meeting", "onMeetingJoined()");
    }

    @Override
    public void onMeetingLeft() {
      Log.d("#meeting", "onMeetingLeft()");
      meeting = null;
      if (!isDestroyed()) finish();
    }

    @Override
    public void onParticipantJoined(Participant participant) {
      Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " joined", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onParticipantLeft(Participant participant) {
      Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " left", Toast.LENGTH_SHORT).show();
    }
  };
}

Step 3: Handle Local Participant Media

After successfully entering the meeting, it's time to manage the webcam and microphone for the local participant (you).

To enable or disable the webcam, we'll use the Meeting class methods enableWebcam() and disableWebcam(), respectively. Similarly, to mute or unmute the microphone, we'll utilize the methods muteMic() and unmuteMic()

public class MeetingActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    //...Meeting Setup is Here

    // actions
    setActionListeners();
  }

  private void setActionListeners() {
    // toggle mic
    findViewById(R.id.btnMic).setOnClickListener(view -> {
      if (micEnabled) {
        // this will mute the local participant's mic
        meeting.muteMic();
        Toast.makeText(MeetingActivity.this, "Mic Disabled", Toast.LENGTH_SHORT).show();
      } else {
        // this will unmute the local participant's mic
        meeting.unmuteMic();
        Toast.makeText(MeetingActivity.this, "Mic Enabled", Toast.LENGTH_SHORT).show();
      }
      micEnabled=!micEnabled;
    });

    // toggle webcam
    findViewById(R.id.btnWebcam).setOnClickListener(view -> {
      if (webcamEnabled) {
        // this will disable the local participant webcam
        meeting.disableWebcam();
        Toast.makeText(MeetingActivity.this, "Webcam Disabled", Toast.LENGTH_SHORT).show();
      } else {
        // this will enable the local participant webcam
        meeting.enableWebcam();
        Toast.makeText(MeetingActivity.this, "Webcam Enabled", Toast.LENGTH_SHORT).show();
      }
      webcamEnabled=!webcamEnabled;
    });

    // leave meeting
    findViewById(R.id.btnLeave).setOnClickListener(view -> {
      // this will make the local participant leave the meeting
      meeting.leave();
    });
  }
}

Step 4: Handling the Participants' View

To display a list of participants in your video UI, we'll utilize a RecyclerView.

(a) This involves creating a new layout for the participant view named item_remote_peer.xml in the res/layout folder. You can copy item_remote_peer.xml file from here.

(b) Create a RecyclerView adapter ParticipantAdapter which will be responsible for displaying the participant list. Within this adapter, define a PeerViewHolder class that extends RecyclerView.ViewHolder.

public class ParticipantAdapter extends RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder> {

  @NonNull
  @Override
  public PeerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
      return new PeerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_remote_peer, parent, false));
  }

  @Override
  public void onBindViewHolder(@NonNull PeerViewHolder holder, int position) {
  }

  @Override
  public int getItemCount() {
      return 0;
  }

  static class PeerViewHolder extends RecyclerView.ViewHolder {
    // 'VideoView' to show Video Stream
    public VideoView participantView;
    public TextView tvName;
    public View itemView;

    PeerViewHolder(@NonNull View view) {
        super(view);
        itemView = view;
        tvName = view.findViewById(R.id.tvName);
        participantView = view.findViewById(R.id.participantView);
    }
  }
}

(c) Now, we will render a list of Participant for the meeting. We will initialize this list in the constructor of the ParticipantAdapter

public class ParticipantAdapter extends RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder> {

  // creating a empty list which will store all participants
  private final List<Participant> participants = new ArrayList<>();

  public ParticipantAdapter(Meeting meeting) {
    // adding the local participant(You) to the list
    participants.add(meeting.getLocalParticipant());

    // adding Meeting Event listener to get the participant join/leave event in the meeting.
    meeting.addEventListener(new MeetingEventListener() {
      @Override
      public void onParticipantJoined(Participant participant) {
        // add participant to the list
        participants.add(participant);
        notifyItemInserted(participants.size() - 1);
      }

      @Override
      public void onParticipantLeft(Participant participant) {
        int pos = -1;
        for (int i = 0; i < participants.size(); i++) {
          if (participants.get(i).getId().equals(participant.getId())) {
            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
  public int getItemCount() {
    return participants.size();
  }
  //...
}

(d) We have listed our participants. Let's set up the view holder to display a participant video.

public class ParticipantAdapter extends RecyclerView.Adapter<ParticipantAdapter.PeerViewHolder> {

  // replace onBindViewHolder() method with following.
  @Override
  public void onBindViewHolder(@NonNull PeerViewHolder holder, int position) {
    Participant participant = participants.get(position);

    holder.tvName.setText(participant.getDisplayName());

    // adding the initial video stream for the participant into the 'VideoView'
    for (Map.Entry<String, Stream> entry : participant.getStreams().entrySet()) {
      Stream stream = entry.getValue();
      if (stream.getKind().equalsIgnoreCase("video")) {
        holder.participantView.setVisibility(View.VISIBLE);
        VideoTrack videoTrack = (VideoTrack) stream.getTrack();
        holder.participantView.addTrack(videoTrack)
        break;
      }
    }
    // add Listener to the participant which will update start or stop the video stream of that participant
    participant.addEventListener(new ParticipantEventListener() {
      @Override
      public void onStreamEnabled(Stream stream) {
        if (stream.getKind().equalsIgnoreCase("video")) {
          holder.participantView.setVisibility(View.VISIBLE);
          VideoTrack videoTrack = (VideoTrack) stream.getTrack();
          holder.participantView.addTrack(videoTrack)
        }
      }

      @Override
      public void onStreamDisabled(Stream stream) {
        if (stream.getKind().equalsIgnoreCase("video")) {
          holder.participantView.removeTrack();
          holder.participantView.setVisibility(View.GONE);
        }
      }
    });
  }
}

(e) Now, add this adapter to the MeetingActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
  //Meeting Setup...
  //...
  final RecyclerView rvParticipants = findViewById(R.id.rvParticipants);
  rvParticipants.setLayoutManager(new GridLayoutManager(this, 2));
  rvParticipants.setAdapter(new ParticipantAdapter(meeting));
}

Integrate Chat Feature

This section delineates the process of implementing group and private chat functionalities within your Android video calling app. VideoSDK provides pubSub a class that uses the Publish-Subscribe mechanism and can be used to develop various functions.

For example, participants can use it to send chat messages to each other, share files or other media, or trigger actions such as muting or unmuting audio or video. Now we will see how we can use PubSub to implement chat functionality. If you are not familiar with the PubSub mechanism and pubSub class, you can follow this guide.

Implement Group Chat

  • The first step in creating a group chat is choosing the topic that all the participants will publish and subscribe to send and receive the messages. We will be using CHAT as the topic for this one.
  • On the send button, we will publish the message that the sender typed in the EditText field.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import java.util.List;
import live.videosdk.rtc.android.Meeting;
import live.videosdk.rtc.android.lib.PubSubMessage;
import live.videosdk.rtc.android.listeners.PubSubMessageListener;
import live.videosdk.rtc.android.model.PubSubPublishOptions;

public class ChatActivity extends AppCompatActivity {
  // Meeting
  Meeting meeting;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);

    /**
     * Here, we have created 'MainApplication' class, which extends android.app.Application class.
     * It has Meeting property and getter and setter methods of Meeting property.
     * In your android manifest, you must declare the class implementing android.app.Application
     * (add the android:name=".MainApplication" attribute to the existing application tag):
     * In MainActivity.java, we have set Meeting property.
     *
     * For Example: (MainActivity.java)
     * Meeting meeting = VideoSDK.initMeeting(context, meetingId, ParticipantName, micEnabled, webcamEnabled,participantId,mode,multiStream,customTrack,metaData);
     * ((MainApplication) this.getApplication()).setMeeting(meeting);
    */

    // Get Meeting
    meeting = ((MainApplication) this.getApplication()).getMeeting();

    findViewById(R.id.btnSend).setOnClickListener(view -> sendMessage());
  }

  private void sendMessage()
  {
    // get message from EditText
    String message = etmessage.getText().toString();
    if (!message.equals("")) {
        PubSubPublishOptions publishOptions = new PubSubPublishOptions();
        publishOptions.setPersist(true);

        // Sending the Message using the publish method
        meeting.pubSub.publish("CHAT", message, publishOptions);

        // Clearing the message input
        etmessage.setText("");
    } else {
        Toast.makeText(ChatActivity.this, "Please Enter Message",
                Toast.LENGTH_SHORT).show();
    }
  }
}
  • The next step would be to display the messages others send. For this we have to subscribe to that topic i.e CHAT and display all the messages.
public class ChatActivity extends AppCompatActivity {

  // PubSubMessageListener
  private PubSubMessageListener pubSubMessageListener = new PubSubMessageListener() {
    @Override
    public void onMessageReceived(PubSubMessage message) {
        // New message
        Toast.makeText(
          ChatActivity.this, message.senderName + " says : "+ message.getMessage(),
          Toast.LENGTH_SHORT
        ).show();
    }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);

    //..

    // Subscribe for 'CHAT' topic
    List<PubSubMessage> pubSubMessageList = meeting.pubSub.subscribe("CHAT", pubSubMessageListener);

    for(PubSubMessage message : pubSubMessageList){
        // Persisted messages
        Toast.makeText(
          ChatActivity.this, message.senderName + " says : "+ message.getMessage(),
          Toast.LENGTH_SHORT
        ).show();
    }
  }
}
  • The final step in the group chat would be unsubscribe to that topic, which you had previously subscribed to but no longer needed. Here we are unsubscribe to CHAT topic on activity destroy.
public class ChatActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);

    //..
  }

  @Override
  protected void onDestroy() {
    // Unsubscribe for 'CHAT' topic
    meeting.pubSub.unsubscribe("CHAT", pubSubMessageListener);
    super.onDestroy();
  }
}

Implement Private Chat

  • If you want to convert into a private chat between two participants, then all you have to do is pass sendOnly parameter in PubSubPublishOptions.
public class ChatActivity extends AppCompatActivity {
 
  //...

  private void sendMessage()
  {
    // get message from EditText
    String message = etmessage.getText().toString();
    if (!message.equals("")) {
        PubSubPublishOptions publishOptions = new PubSubPublishOptions();
        publishOptions.setPersist(true);
        // Pass the participantId of the participant to whom you want to send the message.
        String[] sendOnly = {
          "xyz" 
        };
        publishOptions.setSendOnly(sendOnly);

        // Sending the Message using the publish method
        meeting.pubSub.publish("CHAT", message, publishOptions);

        // Clearing the message input
        etmessage.setText("");
    } else {
        Toast.makeText(ChatActivity.this, "Please Enter Message",
                Toast.LENGTH_SHORT).show();
    }
  }
}

Downloading Chat Messages

All the messages from the PubSub that were published  persist : true can be downloaded as a .csv file. This file will be available in the VideoSDK dashboard as well as through the Sessions API.

Conclusion

Now, your android video chat app not only enhances its functionality but also opens doors to endless possibilities in real-time communication. Whether it's group chat, private chat, or downloading chat messages, leveraging PubSub adds a new dimension to your app, making it more engaging and user-friendly. Embrace this integration, explore its potential, and elevate your app's user experience to new heights. Happy coding!

Sign up with VideoSDK today and Get 10000 minutes free to take your video app to the next level!