val callSettings = CallSettingsRequest(
limits = LimitsSettingsRequest(maxDurationSeconds = 300)
)
val call = streamVideo.call(callType = "default", callId = "callId")
call.create(settings = callSettings)
Session timers
In some cases, you want to be able to limit the duration of a call. StreamVideo supports this use-case by allowing you to specify the duration of a call on creation. Additionally, it provides you a way to extend the duration during the call, if needed.
Low-level client capabilities
First, let’s see how we can create a call that has a limited duration.
This code will create a call, which will have a duration of 300 seconds (5 minutes), as soon as the session is started (a participant joins the call). You can check the start date of a call with the following code:
val startedAt = call.state.session?.value?.startedAt
When the maxDuration of a call is specified, the call session also provides the timerEndsAt
value, which provides the date when the call will end. When a call is ended, all the participants are removed from the call.
val timerEndsAt = call.state.session?.value?.timerEndsAt
Extending the call duration
You can also extend the duration of a call, both before or during the call. To do that, you should use the call.update
method:
val extendDuration = 120 // Example extension duration in seconds
val newDuration = (call.state.settings?.value?.limits?.maxDurationSeconds ?: 0) + extendDuration
val newSettings = CallSettingsRequest(
limits = LimitsSettingsRequest(maxDurationSeconds = newDuration)
)
call.update(settingsOverride = newSettings)
When the call duration is extended, the timerEndsAt
will be updated to reflect that change.
Example implementation
Let’s see how we can put these methods together in a sample session timer implementation. In this cookbook, we will show a popup that will notify the user that a call will end soon. It will also allow the creator of the call to extend its duration.
Prerequisite for following along is a working StreamVideo integration and the ability to establish calls. To help with that, check our tutorials and getting started docs.
Session Timer example
For the purpose of this example we’ll assume that the application incorporates a view model architecture.
Let’s create a new Kotlin file, and call it SessionTimerViewModel.kt
. We will put the following contents in it:
class SessionTimerViewModel(
private val call: Call?, // Replace with your `Call` data type
private val alertInterval: Long, // In milliseconds
private val extendDuration: Long = 120_000L // Default to 2 minutes in milliseconds
) : ViewModel() {
var showTimerAlert by mutableStateOf(false)
private set
var secondsUntilEnd by mutableStateOf(0L)
private set
private var timerEndsAt: OffsetDateTime? = call?.state?.session?.value?.timerEndsAt
set(value) {
field = value
setupTimerIfNeeded()
}
private var countdownJob: Job? = null
private var extendDurationAccumulator = extendDuration
val showExtendCallDurationButton: Boolean
get() = call?.state?.ownCapabilities?.value?.contains(OwnCapability.ChangeMaxDuration) == true
init {
setupTimerIfNeeded()
subscribeForSessionUpdates()
}
fun extendCallDuration() {
call?.let {
viewModelScope.launch {
try {
val newDuration =
(it.state.settings.value?.limits?.maxDurationSeconds
?: 0) * 1000 + extendDurationAccumulator
extendDurationAccumulator += extendDuration
call.update(
settingsOverride = CallSettingsRequest(
limits = LimitsSettingsRequest(maxDurationSeconds = (newDuration / 1000).toInt())
)
)
showTimerAlert = false
} catch (e: Exception) {
// Handle the error, e.g., log it
}
}
}
}
private fun subscribeForSessionUpdates() {
// Subscribe to session updates
// Replace with your own logic for session updates
viewModelScope.launch {
call?.state?.session?.collect { session ->
if (session?.timerEndsAt != timerEndsAt) {
timerEndsAt = session?.timerEndsAt
}
}
}
}
private fun setupTimerIfNeeded() {
countdownJob?.cancel()
countdownJob = null
showTimerAlert = false
timerEndsAt?.let { endTime ->
val alertTime = endTime.minusSeconds(alertInterval / 1000)
val timeUntilAlert = alertTime.toInstant().toEpochMilli() - System.currentTimeMillis()
if (timeUntilAlert <= 0) {
showTimerAlert = true
} else {
viewModelScope.launch {
delay(timeUntilAlert)
showTimerAlert = true
startCountdown(endTime)
}
}
}
}
private fun startCountdown(endTime: OffsetDateTime) {
countdownJob = viewModelScope.launch {
while (true) {
val timeLeft = (endTime.toEpochSecond() * 1000 - System.currentTimeMillis()) / 1000
if (timeLeft <= 0) {
showTimerAlert = false
secondsUntilEnd = 0
break
} else {
secondsUntilEnd = timeLeft
}
delay(1000)
}
}
}
override fun onCleared() {
super.onCleared()
countdownJob?.cancel()
}
fun dismiss() {
showTimerAlert = false
}
}
You can directly add the following imports if needed:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.getstream.video.android.core.Call
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.openapitools.client.models.CallSettingsRequest
import org.openapitools.client.models.LimitsSettingsRequest
import org.openapitools.client.models.OwnCapability
import org.threeten.bp.OffsetDateTime
In the Jetpack Compose view shown during a call, we’ll use the SessionTimerViewModel
to display the session timer popup and a countdown timer.
The showTimerAlert
state will determine when the popup should be visible, and the secondsUntilEnd
will be used to show the remaining time.
Here’s how you can use the SessionTimerViewModel
in your composable:
fun YourCallScreen(
viewModel: SessionTimerViewModel,
) {
// Observe the state from the ViewModel
// Main Call Screen UI
Box(modifier = Modifier.fillMaxSize()) {
// Other call UI components go here
if (viewModel.showTimerAlert) {
TimerPopup(
viewModel = viewModel,
secondsUntilEnd = viewModel.secondsUntilEnd,
onDismiss = { viewModel.dismiss() }
)
}
}
}
@Composable
fun TimerPopup(
viewModel: SessionTimerViewModel,
secondsUntilEnd: Long,
onDismiss: () -> Unit
) = Popup(onDismissRequest = onDismiss) {
// Popup dialog to show the timer and options
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.5f))
.clickable(onClick = onDismiss)
) {
Column(
modifier = Modifier
.align(Alignment.Center)
.background(Color.White, shape = RoundedCornerShape(8.dp))
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Call will end in", style = MaterialTheme.typography.h6)
Text(
text = formatSecondsToTime(secondsUntilEnd),
style = MaterialTheme.typography.h4,
color = Color.Red
)
Spacer(modifier = Modifier.height(16.dp))
if (viewModel.showExtendCallDurationButton) {
Button(onClick = {
viewModel.extendCallDuration()
}) {
Text("Extend Call")
}
}
Spacer(modifier = Modifier.height(8.dp))
TextButton(onClick = onDismiss) {
Text("Dismiss")
}
}
}
}
fun formatSecondsToTime(seconds: Long): String {
val minutes = seconds / 60
val remainingSeconds = seconds % 60
return String.format("%02d:%02d", minutes, remainingSeconds)
}
If you like you can use the following imports:
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
With that, you can have a working implementation of a session timer.