From e3c41a07e5cae7823ea42070aae94cd41cb085c4 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 11:52:35 +0200 Subject: [PATCH 01/14] Empty ViewModel V2 --- .../app/core/di/MavericksViewModelModule.kt | 5 ++ .../settings/devices/v2/DeviceFullInfo.kt | 28 ++++++++++ .../settings/devices/v2/DevicesAction.kt | 21 +++++++ .../settings/devices/v2/DevicesViewEvent.kt | 34 +++++++++++ .../settings/devices/v2/DevicesViewModel.kt | 56 +++++++++++++++++++ .../settings/devices/v2/DevicesViewState.kt | 30 ++++++++++ 6 files changed, 174 insertions(+) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewEvent.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt diff --git a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt index 40484f57e8..8bcfd4e422 100644 --- a/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt +++ b/vector/src/main/java/im/vector/app/core/di/MavericksViewModelModule.kt @@ -352,6 +352,11 @@ interface MavericksViewModelModule { @MavericksViewModelKey(DevicesViewModel::class) fun devicesViewModelFactory(factory: DevicesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds + @IntoMap + @MavericksViewModelKey(im.vector.app.features.settings.devices.v2.DevicesViewModel::class) + fun devicesViewModelV2Factory(factory: im.vector.app.features.settings.devices.v2.DevicesViewModel.Factory): MavericksAssistedViewModelFactory<*, *> + @Binds @IntoMap @MavericksViewModelKey(KeyRequestListViewModel::class) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt new file mode 100644 index 0000000000..f0a91c6183 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +data class DeviceFullInfo( + val deviceInfo: DeviceInfo, + val cryptoDeviceInfo: CryptoDeviceInfo?, + val roomEncryptionTrustLevel: RoomEncryptionTrustLevel, + val isInactive: Boolean, +) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt new file mode 100644 index 0000000000..6fb24c96b2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.platform.VectorViewModelAction + +sealed class DevicesAction : VectorViewModelAction diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewEvent.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewEvent.kt new file mode 100644 index 0000000000..e83004843d --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewEvent.kt @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.platform.VectorViewEvents +import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo + +sealed class DevicesViewEvent : VectorViewEvents { + data class Loading(val message: CharSequence? = null) : DevicesViewEvent() + data class Failure(val throwable: Throwable) : DevicesViewEvent() + data class RequestReAuth(val registrationFlowResponse: RegistrationFlowResponse, val lastErrorCode: String?) : DevicesViewEvent() + data class PromptRenameDevice(val deviceInfo: DeviceInfo) : DevicesViewEvent() + data class ShowVerifyDevice(val userId: String, val transactionId: String?) : DevicesViewEvent() + data class SelfVerification(val session: Session) : DevicesViewEvent() + data class ShowManuallyVerify(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesViewEvent() + object PromptResetSecrets : DevicesViewEvent() +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt new file mode 100644 index 0000000000..f496fae596 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import com.airbnb.mvrx.MavericksViewModelFactory +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import im.vector.app.core.di.MavericksAssistedViewModelFactory +import im.vector.app.core.di.hiltMavericksViewModelFactory +import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.login.ReAuthHelper +import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase +import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase +import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import org.matrix.android.sdk.api.Matrix +import org.matrix.android.sdk.api.session.Session + +class DevicesViewModel @AssistedInject constructor( + @Assisted initialState: DevicesViewState, + private val session: Session, + private val reAuthHelper: ReAuthHelper, + private val stringProvider: StringProvider, + private val matrix: Matrix, + private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, + getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, + private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, +) : VectorViewModel(initialState) { + + @AssistedFactory + interface Factory : MavericksAssistedViewModelFactory { + override fun create(initialState: DevicesViewState): DevicesViewModel + } + + companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + + override fun handle(action: DevicesAction) { + TODO("Not yet implemented") + } +} + diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt new file mode 100644 index 0000000000..284520f5b2 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import com.airbnb.mvrx.Async +import com.airbnb.mvrx.MavericksState +import com.airbnb.mvrx.Uninitialized + +data class DevicesViewState( + val myDeviceId: String = "", + val devices: Async> = Uninitialized, + val hasAccountCrossSigning: Boolean = false, + val accountCrossSigningIsTrusted: Boolean = false, + val unverifiedSessionsCount: Int = 0, + val inactiveSessionsCount: Int = 0, +) : MavericksState From 4a6a39b3c44cd05531e4d37a8bf63af399d20680 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 11:59:12 +0200 Subject: [PATCH 02/14] Declare MarkAsManuallyVerified action --- .../app/features/settings/devices/v2/DevicesAction.kt | 5 ++++- .../app/features/settings/devices/v2/DevicesViewModel.kt | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt index 6fb24c96b2..8c7718bfcf 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesAction.kt @@ -17,5 +17,8 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.core.platform.VectorViewModelAction +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo -sealed class DevicesAction : VectorViewModelAction +sealed class DevicesAction : VectorViewModelAction { + data class MarkAsManuallyVerified(val cryptoDeviceInfo: CryptoDeviceInfo) : DevicesAction() +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index f496fae596..00fae17cad 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -50,7 +50,13 @@ class DevicesViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() override fun handle(action: DevicesAction) { - TODO("Not yet implemented") + when(action) { + is DevicesAction.MarkAsManuallyVerified -> handleMarkAsManuallyVerifiedAction() + } + } + + private fun handleMarkAsManuallyVerifiedAction() { + // TODO implement when needed } } From 712d39b9dd260ddb2f700e3779b69dba546b46d2 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 11:59:32 +0200 Subject: [PATCH 03/14] Inject new ViewModel in the fragment V2 + add use cases --- ...etCurrentSessionCrossSigningInfoUseCase.kt | 3 +- ...GetEncryptionTrustLevelForDeviceUseCase.kt | 1 + .../CurrentSessionCrossSigningInfo.kt | 8 +-- .../settings/devices/v2/DevicesViewModel.kt | 58 +++++++++++----- .../settings/devices/v2/DevicesViewState.kt | 5 +- ...etCurrentSessionCrossSigningInfoUseCase.kt | 49 ++++++++++++++ .../v2/GetDeviceFullInfoListUseCase.kt | 67 +++++++++++++++++++ .../v2/VectorSettingsDevicesFragment.kt | 36 ++++------ .../v2/list/OtherSessionsController.kt | 6 +- .../devices/v2/list/OtherSessionsView.kt | 2 +- .../devices/v2/list/SessionInfoView.kt | 2 +- .../devices/v2/list/SessionInfoViewState.kt | 2 +- .../v2/overview/GetDeviceFullInfoUseCase.kt | 4 +- .../v2/overview/SessionOverviewFragment.kt | 2 +- .../v2/overview/SessionOverviewViewState.kt | 2 +- ...rrentSessionCrossSigningInfoUseCaseTest.kt | 1 + ...ncryptionTrustLevelForDeviceUseCaseTest.kt | 1 + .../overview/GetDeviceFullInfoUseCaseTest.kt | 2 +- 18 files changed, 195 insertions(+), 56 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/{ => v2}/CurrentSessionCrossSigningInfo.kt (78%) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCase.kt index d07bd5daae..8b58bd0536 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCase.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import javax.inject.Inject class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( @@ -28,7 +29,7 @@ class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( val isCrossSigningInitialized = session.cryptoService().crossSigningService().isCrossSigningInitialized() val isCrossSigningVerified = session.cryptoService().crossSigningService().isCrossSigningVerified() return CurrentSessionCrossSigningInfo( - deviceId = session.sessionParams.deviceId, + deviceId = session.sessionParams.deviceId.orEmpty(), isCrossSigningInitialized = isCrossSigningInitialized, isCrossSigningVerified = isCrossSigningVerified ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt index e5ef4b446b..433c4da233 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/CurrentSessionCrossSigningInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/CurrentSessionCrossSigningInfo.kt similarity index 78% rename from vector/src/main/java/im/vector/app/features/settings/devices/CurrentSessionCrossSigningInfo.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/CurrentSessionCrossSigningInfo.kt index 790de08823..cccdb23d52 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/CurrentSessionCrossSigningInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/CurrentSessionCrossSigningInfo.kt @@ -14,13 +14,13 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 /** * Used to hold some info about the cross signing of the current Session. */ data class CurrentSessionCrossSigningInfo( - val deviceId: String?, - val isCrossSigningInitialized: Boolean, - val isCrossSigningVerified: Boolean, + val deviceId: String = "", + val isCrossSigningInitialized: Boolean = false, + val isCrossSigningVerified: Boolean = false, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index 00fae17cad..09de9ca8e7 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -17,29 +17,22 @@ package im.vector.app.features.settings.devices.v2 import com.airbnb.mvrx.MavericksViewModelFactory +import com.airbnb.mvrx.Success import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel -import im.vector.app.core.resources.StringProvider -import im.vector.app.features.login.ReAuthHelper -import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase -import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase -import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase -import org.matrix.android.sdk.api.Matrix -import org.matrix.android.sdk.api.session.Session +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import org.matrix.android.sdk.api.extensions.orFalse +// TODO add unit tests class DevicesViewModel @AssistedInject constructor( @Assisted initialState: DevicesViewState, - private val session: Session, - private val reAuthHelper: ReAuthHelper, - private val stringProvider: StringProvider, - private val matrix: Matrix, - private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, - getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, - private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, + private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, + private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase, ) : VectorViewModel(initialState) { @AssistedFactory @@ -49,8 +42,43 @@ class DevicesViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + init { + observeCurrentSessionCrossSigningInfo() + observeDevices() + } + + private fun observeCurrentSessionCrossSigningInfo() { + getCurrentSessionCrossSigningInfoUseCase.execute() + .onEach { crossSigningInfo -> + setState { + copy(currentSessionCrossSigningInfo = crossSigningInfo) + } + } + .launchIn(viewModelScope) + } + + private fun observeDevices() { + getDeviceFullInfoListUseCase.execute() + .execute { async -> + if (async is Success) { + val deviceFullInfoList = async.invoke() + val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.trustLevel?.isVerified().orFalse() } + val inactiveSessionsCount = deviceFullInfoList.count { it.isInactive } + copy( + devices = async, + unverifiedSessionsCount = unverifiedSessionsCount, + inactiveSessionsCount = inactiveSessionsCount, + ) + } else { + copy( + devices = async + ) + } + } + } + override fun handle(action: DevicesAction) { - when(action) { + when (action) { is DevicesAction.MarkAsManuallyVerified -> handleMarkAsManuallyVerifiedAction() } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt index 284520f5b2..3fc061daa4 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewState.kt @@ -21,10 +21,9 @@ import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized data class DevicesViewState( - val myDeviceId: String = "", + val currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo(), val devices: Async> = Uninitialized, - val hasAccountCrossSigning: Boolean = false, - val accountCrossSigningIsTrusted: Boolean = false, val unverifiedSessionsCount: Int = 0, val inactiveSessionsCount: Int = 0, + val isLoading: Boolean = false, ) : MavericksState diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt new file mode 100644 index 0000000000..9f7a3d8208 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.api.util.Optional +import org.matrix.android.sdk.flow.flow +import javax.inject.Inject + +// TODO add unit tests +class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + + fun execute(): Flow { + return activeSessionHolder.getSafeActiveSession() + ?.let { session -> + session.flow().liveCrossSigningInfo(session.myUserId) + .map { convertToSigningInfo(session.sessionParams.deviceId.orEmpty(), it) } + } ?: emptyFlow() + } + + private fun convertToSigningInfo(deviceId: String, mxCrossSigningInfo: Optional): CurrentSessionCrossSigningInfo { + return CurrentSessionCrossSigningInfo( + deviceId = deviceId, + isCrossSigningInitialized = mxCrossSigningInfo.getOrNull() != null, + isCrossSigningVerified = mxCrossSigningInfo.getOrNull()?.isTrusted() == true + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt new file mode 100644 index 0000000000..fbe6609ef4 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.di.ActiveSessionHolder +import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase +import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.flow.flow +import javax.inject.Inject + +// TODO add unit tests +class GetDeviceFullInfoListUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, + private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, + private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase, + private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, +) { + + fun execute(): Flow> { + return activeSessionHolder.getSafeActiveSession()?.let { session -> + val deviceFullInfoFlow = combine( + getCurrentSessionCrossSigningInfoUseCase.execute(), + session.flow().liveUserCryptoDevices(session.myUserId), + session.flow().liveMyDevicesInfo() + ) { currentSessionCrossSigningInfo, cryptoList, infoList -> + convertToDeviceFullInfoList(currentSessionCrossSigningInfo, cryptoList, infoList) + } + + deviceFullInfoFlow.distinctUntilChanged() + } ?: emptyFlow() + } + + private fun convertToDeviceFullInfoList( + currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, + cryptoList: List, + infoList: List, + ): List { + return infoList + .sortedByDescending { it.lastSeenTs } + .map { deviceInfo -> + val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } + val trustLevelForShield = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) + val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) + DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive) + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt index dc72d4fe9c..acf33dc01d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/VectorSettingsDevicesFragment.kt @@ -24,8 +24,6 @@ import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible -import com.airbnb.mvrx.Async -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.withState @@ -39,10 +37,6 @@ import im.vector.app.core.resources.DrawableProvider import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.verification.VerificationBottomSheet -import im.vector.app.features.settings.devices.DeviceFullInfo -import im.vector.app.features.settings.devices.DevicesAction -import im.vector.app.features.settings.devices.DevicesViewEvents -import im.vector.app.features.settings.devices.DevicesViewModel import im.vector.app.features.settings.devices.v2.list.OtherSessionsController import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState @@ -93,27 +87,27 @@ class VectorSettingsDevicesFragment : private fun observeViewEvents() { viewModel.observeViewEvents { when (it) { - is DevicesViewEvents.Loading -> showLoading(it.message) - is DevicesViewEvents.Failure -> showFailure(it.throwable) - is DevicesViewEvents.RequestReAuth -> Unit // TODO. Next PR - is DevicesViewEvents.PromptRenameDevice -> Unit // TODO. Next PR - is DevicesViewEvents.ShowVerifyDevice -> { + is DevicesViewEvent.Loading -> showLoading(it.message) + is DevicesViewEvent.Failure -> showFailure(it.throwable) + is DevicesViewEvent.RequestReAuth -> Unit // TODO. Next PR + is DevicesViewEvent.PromptRenameDevice -> Unit // TODO. Next PR + is DevicesViewEvent.ShowVerifyDevice -> { VerificationBottomSheet.withArgs( roomId = null, otherUserId = it.userId, transactionId = it.transactionId ).show(childFragmentManager, "REQPOP") } - is DevicesViewEvents.SelfVerification -> { + is DevicesViewEvent.SelfVerification -> { VerificationBottomSheet.forSelfVerification(it.session) .show(childFragmentManager, "REQPOP") } - is DevicesViewEvents.ShowManuallyVerify -> { + is DevicesViewEvent.ShowManuallyVerify -> { ManuallyVerifyDialog.show(requireActivity(), it.cryptoDeviceInfo) { viewModel.handle(DevicesAction.MarkAsManuallyVerified(it.cryptoDeviceInfo)) } } - is DevicesViewEvents.PromptResetSecrets -> { + is DevicesViewEvent.PromptResetSecrets -> { navigator.open4SSetup(requireActivity(), SetupMode.PASSPHRASE_AND_NEEDED_SECRETS_RESET) } } @@ -151,10 +145,11 @@ class VectorSettingsDevicesFragment : override fun invalidate() = withState(viewModel) { state -> if (state.devices is Success) { val devices = state.devices() + val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId val currentDeviceInfo = devices?.firstOrNull { - it.deviceInfo.deviceId == state.myDeviceId + it.deviceInfo.deviceId == currentDeviceId } - val otherDevices = devices?.filter { it.deviceInfo.deviceId != state.myDeviceId } + val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId } renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount) renderCurrentDevice(currentDeviceInfo) @@ -165,7 +160,7 @@ class VectorSettingsDevicesFragment : hideOtherSessionsView() } - handleRequestStatus(state.request) + handleLoadingStatus(state.isLoading) } private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) { @@ -254,10 +249,7 @@ class VectorSettingsDevicesFragment : } } - private fun handleRequestStatus(unIgnoreRequest: Async) { - views.waitingView.root.isVisible = when (unIgnoreRequest) { - is Loading -> true - else -> false - } + private fun handleLoadingStatus(isLoading: Boolean) { + views.waitingView.root.isVisible = isLoading } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt index 6419d02fc9..468b19c45a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsController.kt @@ -24,7 +24,7 @@ import im.vector.app.core.epoxy.noResultItem import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider import im.vector.app.core.resources.StringProvider -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject @@ -60,7 +60,7 @@ class OtherSessionsController @Inject constructor( SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, formattedLastActivityDate ) - } else if (device.trustLevelForShield == RoomEncryptionTrustLevel.Trusted) { + } else if (device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) { stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate) } else { stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate) @@ -71,7 +71,7 @@ class OtherSessionsController @Inject constructor( otherSessionItem { id(device.deviceInfo.deviceId) deviceType(DeviceType.UNKNOWN) // TODO. We don't have this info yet. Update accordingly. - roomEncryptionTrustLevel(device.trustLevelForShield) + roomEncryptionTrustLevel(device.roomEncryptionTrustLevel) sessionName(device.deviceInfo.displayName) sessionDescription(description) sessionDescriptionDrawable(descriptionDrawable) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt index 682a9c6e64..b552664fe9 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionsView.kt @@ -24,7 +24,7 @@ import im.vector.app.R import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.configureWith import im.vector.app.databinding.ViewOtherSessionsBinding -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import javax.inject.Inject @AndroidEntryPoint diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt index 767f09482b..0cb621a502 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoView.kt @@ -57,7 +57,7 @@ class SessionInfoView @JvmOverloads constructor( ) { renderDeviceInfo(sessionInfoViewState.deviceFullInfo.deviceInfo.displayName.orEmpty()) renderVerificationStatus( - sessionInfoViewState.deviceFullInfo.trustLevelForShield, + sessionInfoViewState.deviceFullInfo.roomEncryptionTrustLevel, sessionInfoViewState.isCurrentSession, sessionInfoViewState.isLearnMoreLinkVisible, ) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt index 22ad710676..60e1234820 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SessionInfoViewState.kt @@ -16,7 +16,7 @@ package im.vector.app.features.settings.devices.v2.list -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo data class SessionInfoViewState( val isCurrentSession: Boolean, diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index c3579b68c3..a8a97ab326 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.asFlow import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase @@ -51,7 +51,7 @@ class GetDeviceFullInfoUseCase @Inject constructor( DeviceFullInfo( deviceInfo = info, cryptoDeviceInfo = cryptoInfo, - trustLevelForShield = roomEncryptionTrustLevel, + roomEncryptionTrustLevel = roomEncryptionTrustLevel, isInactive = isInactive ) } else { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt index a6bac6087b..c5cd80bd3c 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewFragment.kt @@ -34,7 +34,7 @@ import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.DrawableProvider import im.vector.app.databinding.FragmentSessionOverviewBinding -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt index c9f5635cbd..a447336c23 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewState.kt @@ -19,7 +19,7 @@ package im.vector.app.features.settings.devices.v2.overview import com.airbnb.mvrx.Async import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo data class SessionOverviewViewState( val deviceId: String, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCaseTest.kt index 7c8ee008eb..1a805f4c3e 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/GetCurrentSessionCrossSigningInfoUseCaseTest.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import im.vector.app.test.fakes.FakeActiveSessionHolder import io.mockk.every import io.mockk.mockk diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt index 8d54b31ab4..e55f0969f7 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt @@ -16,6 +16,7 @@ package im.vector.app.features.settings.devices +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import io.mockk.every import io.mockk.mockk import io.mockk.verify diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index e3d62961a7..70af681c6f 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow -import im.vector.app.features.settings.devices.CurrentSessionCrossSigningInfo +import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase From 39a0b3b1ba273cd9a507e5df922c773afb325c95 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 16:01:22 +0200 Subject: [PATCH 04/14] Moving recently created use cases inside v2 package --- .../app/features/settings/devices/DevicesViewModel.kt | 1 + .../settings/devices/v2/GetDeviceFullInfoListUseCase.kt | 1 - .../GetEncryptionTrustLevelForCurrentDeviceUseCase.kt | 2 +- .../{ => v2}/GetEncryptionTrustLevelForDeviceUseCase.kt | 2 +- .../GetEncryptionTrustLevelForOtherDeviceUseCase.kt | 2 +- .../devices/v2/overview/GetDeviceFullInfoUseCase.kt | 9 +++++---- ...GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt | 2 +- .../GetEncryptionTrustLevelForDeviceUseCaseTest.kt | 5 ++--- .../GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) rename vector/src/main/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt (96%) rename vector/src/main/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForDeviceUseCase.kt (97%) rename vector/src/main/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForOtherDeviceUseCase.kt (97%) rename vector/src/test/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt (97%) rename vector/src/test/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForDeviceUseCaseTest.kt (96%) rename vector/src/test/java/im/vector/app/features/settings/devices/{ => v2}/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt (98%) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt index 82c346b09c..30e7727860 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/DevicesViewModel.kt @@ -34,6 +34,7 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.core.utils.PublishDataSource import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.login.ReAuthHelper +import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.lib.core.utils.flow.throttleFirst import kotlinx.coroutines.Dispatchers diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt index fbe6609ef4..948d23de39 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -17,7 +17,6 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt similarity index 96% rename from vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt index 0d30aba318..7e56d35570 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCase.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt similarity index 97% rename from vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt index 433c4da233..7f330b71d5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCase.kt similarity index 97% rename from vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCase.kt rename to vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCase.kt index 11bc3a8ede..7541b9b1d5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCase.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index a8a97ab326..60c6266901 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -19,8 +19,8 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.asFlow import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.features.settings.devices.v2.DeviceFullInfo -import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase -import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase +import im.vector.app.features.settings.devices.v2.GetCurrentSessionCrossSigningInfoUseCase +import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -29,6 +29,7 @@ import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional import javax.inject.Inject +// TODO update unit tests class GetDeviceFullInfoUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, @@ -38,11 +39,11 @@ class GetDeviceFullInfoUseCase @Inject constructor( fun execute(deviceId: String): Flow> { return activeSessionHolder.getSafeActiveSession()?.let { session -> - val currentSessionCrossSigningInfo = getCurrentSessionCrossSigningInfoUseCase.execute() combine( + getCurrentSessionCrossSigningInfoUseCase.execute(), session.cryptoService().getMyDevicesInfoLive(deviceId).asFlow(), session.cryptoService().getLiveCryptoDeviceInfoWithId(deviceId).asFlow() - ) { deviceInfo, cryptoDeviceInfo -> + ) { currentSessionCrossSigningInfo, deviceInfo, cryptoDeviceInfo -> val info = deviceInfo.getOrNull() val cryptoInfo = cryptoDeviceInfo.getOrNull() val fullInfo = if (info != null && cryptoInfo != null) { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt similarity index 97% rename from vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt index 830eab5dcb..b2ce89df33 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForCurrentDeviceUseCaseTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 import org.amshove.kluent.shouldBeEqualTo import org.junit.Test diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCaseTest.kt similarity index 96% rename from vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCaseTest.kt index e55f0969f7..e43fd49ffc 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForDeviceUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCaseTest.kt @@ -14,9 +14,8 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 -import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import io.mockk.every import io.mockk.mockk import io.mockk.verify @@ -91,7 +90,7 @@ class GetEncryptionTrustLevelForDeviceUseCaseTest { } private fun givenCurrentSessionCrossSigningInfo( - deviceId: String?, + deviceId: String, isCrossSigningInitialized: Boolean, isCrossSigningVerified: Boolean ): CurrentSessionCrossSigningInfo { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt similarity index 98% rename from vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt rename to vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt index 9dc87c2a16..2aeffbbb0d 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForOtherDeviceUseCaseTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package im.vector.app.features.settings.devices +package im.vector.app.features.settings.devices.v2 import org.amshove.kluent.shouldBeEqualTo import org.junit.Test From 0eae1bd50505832ea00a16f8f13bfd9dfd634db5 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 16:44:10 +0200 Subject: [PATCH 05/14] Listen verification + refresh devices use cases --- .../settings/devices/v2/DevicesViewModel.kt | 67 ++++++++++++++++++- ...reshDevicesOnCryptoDevicesChangeUseCase.kt | 52 ++++++++++++++ .../devices/v2/RefreshDevicesUseCase.kt | 33 +++++++++ 3 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt create mode 100644 vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index 09de9ca8e7..20e3baa61d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -21,19 +21,30 @@ import com.airbnb.mvrx.Success import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.MavericksAssistedViewModelFactory import im.vector.app.core.di.hiltMavericksViewModelFactory import im.vector.app.core.platform.VectorViewModel +import im.vector.app.core.utils.PublishDataSource +import im.vector.lib.core.utils.flow.throttleFirst import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.crypto.verification.VerificationService +import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction +import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState +import kotlin.time.Duration.Companion.seconds // TODO add unit tests class DevicesViewModel @AssistedInject constructor( @Assisted initialState: DevicesViewState, + private val activeSessionHolder: ActiveSessionHolder, private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase, -) : VectorViewModel(initialState) { + private val refreshDevicesUseCase: RefreshDevicesUseCase, + private val refreshDevicesOnCryptoDevicesChangeUseCase: RefreshDevicesOnCryptoDevicesChangeUseCase, +) : VectorViewModel(initialState), VerificationService.Listener { @AssistedFactory interface Factory : MavericksAssistedViewModelFactory { @@ -42,9 +53,35 @@ class DevicesViewModel @AssistedInject constructor( companion object : MavericksViewModelFactory by hiltMavericksViewModelFactory() + private val refreshSource = PublishDataSource() + private val refreshThrottleDelayMs = 4.seconds.inWholeMilliseconds + init { + addVerificationListener() observeCurrentSessionCrossSigningInfo() observeDevices() + observeRefreshSource() + refreshDevicesOnCryptoDevicesChange() + queryRefreshDevicesList() + } + + override fun onCleared() { + removeVerificationListener() + super.onCleared() + } + + private fun addVerificationListener() { + activeSessionHolder.getSafeActiveSession() + ?.cryptoService() + ?.verificationService() + ?.addListener(this) + } + + private fun removeVerificationListener() { + activeSessionHolder.getSafeActiveSession() + ?.cryptoService() + ?.verificationService() + ?.removeListener(this) } private fun observeCurrentSessionCrossSigningInfo() { @@ -77,6 +114,34 @@ class DevicesViewModel @AssistedInject constructor( } } + private fun refreshDevicesOnCryptoDevicesChange() { + viewModelScope.launch { + refreshDevicesOnCryptoDevicesChangeUseCase.execute() + } + } + + private fun observeRefreshSource() { + refreshSource.stream() + .throttleFirst(refreshThrottleDelayMs) + .onEach { refreshDevicesUseCase.execute() } + .launchIn(viewModelScope) + } + + override fun transactionUpdated(tx: VerificationTransaction) { + if (tx.state == VerificationTxState.Verified) { + queryRefreshDevicesList() + } + } + + /** + * Force the refresh of the devices list. + * The devices list is the list of the devices where the user is logged in. + * It can be any mobile devices, and any browsers. + */ + private fun queryRefreshDevicesList() { + refreshSource.post(Unit) + } + override fun handle(action: DevicesAction) { when (action) { is DevicesAction.MarkAsManuallyVerified -> handleMarkAsManuallyVerifiedAction() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt new file mode 100644 index 0000000000..26f866aabe --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.di.ActiveSessionHolder +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.sample +import org.matrix.android.sdk.api.NoOpMatrixCallback +import org.matrix.android.sdk.flow.flow +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds + +// TODO add unit tests +class RefreshDevicesOnCryptoDevicesChangeUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + private val samplingPeriodMs = 5.seconds.inWholeMilliseconds + + suspend fun execute() { + activeSessionHolder.getSafeActiveSession() + ?.let { session -> + session.flow().liveUserCryptoDevices(session.myUserId) + .map { it.size } + .distinctUntilChanged() + .sample(samplingPeriodMs) + .onEach { + // If we have a new crypto device change, we might want to trigger refresh of device info + activeSessionHolder.getSafeActiveSession() + ?.cryptoService() + ?.fetchDevicesList(NoOpMatrixCallback()) + } + .collect() + } + } +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt new file mode 100644 index 0000000000..e8f3aa7455 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.core.di.ActiveSessionHolder +import org.matrix.android.sdk.api.NoOpMatrixCallback +import javax.inject.Inject + +// TODO add unit tests +class RefreshDevicesUseCase @Inject constructor( + private val activeSessionHolder: ActiveSessionHolder, +) { + fun execute() { + activeSessionHolder.getSafeActiveSession()?.let { session -> + session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) + session.cryptoService().downloadKeys(listOf(session.myUserId), true, NoOpMatrixCallback()) + } + } +} From 06d447247abc296d368983c627400c7e9d8ba09c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 17:26:43 +0200 Subject: [PATCH 06/14] Updating existing unit tests --- .../devices/v2/overview/GetDeviceFullInfoUseCase.kt | 1 - .../v2/overview/GetDeviceFullInfoUseCaseTest.kt | 11 ++++++----- .../v2/overview/SessionOverviewViewModelTest.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index 60c6266901..fff81b6dc5 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.api.util.toOptional import javax.inject.Inject -// TODO update unit tests class GetDeviceFullInfoUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt index 70af681c6f..7dc8e08a4e 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCaseTest.kt @@ -19,9 +19,9 @@ package im.vector.app.features.settings.devices.v2.overview import androidx.lifecycle.MutableLiveData import androidx.lifecycle.asFlow import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo -import im.vector.app.features.settings.devices.DeviceFullInfo -import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase -import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase +import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.GetCurrentSessionCrossSigningInfoUseCase +import im.vector.app.features.settings.devices.v2.GetEncryptionTrustLevelForDeviceUseCase import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase import im.vector.app.test.fakes.FakeActiveSessionHolder import im.vector.app.test.fakes.FakeFlowLiveDataConversions @@ -31,6 +31,7 @@ import io.mockk.mockk import io.mockk.unmockkAll import io.mockk.verify import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.amshove.kluent.shouldBeEqualTo import org.junit.After @@ -90,7 +91,7 @@ class GetDeviceFullInfoUseCaseTest { DeviceFullInfo( deviceInfo = deviceInfo, cryptoDeviceInfo = cryptoDeviceInfo, - trustLevelForShield = trustLevel, + roomEncryptionTrustLevel = trustLevel, isInactive = isInactive, ) ) @@ -134,7 +135,7 @@ class GetDeviceFullInfoUseCaseTest { isCrossSigningInitialized = true, isCrossSigningVerified = false ) - every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns currentSessionCrossSigningInfo + every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns flowOf(currentSessionCrossSigningInfo) return currentSessionCrossSigningInfo } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt index 735c553808..4a26fc4adc 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/overview/SessionOverviewViewModelTest.kt @@ -18,7 +18,7 @@ package im.vector.app.features.settings.devices.v2.overview import com.airbnb.mvrx.Success import com.airbnb.mvrx.test.MvRxTestRule -import im.vector.app.features.settings.devices.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.test.fakes.FakeSession import im.vector.app.test.test import io.mockk.every From 27031ab267eccccf1835ca016a436299c4d4aac1 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 5 Sep 2022 17:40:02 +0200 Subject: [PATCH 07/14] RefreshDevicesUseCase unit tests --- .../devices/v2/RefreshDevicesUseCase.kt | 1 - .../devices/v2/RefreshDevicesUseCaseTest.kt | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt index e8f3aa7455..a53ab1d2b3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCase.kt @@ -20,7 +20,6 @@ import im.vector.app.core.di.ActiveSessionHolder import org.matrix.android.sdk.api.NoOpMatrixCallback import javax.inject.Inject -// TODO add unit tests class RefreshDevicesUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, ) { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCaseTest.kt new file mode 100644 index 0000000000..4cd7afaf08 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesUseCaseTest.kt @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import io.mockk.every +import io.mockk.just +import io.mockk.runs +import io.mockk.verifyAll +import org.junit.Test +import org.matrix.android.sdk.api.NoOpMatrixCallback + +class RefreshDevicesUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + + private val refreshDevicesUseCase = RefreshDevicesUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance + ) + + @Test + fun `given current session when refreshing then devices list and keys are fetched`() { + val session = fakeActiveSessionHolder.fakeSession + every { session.cryptoService().fetchDevicesList(any()) } just runs + every { session.cryptoService().downloadKeys(any(), any(), any()) } just runs + + refreshDevicesUseCase.execute() + + verifyAll { + session.cryptoService().fetchDevicesList(match { it is NoOpMatrixCallback }) + session.cryptoService().downloadKeys(listOf(session.myUserId), true, match { it is NoOpMatrixCallback }) + } + } +} From 07d9e358424b8996899bad65208ef24d57a659a7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 13:58:21 +0200 Subject: [PATCH 08/14] GetCurrentSessionCrossSigningInfoUseCase unit tests --- ...etCurrentSessionCrossSigningInfoUseCase.kt | 1 - ...rrentSessionCrossSigningInfoUseCaseTest.kt | 131 ++++++++++++++++++ 2 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt index 9f7a3d8208..63e647e7c2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt @@ -26,7 +26,6 @@ import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.flow.flow import javax.inject.Inject -// TODO add unit tests class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, ) { diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt new file mode 100644 index 0000000000..178f388c7f --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.test +import im.vector.app.test.testDispatcher +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.auth.data.SessionParams +import org.matrix.android.sdk.api.session.Session +import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo +import org.matrix.android.sdk.api.util.toOptional +import org.matrix.android.sdk.flow.FlowSession +import org.matrix.android.sdk.flow.flow + +private const val A_DEVICE_ID = "device-id" + +class GetCurrentSessionCrossSigningInfoUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + + private val getCurrentSessionCrossSigningInfoUseCase = GetCurrentSessionCrossSigningInfoUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance + ) + + @Before + fun setUp() { + mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given the active session and existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { + val fakeSession = givenSession(A_DEVICE_ID) + val fakeFlowSession = givenFlowSession(fakeSession) + val isCrossSigningVerified = true + val mxCrossSigningInfo = givenMxCrossSigningInfo(isCrossSigningVerified) + every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) + val expectedResult = CurrentSessionCrossSigningInfo( + deviceId = A_DEVICE_ID, + isCrossSigningInitialized = true, + isCrossSigningVerified = isCrossSigningVerified + ) + + val result = getCurrentSessionCrossSigningInfoUseCase.execute() + .test(this) + + result.assertValues(listOf(expectedResult)) + .finish() + verify { fakeFlowSession.liveCrossSigningInfo(fakeSession.myUserId) } + } + + @Test + fun `given the active session and no existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { + val fakeSession = givenSession(A_DEVICE_ID) + val fakeFlowSession = givenFlowSession(fakeSession) + val mxCrossSigningInfo = null + every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) + val expectedResult = CurrentSessionCrossSigningInfo( + deviceId = A_DEVICE_ID, + isCrossSigningInitialized = false, + isCrossSigningVerified = false + ) + + val result = getCurrentSessionCrossSigningInfoUseCase.execute() + .test(this) + + result.assertValues(listOf(expectedResult)) + .finish() + verify { fakeFlowSession.liveCrossSigningInfo(fakeSession.myUserId) } + } + + @Test + fun `given no active session when getting cross signing info then the result is empty`() = runTest(testDispatcher) { + fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null) + + val result = getCurrentSessionCrossSigningInfoUseCase.execute() + .test(this) + + result.assertNoValues() + .finish() + } + + private fun givenSession(deviceId: String): Session { + val sessionParams = mockk() + every { sessionParams.deviceId } returns deviceId + + val fakeSession = fakeActiveSessionHolder.fakeSession + fakeSession.givenSessionParams(sessionParams) + + return fakeSession + } + + private fun givenFlowSession(session: Session): FlowSession { + val fakeFlowSession = mockk() + every { session.flow() } returns fakeFlowSession + return fakeFlowSession + } + + private fun givenMxCrossSigningInfo(isTrusted: Boolean) = mockk() + .also { + every { it.isTrusted() } returns isTrusted + } +} From 438f3027874ce2b5496b610c2687f7bd01e20513 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 14:47:18 +0200 Subject: [PATCH 09/14] GetDeviceFullInfoListUseCase unit tests --- .../v2/GetDeviceFullInfoListUseCase.kt | 5 +- ...rrentSessionCrossSigningInfoUseCaseTest.kt | 16 +- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 182 ++++++++++++++++++ .../im/vector/app/test/fakes/FakeSession.kt | 11 ++ 4 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt index 948d23de39..da2cf25f39 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCase.kt @@ -27,7 +27,6 @@ import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.flow.flow import javax.inject.Inject -// TODO add unit tests class GetDeviceFullInfoListUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, @@ -58,9 +57,9 @@ class GetDeviceFullInfoListUseCase @Inject constructor( .sortedByDescending { it.lastSeenTs } .map { deviceInfo -> val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } - val trustLevelForShield = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) + val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) - DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive) + DeviceFullInfo(deviceInfo, cryptoDeviceInfo, roomEncryptionTrustLevel, isInactive) } } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt index 178f388c7f..f8ee1231ae 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCaseTest.kt @@ -17,6 +17,7 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeSession import im.vector.app.test.test import im.vector.app.test.testDispatcher import io.mockk.every @@ -30,11 +31,8 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.matrix.android.sdk.api.auth.data.SessionParams -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.util.toOptional -import org.matrix.android.sdk.flow.FlowSession -import org.matrix.android.sdk.flow.flow private const val A_DEVICE_ID = "device-id" @@ -59,7 +57,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { @Test fun `given the active session and existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { val fakeSession = givenSession(A_DEVICE_ID) - val fakeFlowSession = givenFlowSession(fakeSession) + val fakeFlowSession = fakeSession.givenFlowSession() val isCrossSigningVerified = true val mxCrossSigningInfo = givenMxCrossSigningInfo(isCrossSigningVerified) every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) @@ -80,7 +78,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { @Test fun `given the active session and no existing cross signing info when getting these info then the result is correct`() = runTest(testDispatcher) { val fakeSession = givenSession(A_DEVICE_ID) - val fakeFlowSession = givenFlowSession(fakeSession) + val fakeFlowSession = fakeSession.givenFlowSession() val mxCrossSigningInfo = null every { fakeFlowSession.liveCrossSigningInfo(any()) } returns flowOf(mxCrossSigningInfo.toOptional()) val expectedResult = CurrentSessionCrossSigningInfo( @@ -108,7 +106,7 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { .finish() } - private fun givenSession(deviceId: String): Session { + private fun givenSession(deviceId: String): FakeSession { val sessionParams = mockk() every { sessionParams.deviceId } returns deviceId @@ -118,12 +116,6 @@ class GetCurrentSessionCrossSigningInfoUseCaseTest { return fakeSession } - private fun givenFlowSession(session: Session): FlowSession { - val fakeFlowSession = mockk() - every { session.flow() } returns fakeFlowSession - return fakeFlowSession - } - private fun givenMxCrossSigningInfo(isTrusted: Boolean) = mockk() .also { every { it.isTrusted() } returns isTrusted diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt new file mode 100644 index 0000000000..739d5c6668 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.features.settings.devices.v2.list.CheckIfSessionIsInactiveUseCase +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.test +import im.vector.app.test.testDispatcher +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +private const val A_DEVICE_ID_1 = "device-id-1" +private const val A_DEVICE_ID_2 = "device-id-2" +private const val A_DEVICE_ID_3 = "device-id-3" +private const val A_TIMESTAMP_1 = 100L +private const val A_TIMESTAMP_2 = 200L +private const val A_TIMESTAMP_3 = 300L + +class GetDeviceFullInfoListUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + private val checkIfSessionIsInactiveUseCase = mockk() + private val getEncryptionTrustLevelForDeviceUseCase = mockk() + private val getCurrentSessionCrossSigningInfoUseCase = mockk() + + private val getDeviceFullInfoListUseCase = GetDeviceFullInfoListUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance, + checkIfSessionIsInactiveUseCase = checkIfSessionIsInactiveUseCase, + getEncryptionTrustLevelForDeviceUseCase = getEncryptionTrustLevelForDeviceUseCase, + getCurrentSessionCrossSigningInfoUseCase = getCurrentSessionCrossSigningInfoUseCase, + ) + + @Before + fun setUp() { + mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given active session when getting list of device full info then the result list is correct and sorted in descending order`() = runTest(testDispatcher) { + // Given + val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo() + val fakeFlowSession = fakeActiveSessionHolder.fakeSession.givenFlowSession() + val cryptoDeviceInfo1 = givenACryptoDeviceInfo(A_DEVICE_ID_1) + val cryptoDeviceInfo2 = givenACryptoDeviceInfo(A_DEVICE_ID_2) + val cryptoDeviceInfo3 = givenACryptoDeviceInfo(A_DEVICE_ID_3) + val cryptoDeviceInfoList = listOf(cryptoDeviceInfo1, cryptoDeviceInfo2, cryptoDeviceInfo3) + every { fakeFlowSession.liveUserCryptoDevices(any()) } returns flowOf(cryptoDeviceInfoList) + val deviceInfo1 = givenADevicesInfo( + deviceId = A_DEVICE_ID_1, + lastSeenTs = A_TIMESTAMP_1, + isInactive = true, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + cryptoDeviceInfo = cryptoDeviceInfo1 + ) + val deviceInfo2 = givenADevicesInfo( + deviceId = A_DEVICE_ID_2, + lastSeenTs = A_TIMESTAMP_2, + isInactive = false, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + cryptoDeviceInfo = cryptoDeviceInfo2 + ) + val deviceInfo3 = givenADevicesInfo( + deviceId = A_DEVICE_ID_3, + lastSeenTs = A_TIMESTAMP_3, + isInactive = false, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, + cryptoDeviceInfo = cryptoDeviceInfo3 + ) + val deviceInfoList = listOf(deviceInfo1, deviceInfo2, deviceInfo3) + every { fakeFlowSession.liveMyDevicesInfo() } returns flowOf(deviceInfoList) + val expectedResult1 = DeviceFullInfo( + deviceInfo = deviceInfo1, + cryptoDeviceInfo = cryptoDeviceInfo1, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + isInactive = true + ) + val expectedResult2 = DeviceFullInfo( + deviceInfo = deviceInfo2, + cryptoDeviceInfo = cryptoDeviceInfo2, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + isInactive = false + ) + val expectedResult3 = DeviceFullInfo( + deviceInfo = deviceInfo3, + cryptoDeviceInfo = cryptoDeviceInfo3, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, + isInactive = false + ) + val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1) + + // When + val result = getDeviceFullInfoListUseCase.execute() + .test(this) + + // Then + result.assertValues(expectedResult) + .finish() + verify { + getCurrentSessionCrossSigningInfoUseCase.execute() + fakeFlowSession.liveUserCryptoDevices(fakeActiveSessionHolder.fakeSession.myUserId) + fakeFlowSession.liveMyDevicesInfo() + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo1) + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo2) + getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo3) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_1) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_2) + checkIfSessionIsInactiveUseCase.execute(A_TIMESTAMP_3) + } + } + + @Test + fun `given no active session when getting list then the result is empty`() = runTest(testDispatcher) { + // Given + fakeActiveSessionHolder.givenGetSafeActiveSessionReturns(null) + + // When + val result = getDeviceFullInfoListUseCase.execute() + .test(this) + + // Then + result.assertNoValues() + .finish() + } + + private fun givenCurrentSessionCrossSigningInfo(): CurrentSessionCrossSigningInfo { + val currentSessionCrossSigningInfo = mockk() + every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns flowOf(currentSessionCrossSigningInfo) + return currentSessionCrossSigningInfo + } + + private fun givenACryptoDeviceInfo(deviceId: String): CryptoDeviceInfo { + val cryptoDeviceInfo = mockk() + every { cryptoDeviceInfo.deviceId } returns deviceId + return cryptoDeviceInfo + } + + private fun givenADevicesInfo( + deviceId: String, + lastSeenTs: Long, + isInactive: Boolean, + roomEncryptionTrustLevel: RoomEncryptionTrustLevel, + cryptoDeviceInfo: CryptoDeviceInfo, + ): DeviceInfo { + val deviceInfo = mockk() + every { deviceInfo.deviceId } returns deviceId + every { deviceInfo.lastSeenTs } returns lastSeenTs + every { getEncryptionTrustLevelForDeviceUseCase.execute(any(), cryptoDeviceInfo) } returns roomEncryptionTrustLevel + every { checkIfSessionIsInactiveUseCase.execute(lastSeenTs) } returns isInactive + + return deviceInfo + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt index 71bcde5807..35d23e35e8 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeSession.kt @@ -32,6 +32,8 @@ import org.matrix.android.sdk.api.session.getRoomSummary import org.matrix.android.sdk.api.session.homeserver.HomeServerCapabilitiesService import org.matrix.android.sdk.api.session.profile.ProfileService import org.matrix.android.sdk.api.session.room.model.RoomSummary +import org.matrix.android.sdk.flow.FlowSession +import org.matrix.android.sdk.flow.flow class FakeSession( val fakeCryptoService: FakeCryptoService = FakeCryptoService(), @@ -76,6 +78,15 @@ class FakeSession( every { this@FakeSession.sessionParams } returns sessionParams } + /** + * Do not forget to call mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") in the setup method of the tests. + */ + fun givenFlowSession(): FlowSession { + val fakeFlowSession = mockk() + every { flow() } returns fakeFlowSession + return fakeFlowSession + } + companion object { fun withRoomSummary(roomSummary: RoomSummary) = FakeSession().apply { From 3566b10e3ca5a139c1b02e1045c3e109a1a4c587 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 16:35:23 +0200 Subject: [PATCH 10/14] DevicesViewModel unit tests --- .../settings/devices/v2/DevicesViewModel.kt | 3 +- .../devices/v2/DevicesViewModelTest.kt | 193 ++++++++++++++++++ .../app/test/fakes/FakeCryptoService.kt | 5 +- .../app/test/fakes/FakeVerificationService.kt | 22 ++ 4 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt create mode 100644 vector/src/test/java/im/vector/app/test/fakes/FakeVerificationService.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index 20e3baa61d..a50cef6665 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -36,7 +36,6 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransa import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import kotlin.time.Duration.Companion.seconds -// TODO add unit tests class DevicesViewModel @AssistedInject constructor( @Assisted initialState: DevicesViewState, private val activeSessionHolder: ActiveSessionHolder, @@ -99,7 +98,7 @@ class DevicesViewModel @AssistedInject constructor( .execute { async -> if (async is Success) { val deviceFullInfoList = async.invoke() - val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.trustLevel?.isVerified().orFalse() } + val unverifiedSessionsCount = deviceFullInfoList.count { !it.cryptoDeviceInfo?.isVerified.orFalse() } val inactiveSessionsCount = deviceFullInfoList.count { it.isInactive } copy( devices = async, diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt new file mode 100644 index 0000000000..72f8920bb0 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import com.airbnb.mvrx.Success +import com.airbnb.mvrx.test.MvRxTestRule +import im.vector.app.test.fakes.FakeActiveSessionHolder +import im.vector.app.test.fakes.FakeVerificationService +import im.vector.app.test.test +import im.vector.app.test.testDispatcher +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +import org.junit.Rule +import org.junit.Test +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel + +class DevicesViewModelTest { + + @get:Rule + val mvRxTestRule = MvRxTestRule(testDispatcher = testDispatcher) + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + private val getCurrentSessionCrossSigningInfoUseCase = mockk() + private val getDeviceFullInfoListUseCase = mockk() + private val refreshDevicesUseCase = mockk() + private val refreshDevicesOnCryptoDevicesChangeUseCase = mockk() + + private fun createViewModel(): DevicesViewModel { + return DevicesViewModel( + DevicesViewState(), + fakeActiveSessionHolder.instance, + getCurrentSessionCrossSigningInfoUseCase, + getDeviceFullInfoListUseCase, + refreshDevicesUseCase, + refreshDevicesOnCryptoDevicesChangeUseCase, + ) + } + + @Test + fun `given the viewModel when initializing it then verification listener is added`() { + // Given + val fakeVerificationService = givenVerificationService() + givenCurrentSessionCrossSigningInfo() + givenDeviceFullInfoList() + givenRefreshDevicesOnCryptoDevicesChange() + + // When + val viewModel = createViewModel() + + // Then + verify { + fakeVerificationService.addListener(viewModel) + } + } + + @Test + fun `given the viewModel when clearing it then verification listener is removed`() { + // Given + val fakeVerificationService = givenVerificationService() + givenCurrentSessionCrossSigningInfo() + givenDeviceFullInfoList() + givenRefreshDevicesOnCryptoDevicesChange() + + // When + val viewModel = createViewModel() + viewModel.onCleared() + + // Then + verify { + fakeVerificationService.removeListener(viewModel) + } + } + + @Test + fun `given the viewModel when initializing it then view state is updated with current session cross signing info`() { + // Given + givenVerificationService() + val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo() + givenDeviceFullInfoList() + givenRefreshDevicesOnCryptoDevicesChange() + + // When + val viewModelTest = createViewModel().test() + + // Then + viewModelTest.assertLatestState { it.currentSessionCrossSigningInfo == currentSessionCrossSigningInfo } + viewModelTest.finish() + } + + @Test + fun `given the viewModel when initializing it then view state is updated with current device full info list`() { + // Given + givenVerificationService() + givenCurrentSessionCrossSigningInfo() + val deviceFullInfoList = givenDeviceFullInfoList() + givenRefreshDevicesOnCryptoDevicesChange() + + // When + val viewModelTest = createViewModel().test() + + // Then + viewModelTest.assertLatestState { + it.devices is Success + && it.devices.invoke() == deviceFullInfoList + && it.inactiveSessionsCount == 1 + && it.unverifiedSessionsCount == 1 + } + viewModelTest.finish() + } + + @Test + fun `given the viewModel when initializing it then devices are refreshed on crypto devices change`() { + // Given + givenVerificationService() + givenCurrentSessionCrossSigningInfo() + givenDeviceFullInfoList() + givenRefreshDevicesOnCryptoDevicesChange() + + // When + createViewModel() + + // Then + coVerify { refreshDevicesOnCryptoDevicesChangeUseCase.execute() } + } + + private fun givenVerificationService(): FakeVerificationService { + val fakeVerificationService = fakeActiveSessionHolder + .fakeSession + .fakeCryptoService + .fakeVerificationService + every { fakeVerificationService.addListener(any()) } just runs + every { fakeVerificationService.removeListener(any()) } just runs + return fakeVerificationService + } + + private fun givenCurrentSessionCrossSigningInfo(): CurrentSessionCrossSigningInfo { + val currentSessionCrossSigningInfo = mockk() + every { getCurrentSessionCrossSigningInfoUseCase.execute() } returns flowOf(currentSessionCrossSigningInfo) + return currentSessionCrossSigningInfo + } + + /** + * Generate mocked deviceFullInfo list with 1 unverified and inactive + 1 verified and active. + */ + private fun givenDeviceFullInfoList(): List { + val verifiedCryptoDeviceInfo = mockk() + every { verifiedCryptoDeviceInfo.isVerified } returns true + val unverifiedCryptoDeviceInfo = mockk() + every { unverifiedCryptoDeviceInfo.isVerified } returns false + + val deviceFullInfo1 = DeviceFullInfo( + deviceInfo = mockk(), + cryptoDeviceInfo = verifiedCryptoDeviceInfo, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Trusted, + isInactive = false + ) + val deviceFullInfo2 = DeviceFullInfo( + deviceInfo = mockk(), + cryptoDeviceInfo = unverifiedCryptoDeviceInfo, + roomEncryptionTrustLevel = RoomEncryptionTrustLevel.Warning, + isInactive = true + ) + val deviceFullInfoList = listOf(deviceFullInfo1, deviceFullInfo2) + val deviceFullInfoListFlow = flowOf(deviceFullInfoList) + every { getDeviceFullInfoListUseCase.execute() } returns deviceFullInfoListFlow + return deviceFullInfoList + } + + private fun givenRefreshDevicesOnCryptoDevicesChange() { + coEvery { refreshDevicesOnCryptoDevicesChangeUseCase.execute() } just runs + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt index 197ccf4cd2..538ce671d2 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeCryptoService.kt @@ -24,7 +24,8 @@ import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.api.util.Optional class FakeCryptoService( - val fakeCrossSigningService: FakeCrossSigningService = FakeCrossSigningService() + val fakeCrossSigningService: FakeCrossSigningService = FakeCrossSigningService(), + val fakeVerificationService: FakeVerificationService = FakeVerificationService(), ) : CryptoService by mockk() { var roomKeysExport = ByteArray(size = 1) @@ -34,6 +35,8 @@ class FakeCryptoService( override fun crossSigningService() = fakeCrossSigningService + override fun verificationService() = fakeVerificationService + override suspend fun exportRoomKeys(password: String) = roomKeysExport override fun getLiveCryptoDeviceInfo() = MutableLiveData(cryptoDeviceInfos.values.toList()) diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeVerificationService.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeVerificationService.kt new file mode 100644 index 0000000000..984a48b2c1 --- /dev/null +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeVerificationService.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.test.fakes + +import io.mockk.mockk +import org.matrix.android.sdk.api.session.crypto.verification.VerificationService + +class FakeVerificationService : VerificationService by mockk() From 047384c08ccc8618d26aedfc1de54aa71336e264 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 16:43:38 +0200 Subject: [PATCH 11/14] Fix some coding style issues --- .../app/features/settings/devices/v2/DevicesViewModel.kt | 1 - .../devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt | 1 - .../devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt | 1 - .../features/settings/devices/v2/DevicesViewModelTest.kt | 6 ++---- .../settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt | 2 +- 5 files changed, 3 insertions(+), 8 deletions(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt index a50cef6665..e0b6368fc1 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DevicesViewModel.kt @@ -151,4 +151,3 @@ class DevicesViewModel @AssistedInject constructor( // TODO implement when needed } } - diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt index 63e647e7c2..f41b3d4cf8 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt @@ -17,7 +17,6 @@ package im.vector.app.features.settings.devices.v2 import im.vector.app.core.di.ActiveSessionHolder -import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt index 7f330b71d5..6f0dcbface 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetEncryptionTrustLevelForDeviceUseCase.kt @@ -16,7 +16,6 @@ package im.vector.app.features.settings.devices.v2 -import im.vector.app.features.settings.devices.v2.CurrentSessionCrossSigningInfo import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt index 72f8920bb0..cc5cdf6e39 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/DevicesViewModelTest.kt @@ -121,10 +121,8 @@ class DevicesViewModelTest { // Then viewModelTest.assertLatestState { - it.devices is Success - && it.devices.invoke() == deviceFullInfoList - && it.inactiveSessionsCount == 1 - && it.unverifiedSessionsCount == 1 + it.devices is Success && it.devices.invoke() == deviceFullInfoList && + it.inactiveSessionsCount == 1 && it.unverifiedSessionsCount == 1 } viewModelTest.finish() } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt index 739d5c6668..fa9f742976 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/GetDeviceFullInfoListUseCaseTest.kt @@ -66,7 +66,7 @@ class GetDeviceFullInfoListUseCaseTest { } @Test - fun `given active session when getting list of device full info then the result list is correct and sorted in descending order`() = runTest(testDispatcher) { + fun `given active session when getting list of device full info then the list is correct and sorted in descending order`() = runTest(testDispatcher) { // Given val currentSessionCrossSigningInfo = givenCurrentSessionCrossSigningInfo() val fakeFlowSession = fakeActiveSessionHolder.fakeSession.givenFlowSession() From f2891b74c552dcf34e14577e2c2341a9a9f9213a Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 16:47:39 +0200 Subject: [PATCH 12/14] Adding changelog entry --- changelog.d/7043.wip | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7043.wip diff --git a/changelog.d/7043.wip b/changelog.d/7043.wip new file mode 100644 index 0000000000..3c9b7731bf --- /dev/null +++ b/changelog.d/7043.wip @@ -0,0 +1 @@ +[Devices Management] Refactor some code to improve testability From c45f536855194f1eba6d415fd8663ac545cf4792 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Tue, 6 Sep 2022 17:30:36 +0200 Subject: [PATCH 13/14] RefreshDevicesOnCryptoDevicesChangeUseCase unit tests --- ...reshDevicesOnCryptoDevicesChangeUseCase.kt | 5 +- ...DevicesOnCryptoDevicesChangeUseCaseTest.kt | 77 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCaseTest.kt diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt index 26f866aabe..7d0a96eb0d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCase.kt @@ -27,7 +27,6 @@ import org.matrix.android.sdk.flow.flow import javax.inject.Inject import kotlin.time.Duration.Companion.seconds -// TODO add unit tests class RefreshDevicesOnCryptoDevicesChangeUseCase @Inject constructor( private val activeSessionHolder: ActiveSessionHolder, ) { @@ -42,9 +41,7 @@ class RefreshDevicesOnCryptoDevicesChangeUseCase @Inject constructor( .sample(samplingPeriodMs) .onEach { // If we have a new crypto device change, we might want to trigger refresh of device info - activeSessionHolder.getSafeActiveSession() - ?.cryptoService() - ?.fetchDevicesList(NoOpMatrixCallback()) + session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) } .collect() } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCaseTest.kt new file mode 100644 index 0000000000..97958d04ed --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/RefreshDevicesOnCryptoDevicesChangeUseCaseTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2022 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.settings.devices.v2 + +import im.vector.app.test.fakes.FakeActiveSessionHolder +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.runs +import io.mockk.unmockkAll +import io.mockk.verify +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo +import org.matrix.android.sdk.flow.FlowSession +import org.matrix.android.sdk.flow.flow + +class RefreshDevicesOnCryptoDevicesChangeUseCaseTest { + + private val fakeActiveSessionHolder = FakeActiveSessionHolder() + + private val refreshDevicesOnCryptoDevicesChangeUseCase = RefreshDevicesOnCryptoDevicesChangeUseCase( + activeSessionHolder = fakeActiveSessionHolder.instance + ) + + @Before + fun setUp() { + mockkStatic("org.matrix.android.sdk.flow.FlowSessionKt") + } + + @After + fun tearDown() { + unmockkAll() + } + + @Test + fun `given the current session when crypto devices list changes then the devices list is refreshed`() = runTest { + // Given + val device1 = givenACryptoDevice() + val devices = listOf(device1) + val fakeSession = fakeActiveSessionHolder.fakeSession + val flowSession = mockk() + every { fakeSession.flow() } returns flowSession + every { flowSession.liveUserCryptoDevices(any()) } returns flowOf(devices) + every { fakeSession.cryptoService().fetchDevicesList(any()) } just runs + + // When + refreshDevicesOnCryptoDevicesChangeUseCase.execute() + + // Then + verify { + flowSession.liveUserCryptoDevices(fakeSession.myUserId) + // FIXME the following verification does not work due to the usage of Flow.sample() inside the use case implementation + // fakeSession.cryptoService().fetchDevicesList(match { it is NoOpMatrixCallback }) + } + } + + private fun givenACryptoDevice(): CryptoDeviceInfo = mockk() +} From 45b138e53d77335b98a5f8238f145a4dbb9a0953 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Wed, 7 Sep 2022 17:09:57 +0200 Subject: [PATCH 14/14] Using orFalse() extension method on nullable boolean --- .../devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt index f41b3d4cf8..cc848342de 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/GetCurrentSessionCrossSigningInfoUseCase.kt @@ -20,6 +20,7 @@ import im.vector.app.core.di.ActiveSessionHolder import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.map +import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo import org.matrix.android.sdk.api.util.Optional import org.matrix.android.sdk.flow.flow @@ -41,7 +42,7 @@ class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor( return CurrentSessionCrossSigningInfo( deviceId = deviceId, isCrossSigningInitialized = mxCrossSigningInfo.getOrNull() != null, - isCrossSigningVerified = mxCrossSigningInfo.getOrNull()?.isTrusted() == true + isCrossSigningVerified = mxCrossSigningInfo.getOrNull()?.isTrusted().orFalse() ) } }