Building the Open-Source Conferencing App for Android

{% include_relative nav.html selected="android" %}

{% capture prerequisites%}

{% capture build_instructions%}

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. {% endcapture %}

{% capture login %} Refer to the source code hereopen_in_new 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 hereopen_in_new.

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

{% endcapture %}

{% capture create_room %} Refer to the source code hereopen_in_new 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;
}

{% endcapture %}

{% capture create_room_pin %} If a room is created successfully using setgroup, you will receive the room pin and spin in the response. Refer to the source code hereopen_in_new

...

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 hereopen_in_new to see how you can invite participants. {% endcapture %}

{% capture join_room %} Refer to source code hereopen_in_new 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.javaopen_in_new.

// 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);
}

{% endcapture %}

{% capture launch_group_call %} The app launches GroupCallActivityopen_in_new to show the group call screen. This, in turn, loads GroupCallFragmentopen_in_new and starts the group call.

Refer to the source code JoinRoomActivity.javaopen_in_new

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);
}

{% endcapture %}

{% capture start_group_call %} See GroupCallFragment.javaopen_in_new

@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);
  
  ...
}

{% endcapture %}

{% capture viewing_others %} In this app, GroupCallFragment.javaopen_in_new implements GroupCallListener.

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

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

  } 
    ...
}

{% endcapture %}

{% capture listeners %}

{% endcapture %}

{% capture display_streams %} When you receive the video stream from a participant MesiboGroupcall_OnVideoopen_in_new will be called. You can then use, setVideoViewopen_in_new 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 MesiboParticipantSortListeneropen_in_new 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 ParticipantViewHolderopen_in_new 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 hereopen_in_new.

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_onSetCoordinatesopen_in_new

@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);
}

{% endcapture %}

{% capture conf_ops %}

  • For examples of operations carried on a local participant object(local publisher), such as sharing screen, etc refer to the source code hereopen_in_new

  • 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 hereopen_in_new. In the app, when you click on the video of a publisher the control buttons for that publisher appear. {% endcapture %}

{% capture mute %} MesiboGroupcall_OnMuteopen_in_new is called when the publisher mutes audio or video. Refer to the source code hereopen_in_new

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 toggleAudioMuteopen_in_new and toggle the video using toggleVideoMuteopen_in_new.

{% endcapture %}

{% capture talk_detection %} MesiboGroupcall_OnTalkingopen_in_new 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 
    }
}

{% endcapture %}

{% capture screen_sharing %} Refer to the source code in GroupCallFragment.javaopen_in_new 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);
}

{% endcapture %}

{% capture play_sound %} 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 calledopen_in_new).

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

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

  }
  ...

}

{% endcapture %}

{% capture chat %} For implementing chat, you can use the Mesibo Messaging UI Helper.

For group chat, simply set the group profileopen_in_new 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 profileopen_in_new 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.

{% endcapture %}

{% capture hang_up%} Whenever a publisher hangs up, MesiboGroupcall_OnHangupopen_in_new will be called.

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

participant.hangup();

{% endcapture %}

{% capture leave_room%}

mGroupcall.leave()

In this app, we call leave when the local hangup button is pressedopen_in_new and when back button is pressedopen_in_new {% endcapture %}

{% capture conclusion %} In the next part, we will explore the iOS app.

On to Part 3 >> {% endcapture %}

{% capture participant_list%}

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);
}

{% endcapture %}

{% include_relative conf-app.md %}