Merge pull request #4925 from element-hq/feature/bma/elementCallCheck

Introduce SessionEnterpriseService.
This commit is contained in:
Benoit Marty 2025-06-24 12:52:07 +02:00 committed by GitHub
commit 1f34125a5f
15 changed files with 95 additions and 20 deletions

@ -1 +1 @@
Subproject commit 4a07c862a23a9fd1418eabf132cf9d6b25ea4927 Subproject commit b7ababb9537da8bec254b8ed00b5a4122e9f3e3b

View file

@ -16,8 +16,6 @@ interface EnterpriseService {
fun defaultHomeserverList(): List<String> fun defaultHomeserverList(): List<String>
suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String): Boolean
suspend fun isElementCallAvailable(): Boolean
fun semanticColorsLight(): SemanticColors fun semanticColorsLight(): SemanticColors
fun semanticColorsDark(): SemanticColors fun semanticColorsDark(): SemanticColors

View file

@ -0,0 +1,12 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.enterprise.api
interface SessionEnterpriseService {
suspend fun isElementCallAvailable(): Boolean
}

View file

@ -25,8 +25,6 @@ class DefaultEnterpriseService @Inject constructor() : EnterpriseService {
override fun defaultHomeserverList(): List<String> = emptyList() override fun defaultHomeserverList(): List<String> = emptyList()
override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true override suspend fun isAllowedToConnectToHomeserver(homeserverUrl: String) = true
override suspend fun isElementCallAvailable(): Boolean = true
override fun semanticColorsLight(): SemanticColors = compoundColorsLight override fun semanticColorsLight(): SemanticColors = compoundColorsLight
override fun semanticColorsDark(): SemanticColors = compoundColorsDark override fun semanticColorsDark(): SemanticColors = compoundColorsDark

View file

@ -0,0 +1,18 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.enterprise.impl
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.libraries.di.SessionScope
import javax.inject.Inject
@ContributesBinding(SessionScope::class)
class DefaultSessionEnterpriseService @Inject constructor() : SessionEnterpriseService {
override suspend fun isElementCallAvailable(): Boolean = true
}

View file

@ -0,0 +1,20 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.enterprise.impl
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
class DefaultSessionEnterpriseServiceTest {
@Test
fun `isElementCallAvailable is always true`() = runTest {
val service = DefaultSessionEnterpriseService()
assertThat(service.isElementCallAvailable()).isTrue()
}
}

View file

@ -18,7 +18,6 @@ class FakeEnterpriseService(
private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() }, private val isEnterpriseUserResult: (SessionId) -> Boolean = { lambdaError() },
private val defaultHomeserverListResult: () -> List<String> = { emptyList() }, private val defaultHomeserverListResult: () -> List<String> = { emptyList() },
private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() }, private val isAllowedToConnectToHomeserverResult: (String) -> Boolean = { lambdaError() },
private val isElementCallAvailableResult: () -> Boolean = { lambdaError() },
private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() }, private val semanticColorsLightResult: () -> SemanticColors = { lambdaError() },
private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() }, private val semanticColorsDarkResult: () -> SemanticColors = { lambdaError() },
private val firebasePushGatewayResult: () -> String? = { lambdaError() }, private val firebasePushGatewayResult: () -> String? = { lambdaError() },
@ -36,10 +35,6 @@ class FakeEnterpriseService(
isAllowedToConnectToHomeserverResult(homeserverUrl) isAllowedToConnectToHomeserverResult(homeserverUrl)
} }
override suspend fun isElementCallAvailable(): Boolean = simulateLongTask {
isElementCallAvailableResult()
}
override fun semanticColorsLight(): SemanticColors { override fun semanticColorsLight(): SemanticColors {
return semanticColorsLightResult() return semanticColorsLightResult()
} }

View file

@ -0,0 +1,20 @@
/*
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.features.enterprise.test
import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.tests.testutils.lambda.lambdaError
import io.element.android.tests.testutils.simulateLongTask
class FakeSessionEnterpriseService(
private val isElementCallAvailableResult: () -> Boolean = { lambdaError() },
) : SessionEnterpriseService {
override suspend fun isElementCallAvailable(): Boolean = simulateLongTask {
isElementCallAvailableResult()
}
}

View file

@ -15,7 +15,7 @@ import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import io.element.android.features.call.api.CurrentCall import io.element.android.features.call.api.CurrentCall
import io.element.android.features.call.api.CurrentCallService import io.element.android.features.call.api.CurrentCallService
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
@ -25,12 +25,12 @@ import javax.inject.Inject
class RoomCallStatePresenter @Inject constructor( class RoomCallStatePresenter @Inject constructor(
private val room: JoinedRoom, private val room: JoinedRoom,
private val currentCallService: CurrentCallService, private val currentCallService: CurrentCallService,
private val enterpriseService: EnterpriseService, private val sessionEnterpriseService: SessionEnterpriseService,
) : Presenter<RoomCallState> { ) : Presenter<RoomCallState> {
@Composable @Composable
override fun present(): RoomCallState { override fun present(): RoomCallState {
val isAvailable by produceState(false) { val isAvailable by produceState(false) {
value = enterpriseService.isElementCallAvailable() value = sessionEnterpriseService.isElementCallAvailable()
} }
val roomInfo by room.roomInfoFlow.collectAsState() val roomInfo by room.roomInfoFlow.collectAsState()
val syncUpdateFlow = room.syncUpdateFlow.collectAsState() val syncUpdateFlow = room.syncUpdateFlow.collectAsState()

View file

@ -11,7 +11,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.call.api.CurrentCall import io.element.android.features.call.api.CurrentCall
import io.element.android.features.call.api.CurrentCallService import io.element.android.features.call.api.CurrentCallService
import io.element.android.features.call.test.FakeCurrentCallService import io.element.android.features.call.test.FakeCurrentCallService
import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.enterprise.test.FakeSessionEnterpriseService
import io.element.android.features.roomcall.api.RoomCallState import io.element.android.features.roomcall.api.RoomCallState
import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.test.room.FakeBaseRoom import io.element.android.libraries.matrix.test.room.FakeBaseRoom
@ -230,7 +230,7 @@ class RoomCallStatePresenterTest {
return RoomCallStatePresenter( return RoomCallStatePresenter(
room = joinedRoom, room = joinedRoom,
currentCallService = currentCallService, currentCallService = currentCallService,
enterpriseService = FakeEnterpriseService( sessionEnterpriseService = FakeSessionEnterpriseService(
isElementCallAvailableResult = { isElementCallAvailable }, isElementCallAvailableResult = { isElementCallAvailable },
), ),
) )

View file

@ -21,7 +21,7 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.enterprise.api.EnterpriseService import io.element.android.features.enterprise.api.SessionEnterpriseService
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvents
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog import io.element.android.features.userprofile.api.UserProfileState.ConfirmationDialog
@ -45,7 +45,7 @@ class UserProfilePresenter @AssistedInject constructor(
@Assisted private val userId: UserId, @Assisted private val userId: UserId,
private val client: MatrixClient, private val client: MatrixClient,
private val startDMAction: StartDMAction, private val startDMAction: StartDMAction,
private val enterpriseService: EnterpriseService, private val sessionEnterpriseService: SessionEnterpriseService,
) : Presenter<UserProfileState> { ) : Presenter<UserProfileState> {
@AssistedFactory @AssistedFactory
interface Factory { interface Factory {
@ -62,7 +62,7 @@ class UserProfilePresenter @AssistedInject constructor(
@Composable @Composable
private fun getCanCall(roomId: RoomId?): State<Boolean> { private fun getCanCall(roomId: RoomId?): State<Boolean> {
val isElementCallAvailable by produceState(initialValue = false, roomId) { val isElementCallAvailable by produceState(initialValue = false, roomId) {
value = enterpriseService.isElementCallAvailable() value = sessionEnterpriseService.isElementCallAvailable()
} }
return produceState(initialValue = false, isElementCallAvailable, roomId) { return produceState(initialValue = false, isElementCallAvailable, roomId) {

View file

@ -16,7 +16,7 @@ import com.google.common.truth.Truth.assertThat
import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUser import io.element.android.features.createroom.api.ConfirmingStartDmWithMatrixUser
import io.element.android.features.createroom.api.StartDMAction import io.element.android.features.createroom.api.StartDMAction
import io.element.android.features.createroom.test.FakeStartDMAction import io.element.android.features.createroom.test.FakeStartDMAction
import io.element.android.features.enterprise.test.FakeEnterpriseService import io.element.android.features.enterprise.test.FakeSessionEnterpriseService
import io.element.android.features.userprofile.api.UserProfileEvents import io.element.android.features.userprofile.api.UserProfileEvents
import io.element.android.features.userprofile.api.UserProfileState import io.element.android.features.userprofile.api.UserProfileState
import io.element.android.features.userprofile.api.UserProfileVerificationState import io.element.android.features.userprofile.api.UserProfileVerificationState
@ -410,7 +410,7 @@ class UserProfilePresenterTest {
userId = userId, userId = userId,
client = client, client = client,
startDMAction = startDMAction, startDMAction = startDMAction,
enterpriseService = FakeEnterpriseService( sessionEnterpriseService = FakeSessionEnterpriseService(
isElementCallAvailableResult = { isElementCallAvailable }, isElementCallAvailableResult = { isElementCallAvailable },
), ),
) )

View file

@ -164,6 +164,11 @@ interface MatrixClient {
* Check if the user can report a room. * Check if the user can report a room.
*/ */
suspend fun canReportRoom(): Boolean suspend fun canReportRoom(): Boolean
/**
* Return true if Livekit Rtc is supported, i.e. if Element Call is available.
*/
suspend fun isLivekitRtcSupported(): Boolean
} }
/** /**

View file

@ -678,6 +678,10 @@ class RustMatrixClient(
}.getOrDefault(false) }.getOrDefault(false)
} }
override suspend fun isLivekitRtcSupported(): Boolean = withContext(sessionDispatcher) {
innerClient.isLivekitRtcSupported()
}
private suspend fun File.getCacheSize( private suspend fun File.getCacheSize(
includeCryptoDb: Boolean = false, includeCryptoDb: Boolean = false,
): Long = withContext(sessionDispatcher) { ): Long = withContext(sessionDispatcher) {

View file

@ -89,6 +89,7 @@ class FakeMatrixClient(
private val ignoreUserResult: (UserId) -> Result<Unit> = { lambdaError() }, private val ignoreUserResult: (UserId) -> Result<Unit> = { lambdaError() },
private var unIgnoreUserResult: (UserId) -> Result<Unit> = { Result.success(Unit) }, private var unIgnoreUserResult: (UserId) -> Result<Unit> = { Result.success(Unit) },
private val canReportRoomLambda: () -> Boolean = { false }, private val canReportRoomLambda: () -> Boolean = { false },
private val isLivekitRtcSupportedLambda: () -> Boolean = { false },
override val ignoredUsersFlow: StateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf()), override val ignoredUsersFlow: StateFlow<ImmutableList<UserId>> = MutableStateFlow(persistentListOf()),
) : MatrixClient { ) : MatrixClient {
var setDisplayNameCalled: Boolean = false var setDisplayNameCalled: Boolean = false
@ -334,4 +335,8 @@ class FakeMatrixClient(
override suspend fun canReportRoom(): Boolean { override suspend fun canReportRoom(): Boolean {
return canReportRoomLambda() return canReportRoomLambda()
} }
override suspend fun isLivekitRtcSupported(): Boolean {
return isLivekitRtcSupportedLambda()
}
} }