Fix issues with stuck 'loading...' state and hangup (#1690)
* Fix issues with stuck 'loading...' state and hangu
This commit is contained in:
parent
42029d6cd2
commit
22767031d9
5 changed files with 34 additions and 14 deletions
|
|
@ -18,6 +18,7 @@ package io.element.android.features.call.data
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class WidgetMessage(
|
data class WidgetMessage(
|
||||||
|
|
@ -25,6 +26,7 @@ data class WidgetMessage(
|
||||||
@SerialName("widgetId") val widgetId: String,
|
@SerialName("widgetId") val widgetId: String,
|
||||||
@SerialName("requestId") val requestId: String,
|
@SerialName("requestId") val requestId: String,
|
||||||
@SerialName("action") val action: Action,
|
@SerialName("action") val action: Action,
|
||||||
|
@SerialName("data") val data: JsonElement? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
|
@ -38,6 +40,8 @@ data class WidgetMessage(
|
||||||
@Serializable
|
@Serializable
|
||||||
enum class Action {
|
enum class Action {
|
||||||
@SerialName("im.vector.hangup")
|
@SerialName("im.vector.hangup")
|
||||||
HangUp
|
HangUp,
|
||||||
|
@SerialName("send_event")
|
||||||
|
SendEvent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,12 @@ package io.element.android.features.call.ui
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import dagger.assisted.Assisted
|
import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
|
|
@ -41,6 +44,9 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.json.contentOrNull
|
||||||
|
import kotlinx.serialization.json.jsonObject
|
||||||
|
import kotlinx.serialization.json.jsonPrimitive
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
class CallScreenPresenter @AssistedInject constructor(
|
class CallScreenPresenter @AssistedInject constructor(
|
||||||
|
|
@ -66,6 +72,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
val urlState = remember { mutableStateOf<Async<String>>(Async.Uninitialized) }
|
val urlState = remember { mutableStateOf<Async<String>>(Async.Uninitialized) }
|
||||||
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
val callWidgetDriver = remember { mutableStateOf<MatrixWidgetDriver?>(null) }
|
||||||
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
val messageInterceptor = remember { mutableStateOf<WidgetMessageInterceptor?>(null) }
|
||||||
|
var isJoinedCall by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
loadUrl(callType, urlState, callWidgetDriver)
|
loadUrl(callType, urlState, callWidgetDriver)
|
||||||
|
|
@ -92,8 +99,16 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
callWidgetDriver.value?.send(it)
|
callWidgetDriver.value?.send(it)
|
||||||
|
|
||||||
val parsedMessage = parseMessage(it)
|
val parsedMessage = parseMessage(it)
|
||||||
if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget && parsedMessage.action == WidgetMessage.Action.HangUp) {
|
if (parsedMessage?.direction == WidgetMessage.Direction.FromWidget) {
|
||||||
close(callWidgetDriver.value, navigator)
|
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)
|
.launchIn(this)
|
||||||
|
|
@ -105,11 +120,13 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
is CallScreeEvents.Hangup -> {
|
is CallScreeEvents.Hangup -> {
|
||||||
val widgetId = callWidgetDriver.value?.id
|
val widgetId = callWidgetDriver.value?.id
|
||||||
val interceptor = messageInterceptor.value
|
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)
|
sendHangupMessage(widgetId, interceptor)
|
||||||
}
|
} else {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
close(callWidgetDriver.value, navigator)
|
close(callWidgetDriver.value, navigator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is CallScreeEvents.SetupMessageChannels -> {
|
is CallScreeEvents.SetupMessageChannels -> {
|
||||||
|
|
@ -159,6 +176,7 @@ class CallScreenPresenter @AssistedInject constructor(
|
||||||
widgetId = widgetId,
|
widgetId = widgetId,
|
||||||
requestId = "widgetapi-${clock.epochMillis()}",
|
requestId = "widgetapi-${clock.epochMillis()}",
|
||||||
action = WidgetMessage.Action.HangUp,
|
action = WidgetMessage.Action.HangUp,
|
||||||
|
data = null,
|
||||||
)
|
)
|
||||||
messageInterceptor.sendMessage(WidgetMessageSerializer.serialize(message))
|
messageInterceptor.sendMessage(WidgetMessageSerializer.serialize(message))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -111,12 +111,8 @@ private fun CallWebView(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
factory = { context ->
|
factory = { context ->
|
||||||
WebView(context).apply {
|
WebView(context).apply {
|
||||||
setup(userAgent, onPermissionsRequested)
|
|
||||||
if (url is Async.Success) {
|
|
||||||
loadUrl(url.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
onWebViewCreated(this)
|
onWebViewCreated(this)
|
||||||
|
setup(userAgent, onPermissionsRequested)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = { webView ->
|
update = { webView ->
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ class WebViewWidgetMessageInterceptor(
|
||||||
const val LISTENER_NAME = "elementX"
|
const val LISTENER_NAME = "elementX"
|
||||||
}
|
}
|
||||||
|
|
||||||
override val interceptedMessages = MutableSharedFlow<String>(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<String>(extraBufferCapacity = 10)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
webView.webViewClient = object : WebViewClient() {
|
webView.webViewClient = object : WebViewClient() {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,8 @@ class RustWidgetDriver(
|
||||||
private val widgetCapabilitiesProvider: WidgetCapabilitiesProvider,
|
private val widgetCapabilitiesProvider: WidgetCapabilitiesProvider,
|
||||||
): MatrixWidgetDriver {
|
): MatrixWidgetDriver {
|
||||||
|
|
||||||
override val incomingMessages = MutableSharedFlow<String>()
|
// It's important to have extra capacity here to make sure we don't drop any messages
|
||||||
|
override val incomingMessages = MutableSharedFlow<String>(extraBufferCapacity = 10)
|
||||||
|
|
||||||
private val driverAndHandle = makeWidgetDriver(widgetSettings.toRustWidgetSettings())
|
private val driverAndHandle = makeWidgetDriver(widgetSettings.toRustWidgetSettings())
|
||||||
private var receiveMessageJob: Job? = null
|
private var receiveMessageJob: Job? = null
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue