Skip to main content
Version: 0.1.x

Invite Guest on Stage - Android

In this guide, we will see how you can request a viewer to join your livestream by using the changeMode().

info

Important Changes Android SDK in Version v0.2.0

  • The following modes have been deprecated:
    • CONFERENCE has been replaced by SEND_AND_RECV
    • VIEWER has been replaced by SIGNALLING_ONLY

Please update your implementation to use the new modes.

⚠️ Compatibility Notice:
To ensure a seamless meeting experience, all participants must use the same SDK version.
Do not mix version v0.2.0 + with older versions, as it may cause significant conflicts.

Let's first have a look at how we will be using the PubSub mechanism to acheive the requesting and switching of the participant's mode.

invite-guest-pubsub

note

Before going forward in this guide, do make sure all the attendees join the meeting with mode as SIGNALLING_ONLY and the host joins with mode as SEND_AND_RECV

Step 1: Loading Viewer List

The host will see a list of participants who have joined as SIGNALLING_ONLY along with a button that, when selected, will invite that user to join the livestream.

Here we are using RecyclerView to display viewer list with button.

layout_viewers_list_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#2B3034"
android:id="@+id/layout_participants">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingVertical="15sp"
android:text="Viewer List"
android:textSize="20sp"
android:textColor="@color/white"/>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvViewersLinearView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="15sp" />

</LinearLayout>
item_viewer_list_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="12dp">


<TextView
android:id="@+id/viewerName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="20sp"
android:textColor="@color/white"/>

<Button
android:id="@+id/btnRequest"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Request to join Stage"
android:textAllCaps="false" />


</LinearLayout>
  • Create a recycler view adapter named ViewerListAdapter which will show the viewer list. Create PeerViewHolder in the adapter which will extend RecyclerView.ViewHolder.
class ViewerListAdapter(items: ArrayList<Participant?>?, private val meeting: Meeting) :
RecyclerView.Adapter<ViewerListAdapter.ViewHolder?>() {
private var viewers = ArrayList<Participant?>()

init {
viewers.addAll(items!!)

// adding Meeting Event listener to get the participant join/leave and panticipant mode change event in the meeting.
meeting.addEventListener(object : MeetingEventListener() {
override fun onParticipantJoined(participant: Participant) {
super.onParticipantJoined(participant)
viewers = ArrayList()
viewers.addAll(allViewers)
notifyDataSetChanged()
}

override fun onParticipantLeft(participant: Participant) {
super.onParticipantLeft(participant)
viewers = ArrayList()
viewers.addAll(allViewers)
notifyDataSetChanged()
}

override fun onParticipantModeChanged(data: JSONObject) {
viewers = ArrayList()
viewers.addAll(allViewers)
notifyDataSetChanged()
}
})
}

private val allViewers: ArrayList<Participant?>
private get() {
val viewerList: ArrayList<Participant?> = ArrayList()
val participants: Iterator<Participant> = meeting.participants.values.iterator()
for (i in 0 until meeting.participants.size) {
val participant = participants.next()
if (participant.mode == "SIGNALLING_ONLY") {
viewerList.add(participant)
}
}
return viewerList
}

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

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val participant = viewers[position]
holder.viewerName.text = viewers[position]!!.displayName
}

override fun getItemCount(): Int {
return viewers.size
}

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var viewerName: TextView
var btnRequest: Button

init {
viewerName = itemView.findViewById(R.id.viewerName)
btnRequest = itemView.findViewById(R.id.btnRequest)
}
}
}
  • Set this adapter to RecyclerView
btnViewerList.setOnClickListener { openViewerList() }

fun openViewerList() {
val viewerListView: RecyclerView
val bottomSheetDialog = BottomSheetDialog(mContext!!)
val layout_participants = findViewById(R.id.layout_participants)
val v3 = LayoutInflater.from(mContext)
.inflate(R.layout.layout_viewers_list_view, layout_participants)
bottomSheetDialog.setContentView(v3)
viewerListView = v3.findViewById(R.id.rvViewersLinearView)
viewerListView.minimumHeight = 400
bottomSheetDialog.show()
val viewerList: ArrayList<*> = ArrayList<Any?>()
val participants: Iterator<Participant> = meeting!!.participants.values.iterator()
for (i in 0 until meeting!!.participants.size) {
val participant = participants.next()
if (participant.mode == "SIGNALLING_ONLY")
viewerList.add(participant)
}
viewerListView.layoutManager = LinearLayoutManager(mContext)
viewerListView.adapter = ViewerListAdapter(viewerList, meeting!!)
viewerListView.setHasFixedSize(true)
}

Step 2: Requesting a Viewer to Join Livestream

We have a Viewer list ready. Now, let us handle the click event for the btnRequest.

We will be using CHANGE_MODE_ + participantId as the topic for PubSub.

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
//...

holder.btnRequest.setOnClickListener { v: View? ->
val pubSubPublishOptions = PubSubPublishOptions()
pubSubPublishOptions.isPersist = false
meeting.pubSub.publish(
"CHANGE_MODE_" + participant!!.id,
participant.id,
pubSubPublishOptions
)
}
}

Step 3: Showing Viewer Request Dialog

After implementing the Host requesting viewer to join the livestream, let's display the viewer's request dialogue and switch the SIGNALLING_ONLY mode to SEND_AND_RECV.

meeting!!.addEventListener(object : MeetingEventListener() {
override fun onMeetingJoined() {
val changeModeListener =
PubSubMessageListener { pubSubMessage: PubSubMessage ->
if (pubSubMessage.message == meeting!!.localParticipant.id) {
showRequestDialog(pubSubMessage.senderName)
}
}
meeting!!.pubSub.subscribe("CHANGE_MODE_" + meeting!!.localParticipant.id, changeModeListener)
}
})

private fun showRequestDialog(name: String) {
val builder = AlertDialog.Builder(
mContext!!
)
builder.setMessage("$name has request you to go on stage")
builder.setTitle("Change mode Request")
builder.setCancelable(false)
builder.setPositiveButton("Yes") { dialog: DialogInterface, _: Int ->
meeting!!.changeMode("SEND_AND_RECV")
//...
dialog.dismiss()
}
builder.setNegativeButton(
"No"
) { dialog: DialogInterface, which: Int -> dialog.dismiss() }
val alertDialog = builder.create()
alertDialog.show()
}

Step 4: Pin the participant

We need to pin the participant so that he/she can appears on the livestream. To achieve this, we will listen to the callback on the onParticipantModeChanged of Meeting class.

class ParticipantListAdapter(
items: ArrayList<Participant?>?,
private val meeting: Meeting,
private val context: Context
) : RecyclerView.Adapter<ParticipantListAdapter.ViewHolder>() {
// ...

init {
//...
meeting.addEventListener(object : MeetingEventListener() {
override fun onParticipantJoined(participant: Participant) {
//...
}

override fun onParticipantLeft(participant: Participant) {
// ...
}

override fun onParticipantModeChanged(data: JSONObject) {
//...
val participant = meeting.participants[data.getString("participantId")]
if (data.getString("mode") == "SEND_AND_RECV") {
participant!!.pin("SHARE_AND_CAM")
} else {
participant!!.unpin("SHARE_AND_CAM")
}
}
})
}

//...
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
//...
}
}

API Reference

The API references for all the methods utilized in this guide are provided below.

Got a Question? Ask us on discord