Audience Polls during Live Stream - Android
Interactive polls are a great way to increase engagement during livestreams. Using VideoSDK’s PubSub mechanism, you can easily implement real-time audience polling, where viewers can vote and the host can see live results instantly.
This guide walks you through how to create, send, and visualize poll results during a livestream.
publish() and subscribe() are suspend functions, so the dialogs below take a CoroutineScope, such as the activity's lifecycleScope, to call them. Each returns once the server has answered. It throws a VideoSDKException when the live stream is not joined yet, is reconnecting, or the server call fails. The examples below catch it.
Step 1: Creating and Publishing a Poll
To initiate a poll, use the PubSub class with a POLL topic. The poll structure should include a question and multiple options. This message will be published to all participants.
class CreatePollDialog(
private val context: Context,
private val liveStream: Room,
private val scope: CoroutineScope
) {
companion object {
const val POLL_TOPIC = "POLL"
}
fun show() {
// ... UI setup code ...
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
// Validate inputs
// Create poll object
val options = mutableListOf(option1, option2)
if (option3.isNotEmpty()) options.add(option3)
if (option4.isNotEmpty()) options.add(option4)
val poll = SimplePoll(question, options)
// Publish poll to all participants
val pubSubPublishOptions = PubSubPublishOptions(persist = true)
scope.launch {
try {
liveStream.pubSub.publish(POLL_TOPIC, poll.toJsonString(), pubSubPublishOptions)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Poll not published: ${e.code} ${e.name}")
return@launch
}
// Show results dialog to host
PollResultsDialog(context, liveStream, scope).show(poll)
dialog.dismiss()
}
}
}
}
Step 2: Subscribing to Polls and Displaying Options
Participants can listen to the POLL topic and render voting options dynamically based on the incoming data. The senderId of the received poll message identifies the host. Publish the vote with sendOnly set to that id, so only the host receives it and other participants can't see who voted for what.
class PollVotingDialog(
private val context: Context,
private val liveStream: Room,
private val scope: CoroutineScope
) {
companion object {
const val POLL_RESPONSE_TOPIC = "POLL_RESPONSE"
}
// hostId is the senderId of the received POLL message (message.senderId)
fun show(poll: SimplePoll, hostId: String) {
// ... UI setup code ...
// Create option buttons dynamically
poll.options.forEach { option ->
val button = Button(context)
button.text = option
button.setOnClickListener {
// Submit vote
val response = SimplePollResponse(
pollId = poll.id,
option = option,
participantId = liveStream.localParticipant.id,
participantName = liveStream.localParticipant.displayName
)
val sendOnly: Array<String> = arrayOf(hostId)
val publishOptions = PubSubPublishOptions(sendOnly = sendOnly)
scope.launch {
try {
liveStream.pubSub.publish(POLL_RESPONSE_TOPIC, response.toJsonString(), publishOptions)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Vote not sent: ${e.code} ${e.name}")
return@launch
}
// Disable all buttons after voting
for (i in 0 until optionsContainer.childCount) {
optionsContainer.getChildAt(i).isEnabled = false
}
}
}
optionsContainer.addView(button)
}
}
}
Step 3: Aggregating and Displaying Poll Results
The host can subscribe to the POLL_RESPONSE topic to collect responses and render the result in real-time.
class PollResultsDialog(
private val context: Context,
private val liveStream: Room,
private val scope: CoroutineScope
) {
private val pollResults = ConcurrentHashMap<String, Int>()
private var totalVotes = 0
private lateinit var responsesListener: PubSubMessageListener
companion object {
const val POLL_RESPONSE_TOPIC = "POLL_RESPONSE"
}
fun show(poll: SimplePoll) {
// ... UI setup code ...
// Initialize results map with 0 votes for each option
poll.options.forEach { option ->
pollResults[option] = 0
}
// Create initial result bars
updateResultBars()
// Listen for poll responses
responsesListener = object : PubSubMessageListener {
override fun onMessageReceived(pubSubMessage: PubSubMessage) {
try {
val response = SimplePollResponse.fromJsonString(pubSubMessage.message ?: return)
val option = response.option
// Update vote count
pollResults[option] = (pollResults[option] ?: 0) + 1
totalVotes++
// Update UI on main thread
(context as? android.app.Activity)?.runOnUiThread {
updateResultBars()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) {
// Persisted message list, delivered in batches
Log.d("VideoSDK", "messages: $messages")
}
}
// Subscribe to poll responses
scope.launch {
try {
liveStream.pubSub.subscribe(POLL_RESPONSE_TOPIC, responsesListener)
} catch (e: VideoSDKException) {
Log.e("VideoSDK", "Subscribe failed: ${e.code} ${e.name}")
}
}
}
private fun updateResultBars() {
// Clear previous results
optionsContainer?.removeAllViews()
// Create result bars for each option
pollResults.forEach { (option, votes) ->
val percentage = if (totalVotes > 0) (votes * 100) / totalVotes else 0
// Create progress bar UI showing percentage
// ... UI code to display results ...
}
}
}
API Reference
The API references for all the methods and events utilized in this guide are provided below.
Got a Question? Ask us on discord

