Building the Open-Source Conferencing App for Android

Build Instructions

Building the code is as simple as:

  1. Launch Android Studio
  2. Open the project android/java/MesiboConference from the folder where you have downloaded the code using the menu File -> Open
  3. Build using menu Build -> Rebuild Project
  4. It may take a while to build the project for the first time.
  5. Once the build is over, run on the device using the menu Run -> Run (app)
  6. That’s it, you should see the welcome screen and then the login screen.

Refer to the source code here for an example on invoking the login REST API.

public static boolean emailLogin(String name, String email, String verificationCode, ResponseHandler handler) {

  Bundle b = new Bundle();
  b.putString("op", "login");
  b.putString("appid", mContext.getPackageName());
  b.putString("email", email);
  b.putString("name", name);
  b.putString("code", verificationCode);

  handler.sendRequest(b, null, null);
  return true;
}

If login is successful, your server will generate an access token using mesibo User Management API and send it to you in the response. You need to use this access token later while initializing the mesibo real-time API. Refer to the source code here.

if (response.op.equals("login") && !TextUtils.isEmpty(response.token)) {
  AppConfig.getConfig().token = response.token; 
  ...
}

Refer to the source code here for an example on invoking the setgroup REST API.

public static boolean createRoom(String name, int mSelectedResolution, String mesiboToken, ResponseHandler handler) {

  Bundle b = new Bundle();
  b.putString("op", "setgroup");
  b.putString("token", mesiboToken);
  b.putString("name", name);
  b.putInt("resolution", mSelectedResolution);

  handler.sendRequest(b, null, null);
  return true;
}

If a room is created successfully using setgroup, you will receive the room pin and spin in the response. Refer to the source code here

...

if(response.op.equals("setgroup") && response.result.equalsIgnoreCase("OK")){
  SampleAPI.Room room = getRoom(response);

  AppConfig.getConfig().activeRoom = room;
  joinConferenceRoom((int) response.gid, room);
}

...

private SampleAPI.Room getRoom(SampleAPI.Response response){
  if(null == response)
    return  null;

  SampleAPI.Room room = new SampleAPI.Room();

  long gid = response.gid;
  if(gid <=0)
    return null;

  room.gid = gid;
  room.name = response.name;
  room.resolution = response.resolution;
  room.pin = response.pin;
  room.spin =response.spin;
  room.publish = response.publish;
  room.audio = mAudio;
  room.video = mVideo;
  room.duration = response.duration;

  return room;
}

You can share the room-id and room-pin(either pin for active participants or spin for subscribe-only participants) with other users who you want to join the conference. Refer to the source code here to see how you can invite participants.

Refer to source code here for an example on invoking the joingroup REST API.

public static boolean enterRoom(String gid, String pin, String mesiboToken, ResponseHandler handler) {

  Bundle b = new Bundle();
  b.putString("op", "joingroup");
  b.putString("token", mesiboToken);
  b.putString("gid", gid);
  b.putString("pin", pin);

  handler.sendRequest(b, null, null);
  return true;
}

If joingroup is successful, you can join a group call. Refer to source code JoinRoomActivity.java.

// Entering an existing room
if(response.op.equals("joingroup") && response.result.equalsIgnoreCase("OK")){
  SampleAPI.Room room = getRoom(response);

  AppConfig.getConfig().activeRoom = room;
  joinConferenceRoom((int) response.gid, room);
}

The app launches GroupCallActivity to show the group call screen. This, in turn, loads GroupCallFragment and starts the group call.

Refer to the source code JoinRoomActivity.java

private void joinConferenceRoom(Integer gid, SampleAPI.Room room) {
  MesiboCall.getInstance().init(MainApplication.getAppContext());

  Intent intent = new Intent(MainApplication.getAppContext(), GroupCallActivity.class);
  intent.putExtra("gid", room.gid);
  intent.putExtra("duration", room.duration);

  if(room == null)
    return;

  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

  MainApplication.getAppContext().startActivity(intent);
}

See GroupCallFragment.java

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  
  ...

  mGroupcall = MesiboCall.getInstance().groupCall((MesiboCallActivity) getActivity(), mGid);

  ...

  return view;
}

@Override
public void onResume() {
  super.onResume();
  if(!isGroupCallStarted)
    startGroupCall();
}

private void startGroupCall(){
  
  ...
  
  mGroupcall.join(this);
  isGroupCallStarted = true;

  ...

  //Publish self stream
  mLocalPublisher = mGroupcall.createPublisher(0);
  mLocalPublisher.setVideoSource(MesiboCall.MESIBOCALL_VIDEOSOURCE_CAMERAFRONT, 0);

  mLocalPublisher.call(mRoom.audio, mRoom.video, this);
  
  ...
}

In this app, GroupCallFragment.java implements GroupCallListener.

When someone joins the room and starts publishing, you will receive their Participant object through MesiboGroupCall_OnPublisher.

@Override
public void MesiboGroupcall_OnPublisher(MesiboCall.MesiboParticipant participant, boolean joined) {
  ...
  if (joined) {
    ...
    participant.call(mRoom.audio, mRoom.video , this);

  } 
    ...
}

When you receive the video stream from a participant MesiboGroupcall_OnVideo will be called. You can then use, setVideoView to display them.

Sorting Streams (Optional)

You can display the videos of participants in a grid or whatever fashion you like. Mesibo also provides a utility function to sort videos in a grid using the aspect ratio. To use this, you need to call the sort function with a list of streams and implement MesiboParticipantSortListener which will be called with the video cordinates. Note, this is an optional utility. You can always use your own method of sorting participants and displaying videos.

First we determine the available screen width and height:

if(mWidth < 0 || mHeight < 0) {
  DisplayMetrics metrics = getDisplayMetrics(activity);
  if(metrics != null) {
    if(mWidth < 0)
      mWidth = metrics.widthPixels;
    if(mHeight < 0)
      mHeight = metrics.heightPixels;
  }
}

Here, each video is displayed in a ParticipantViewHolder containing a MesiboVideoView. So, in a grid, we will have a list of views. We will sort these views, based on the aspect ratio every time the grid is updated (when participant video added or removed, when video aspect ratio changes, etc). Refer to the source code here.

public void setStreams(ArrayList<ParticipantViewHolder> views) {
  ...
  mFrameLayout.removeAllViewsInLayout();
  ArrayList<Object> sorted = (ArrayList<Object>) mGroupCall.sort(this, views, mWidth, mHeight, 0, 8, null);
  ...
}

The position for each video in the grid is set in ParticipantSort_onSetCoordinates

@Override
public void ParticipantSort_onSetCoordinates(Object o, int position, float x, float y, float width, float height) {
  ParticipantViewHolder vh = (ParticipantViewHolder)o;
  vh.setCoordinates(position, x, y, width, height);
}
  • For examples of operations carried on a local participant object(local publisher), such as sharing screen, etc refer to the source code here

  • For example, operations carried out on a remote participant object, such as muting a remote publisher, hanging up a remote publisher, etc. refer to the source code here. In the app, when you click on the video of a publisher the control buttons for that publisher appear.

MesiboGroupcall_OnMute is called when the publisher mutes audio or video. Refer to the source code here

public void MesiboGroupcall_OnMute(MesiboCall.MesiboParticipant participant, 
  boolean audioMuted, boolean videoMuted) {
    
    if(remote){
      // Remote participant has muted
    }

    // Check mute status
    if(audioMuted){
        // Audio Muted
    }

    if(videoMuted){
        // Video Muted
    }
}

You can toggle the audio using toggleAudioMute and toggle the video using toggleVideoMute.

MesiboGroupcall_OnTalking is called when the participant starts or stops talking.

public void MesiboGroupcall_OnTalking(MesiboCall.MesiboParticipant participant, boolean talking) {
    if(talking){
            // If talking is true, participant started talking
            // if it is false, participant stopped talking
            // Handle talking. For example, Show a talking icon 
    }
}

Refer to the source code in GroupCallFragment.java for an example where we switch the video source since at a time we are either sharing the screen or publishing from the camera on the app.

private void onLocalSwitchSource(View view){
  if(mLocalPublisher == null)
    return;

  mLocalPublisher.switchSource();

  int source = mLocalPublisher.getVideoSource();
  int icon = -1;
  if(MesiboCall.MESIBOCALL_VIDEOSOURCE_SCREEN == source){
    icon = R.drawable.ic_baseline_camera_alt_24;
  }
  else {
    icon = R.drawable.ic_mesibo_screen_sharing;
  }

  ImageButton switchSourcebutton = (ImageButton)view;
  switchSourcebutton.setImageResource(icon);
}

You can use the utility function playInCallSound to play sound in the background in a group call. For example, in this app, we are playing a sound whenever a new participant joins the room. (When MesiboGroupcall_OnPublisher is called).

@Override
public void MesiboGroupcall_OnPublisher(MesiboCall.MesiboParticipant participant, boolean joined) {
  ...  
  if (joined) {

    mGroupcall.playInCallSound(getContext(), R.raw.join, false);

  }
  ...

}

For implementing chat, you can use the Mesibo Messaging UI Helper.

For group chat, simply set the group profile and use launchMessageView to launch the chat UIfor the group.

protected void setRoom(SampleAPI.Room room){
  if(room == null)
    return;

  mRoom = room;

  mGroupProfile = new Mesibo.UserProfile();
  mGroupProfile.address = null;
  mGroupProfile.groupid = mRoom.gid;
  mGroupProfile.name = mRoom.name;

  Mesibo.setUserProfile(mGroupProfile, false);
}

...

public void onLaunchGroupMessagingUi(View view) {
  MesiboUI.Config opt = MesiboUI.getConfig();
  opt.mToolbarColor = 0xff00868b;
  MesiboUI.launchMessageView(getActivity(), null, mGroupProfile.groupid);
}

Similarly, for one-to-one chat, create a user profile and call launchMessageView

private void setUserProfile(MesiboCall.MesiboParticipant participant) {
  Mesibo.UserProfile user = new Mesibo.UserProfile();
  user.address = participant.getAddress();
  user.name = participant.getName();

  Mesibo.setUserProfile(user, false);

}

...

public void onLaunchMessagingUi(View view) {
  MesiboCall.MesiboParticipant p = getParticipant();
  MesiboUI.Config opt = MesiboUI.getConfig();
  opt.mToolbarColor = 0xff00868b;
  MesiboUI.launchMessageView(view.getContext(), p.getAddress(), 0);
}

Refer to the documentation Launching Messaging UI in Android to learn more.

Whenever a publisher hangs up, MesiboGroupcall_OnHangup will be called.

To, stop viewing a participant you need to call hangup on their participant object.

participant.hangup();
mGroupcall.leave()

In this app, we call leave when the local hangup button is pressed and when back button is pressed

In the next part, we will explore the iOS app.

On to Part 3 »

Displaying List of Participants (Optional)

All publishers and subscribers in the conference rooms are members of a group. So, essentially you need to display a list of users. For this, you may simply use a UI utility component from Android UI Modules - MesiboUserListFragment.

MesiboUserListFragment displays a list of users in different modes. Here, we use the mode MODE_SELECTCONTACT. Refer to Android UI Modules Documentation to learn more.

private void onListParticipants(View v) {
  Bundle bundle = new Bundle();
  bundle.putLong("groupid", mRoom.gid);

  MesiboUI.Config opt = MesiboUI.getConfig();
  opt.mToolbarColor = 0xff00868b;
  opt.selectContactTitle = "Participants";
  opt.createGroupTitle = null;

  UIManager.launchParticipantList(getActivity(), 0, MesiboUserListFragment.MODE_SELECTCONTACT, 0, bundle);
}