[Element Call] Keep MatrixClient alive while the call is working (#1695)

* Element Call: keep MatrixClient alive to get event updates
This commit is contained in:
Jorge Martin Espinosa 2023-10-31 16:58:33 +01:00 committed by GitHub
parent 06514d8be5
commit f7f3925fc9
8 changed files with 125 additions and 16 deletions

View file

@ -18,7 +18,7 @@ package io.element.android.features.call.ui
import io.element.android.features.call.utils.WidgetMessageInterceptor
sealed interface CallScreeEvents {
data object Hangup : CallScreeEvents
data class SetupMessageChannels(val widgetMessageInterceptor: WidgetMessageInterceptor) : CallScreeEvents
sealed interface CallScreenEvents {
data object Hangup : CallScreenEvents
data class SetupMessageChannels(val widgetMessageInterceptor: WidgetMessageInterceptor) : CallScreenEvents
}

View file

@ -17,6 +17,7 @@
package io.element.android.features.call.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
@ -37,10 +38,13 @@ import io.element.android.libraries.architecture.Async
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.architecture.runCatchingUpdatingState
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@ -53,9 +57,11 @@ class CallScreenPresenter @AssistedInject constructor(
@Assisted private val callType: CallType,
@Assisted private val navigator: CallScreenNavigator,
private val callWidgetProvider: CallWidgetProvider,
private val userAgentProvider: UserAgentProvider,
userAgentProvider: UserAgentProvider,
private val clock: SystemClock,
private val dispatchers: CoroutineDispatchers,
private val matrixClientsProvider: MatrixClientProvider,
private val appCoroutineScope: CoroutineScope,
) : Presenter<CallScreenState> {
@AssistedFactory
@ -78,6 +84,8 @@ class CallScreenPresenter @AssistedInject constructor(
loadUrl(callType, urlState, callWidgetDriver)
}
HandleMatrixClientSyncState()
callWidgetDriver.value?.let { driver ->
LaunchedEffect(Unit) {
driver.incomingMessages
@ -115,21 +123,22 @@ class CallScreenPresenter @AssistedInject constructor(
}
}
fun handleEvents(event: CallScreeEvents) {
fun handleEvents(event: CallScreenEvents) {
when (event) {
is CallScreeEvents.Hangup -> {
is CallScreenEvents.Hangup -> {
val widgetId = callWidgetDriver.value?.id
val interceptor = messageInterceptor.value
if (widgetId != null && interceptor != null && isJoinedCall) {
// If the call was joined, we need to hang up first. Then the UI will be dismissed automatically.
sendHangupMessage(widgetId, interceptor)
isJoinedCall = false
} else {
coroutineScope.launch {
close(callWidgetDriver.value, navigator)
}
}
}
is CallScreeEvents.SetupMessageChannels -> {
is CallScreenEvents.SetupMessageChannels -> {
messageInterceptor.value = event.widgetMessageInterceptor
}
}
@ -166,6 +175,36 @@ class CallScreenPresenter @AssistedInject constructor(
}
}
@Composable
private fun HandleMatrixClientSyncState() {
val coroutineScope = rememberCoroutineScope()
DisposableEffect(Unit) {
val client = (callType as? CallType.RoomCall)?.sessionId?.let {
matrixClientsProvider.getOrNull(it)
} ?: return@DisposableEffect onDispose { }
coroutineScope.launch {
client.syncService().syncState
.onEach { state ->
if (state != SyncState.Running) {
client.syncService().startSync()
}
}
.collect()
}
onDispose {
// We can't use the local coroutine scope here because it will be disposed before this effect
appCoroutineScope.launch {
client.syncService().run {
if (syncState.value == SyncState.Running) {
stopSync()
}
}
}
}
}
}
private fun parseMessage(message: String): WidgetMessage? {
return WidgetMessageSerializer.deserialize(message).getOrNull()
}

View file

@ -22,5 +22,5 @@ data class CallScreenState(
val urlState: Async<String>,
val userAgent: String,
val isInWidgetMode: Boolean,
val eventSink: (CallScreeEvents) -> Unit,
val eventSink: (CallScreenEvents) -> Unit,
)

View file

@ -65,14 +65,14 @@ internal fun CallScreenView(
navigationIcon = {
BackButton(
resourceId = CommonDrawables.ic_compound_close,
onClick = { state.eventSink(CallScreeEvents.Hangup) }
onClick = { state.eventSink(CallScreenEvents.Hangup) }
)
}
)
}
) { padding ->
BackHandler {
state.eventSink(CallScreeEvents.Hangup)
state.eventSink(CallScreenEvents.Hangup)
}
CallWebView(
modifier = Modifier
@ -88,7 +88,7 @@ internal fun CallScreenView(
},
onWebViewCreated = { webView ->
val interceptor = WebViewWidgetMessageInterceptor(webView)
state.eventSink(CallScreeEvents.SetupMessageChannels(interceptor))
state.eventSink(CallScreenEvents.SetupMessageChannels(interceptor))
}
)
}