diff --git a/features/call/src/main/kotlin/io/element/android/features/call/data/WidgetMessage.kt b/features/call/src/main/kotlin/io/element/android/features/call/data/WidgetMessage.kt index 11396c551f..2b38f6850b 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/data/WidgetMessage.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/data/WidgetMessage.kt @@ -18,6 +18,7 @@ package io.element.android.features.call.data import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement @Serializable data class WidgetMessage( @@ -25,6 +26,7 @@ data class WidgetMessage( @SerialName("widgetId") val widgetId: String, @SerialName("requestId") val requestId: String, @SerialName("action") val action: Action, + @SerialName("data") val data: JsonElement? = null, ) { @Serializable @@ -38,6 +40,8 @@ data class WidgetMessage( @Serializable enum class Action { @SerialName("im.vector.hangup") - HangUp + HangUp, + @SerialName("send_event") + SendEvent, } } diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenPresenter.kt b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenPresenter.kt index a29f43af80..f9bb8bde2f 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenPresenter.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenPresenter.kt @@ -19,9 +19,12 @@ package io.element.android.features.call.ui import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -41,6 +44,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch +import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import java.util.UUID class CallScreenPresenter @AssistedInject constructor( @@ -66,6 +72,7 @@ class CallScreenPresenter @AssistedInject constructor( val urlState = remember { mutableStateOf>(Async.Uninitialized) } val callWidgetDriver = remember { mutableStateOf(null) } val messageInterceptor = remember { mutableStateOf(null) } + var isJoinedCall by rememberSaveable { mutableStateOf(false) } LaunchedEffect(Unit) { loadUrl(callType, urlState, callWidgetDriver) @@ -92,8 +99,16 @@ class CallScreenPresenter @AssistedInject constructor( callWidgetDriver.value?.send(it) val parsedMessage = parseMessage(it) - if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget && parsedMessage.action == WidgetMessage.Action.HangUp) { - close(callWidgetDriver.value, navigator) + if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget) { + if (parsedMessage.action == WidgetMessage.Action.HangUp) { + close(callWidgetDriver.value, navigator) + } else if (parsedMessage.action == WidgetMessage.Action.SendEvent) { + // This event is received when a member joins the call, the first one will be the current one + val type = parsedMessage.data?.jsonObject?.get("type")?.jsonPrimitive?.contentOrNull + if (type == "org.matrix.msc3401.call.member") { + isJoinedCall = true + } + } } } .launchIn(this) @@ -105,11 +120,13 @@ class CallScreenPresenter @AssistedInject constructor( is CallScreeEvents.Hangup -> { val widgetId = callWidgetDriver.value?.id val interceptor = messageInterceptor.value - if (widgetId != null && interceptor != null) { + 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) - } - coroutineScope.launch { - close(callWidgetDriver.value, navigator) + } else { + coroutineScope.launch { + close(callWidgetDriver.value, navigator) + } } } is CallScreeEvents.SetupMessageChannels -> { @@ -159,6 +176,7 @@ class CallScreenPresenter @AssistedInject constructor( widgetId = widgetId, requestId = "widgetapi-${clock.epochMillis()}", action = WidgetMessage.Action.HangUp, + data = null, ) messageInterceptor.sendMessage(WidgetMessageSerializer.serialize(message)) } diff --git a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt index 9ac06a63b7..33611515a7 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/ui/CallScreenView.kt @@ -111,12 +111,8 @@ private fun CallWebView( modifier = modifier, factory = { context -> WebView(context).apply { - setup(userAgent, onPermissionsRequested) - if (url is Async.Success) { - loadUrl(url.data) - } - onWebViewCreated(this) + setup(userAgent, onPermissionsRequested) } }, update = { webView -> diff --git a/features/call/src/main/kotlin/io/element/android/features/call/utils/WebViewWidgetMessageInterceptor.kt b/features/call/src/main/kotlin/io/element/android/features/call/utils/WebViewWidgetMessageInterceptor.kt index bdb6ee48f5..e11529f068 100644 --- a/features/call/src/main/kotlin/io/element/android/features/call/utils/WebViewWidgetMessageInterceptor.kt +++ b/features/call/src/main/kotlin/io/element/android/features/call/utils/WebViewWidgetMessageInterceptor.kt @@ -36,7 +36,8 @@ class WebViewWidgetMessageInterceptor( const val LISTENER_NAME = "elementX" } - override val interceptedMessages = MutableSharedFlow(replay = 1, extraBufferCapacity = 2) + // It's important to have extra capacity here to make sure we don't drop any messages + override val interceptedMessages = MutableSharedFlow(extraBufferCapacity = 10) init { webView.webViewClient = object : WebViewClient() { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/RustWidgetDriver.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/RustWidgetDriver.kt index 33e829a206..641be4c618 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/RustWidgetDriver.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/widget/RustWidgetDriver.kt @@ -36,7 +36,8 @@ class RustWidgetDriver( private val widgetCapabilitiesProvider: WidgetCapabilitiesProvider, ): MatrixWidgetDriver { - override val incomingMessages = MutableSharedFlow() + // It's important to have extra capacity here to make sure we don't drop any messages + override val incomingMessages = MutableSharedFlow(extraBufferCapacity = 10) private val driverAndHandle = makeWidgetDriver(widgetSettings.toRustWidgetSettings()) private var receiveMessageJob: Job? = null