Ensure the user can join the call even if they has joined a call in another session.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.call.api
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
|
||||
/**
|
||||
* Value for the local current call.
|
||||
*/
|
||||
sealed interface CurrentCall {
|
||||
data object None : CurrentCall
|
||||
|
||||
data class RoomCall(
|
||||
val roomId: RoomId,
|
||||
) : CurrentCall
|
||||
|
||||
data class ExternalUrl(
|
||||
val url: String,
|
||||
) : CurrentCall
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.call.api
|
||||
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
interface CurrentCallObserver {
|
||||
/**
|
||||
* The current call state flow, which will be updated when the active call changes.
|
||||
* This value reflect the local state of the call. It is not updated if the user answers
|
||||
* a call from another session.
|
||||
*/
|
||||
val currentCall: StateFlow<CurrentCall>
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.appconfig.ElementCallConfig
|
||||
import io.element.android.features.call.api.CallType
|
||||
import io.element.android.features.call.api.CurrentCall
|
||||
import io.element.android.features.call.impl.notifications.CallNotificationData
|
||||
import io.element.android.features.call.impl.notifications.RingingCallNotificationCreator
|
||||
import io.element.android.libraries.di.AppScope
|
||||
@@ -82,6 +83,7 @@ class DefaultActiveCallManager @Inject constructor(
|
||||
private val ringingCallNotificationCreator: RingingCallNotificationCreator,
|
||||
private val notificationManagerCompat: NotificationManagerCompat,
|
||||
private val matrixClientProvider: MatrixClientProvider,
|
||||
private val defaultCurrentCallObserver: DefaultCurrentCallObserver,
|
||||
) : ActiveCallManager {
|
||||
private var timedOutCallJob: Job? = null
|
||||
|
||||
@@ -89,6 +91,7 @@ class DefaultActiveCallManager @Inject constructor(
|
||||
|
||||
init {
|
||||
observeRingingCall()
|
||||
observeCurrentCall()
|
||||
}
|
||||
|
||||
override fun registerIncomingCall(notificationData: CallNotificationData) {
|
||||
@@ -209,6 +212,28 @@ class DefaultActiveCallManager @Inject constructor(
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
private fun observeCurrentCall() {
|
||||
activeCall
|
||||
.onEach { value ->
|
||||
if (value == null) {
|
||||
defaultCurrentCallObserver.onCallEnded()
|
||||
} else {
|
||||
when (value.callState) {
|
||||
is CallState.Ringing -> {
|
||||
// Nothing to do
|
||||
}
|
||||
is CallState.InCall -> {
|
||||
when (val callType = value.callType) {
|
||||
is CallType.ExternalUrl -> defaultCurrentCallObserver.onCallStarted(CurrentCall.ExternalUrl(callType.url))
|
||||
is CallType.RoomCall -> defaultCurrentCallObserver.onCallStarted(CurrentCall.RoomCall(callType.roomId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
* Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.features.call.impl.utils
|
||||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.features.call.api.CurrentCall
|
||||
import io.element.android.features.call.api.CurrentCallObserver
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@SingleIn(AppScope::class)
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultCurrentCallObserver @Inject constructor() : CurrentCallObserver {
|
||||
override val currentCall = MutableStateFlow<CurrentCall>(CurrentCall.None)
|
||||
|
||||
fun onCallStarted(call: CurrentCall) {
|
||||
currentCall.value = call
|
||||
}
|
||||
|
||||
fun onCallEnded() {
|
||||
currentCall.value = CurrentCall.None
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import io.element.android.features.call.impl.notifications.RingingCallNotificati
|
||||
import io.element.android.features.call.impl.utils.ActiveCall
|
||||
import io.element.android.features.call.impl.utils.CallState
|
||||
import io.element.android.features.call.impl.utils.DefaultActiveCallManager
|
||||
import io.element.android.features.call.impl.utils.DefaultCurrentCallObserver
|
||||
import io.element.android.features.call.test.aCallNotificationData
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
@@ -299,5 +300,6 @@ class DefaultActiveCallManagerTest {
|
||||
),
|
||||
notificationManagerCompat = notificationManagerCompat,
|
||||
matrixClientProvider = matrixClientProvider,
|
||||
defaultCurrentCallObserver = DefaultCurrentCallObserver(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ private fun OnGoingCallMenuItem(
|
||||
onJoinCallClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (!roomCallState.isUserInTheCall) {
|
||||
if (!roomCallState.isUserLocallyInTheCall) {
|
||||
Button(
|
||||
onClick = onJoinCallClick,
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
|
||||
@@ -20,6 +20,7 @@ sealed interface RoomCallState {
|
||||
data class OnGoing(
|
||||
val canJoinCall: Boolean,
|
||||
val isUserInTheCall: Boolean,
|
||||
val isUserLocallyInTheCall: Boolean,
|
||||
) : RoomCallState
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ open class RoomCallStateProvider : PreviewParameterProvider<RoomCallState> {
|
||||
fun anOngoingCallState(
|
||||
canJoinCall: Boolean = true,
|
||||
isUserInTheCall: Boolean = false,
|
||||
isUserLocallyInTheCall: Boolean = isUserInTheCall,
|
||||
) = RoomCallState.OnGoing(
|
||||
canJoinCall = canJoinCall,
|
||||
isUserInTheCall = isUserInTheCall,
|
||||
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
||||
)
|
||||
|
||||
fun aStandByCallState(
|
||||
|
||||
@@ -20,6 +20,7 @@ setupAnvil()
|
||||
dependencies {
|
||||
api(projects.features.roomcall.api)
|
||||
implementation(libs.kotlinx.collections.immutable)
|
||||
implementation(projects.features.call.api)
|
||||
implementation(projects.libraries.architecture)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(projects.libraries.matrixui)
|
||||
|
||||
@@ -12,6 +12,8 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import io.element.android.features.call.api.CurrentCall
|
||||
import io.element.android.features.call.api.CurrentCallObserver
|
||||
import io.element.android.features.roomcall.api.RoomCallState
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
@@ -20,6 +22,7 @@ import javax.inject.Inject
|
||||
|
||||
class RoomCallStatePresenter @Inject constructor(
|
||||
private val room: MatrixRoom,
|
||||
private val currentCallObserver: CurrentCallObserver,
|
||||
) : Presenter<RoomCallState> {
|
||||
@Composable
|
||||
override fun present(): RoomCallState {
|
||||
@@ -31,10 +34,17 @@ class RoomCallStatePresenter @Inject constructor(
|
||||
room.sessionId in roomInfo?.activeRoomCallParticipants.orEmpty()
|
||||
}
|
||||
}
|
||||
val currentCall by currentCallObserver.currentCall.collectAsState()
|
||||
val isUserLocallyInTheCall by remember {
|
||||
derivedStateOf {
|
||||
(currentCall as? CurrentCall.RoomCall)?.roomId == room.roomId
|
||||
}
|
||||
}
|
||||
val callState = when {
|
||||
roomInfo?.hasRoomCall == true -> RoomCallState.OnGoing(
|
||||
canJoinCall = canJoinCall,
|
||||
isUserInTheCall = isUserInTheCall,
|
||||
isUserLocallyInTheCall = isUserLocallyInTheCall,
|
||||
)
|
||||
else -> RoomCallState.StandBy(canStartCall = canJoinCall)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user