From 815fca38e3cb232650785ccf43f3695fa130c224 Mon Sep 17 00:00:00 2001 From: "sergei.bakhtiarov" Date: Wed, 4 Feb 2026 17:00:22 +0100 Subject: [PATCH 1/2] fix: e2ei identity certificate status (WPB-20235) --- .../wire/android/ui/authentication/devices/DeviceItem.kt | 4 ++-- .../android/ui/settings/devices/DeviceDetailsScreen.kt | 6 +++--- .../android/ui/settings/devices/DeviceDetailsViewModel.kt | 7 +++++++ .../ui/settings/devices/model/DeviceDetailsState.kt | 1 + 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt index 2a4f0de0186..a130461d8a3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/devices/DeviceItem.kt @@ -209,8 +209,8 @@ private fun ColumnScope.DeviceItemTexts( .then(semantic) ) if (shouldShowVerifyLabel) { - if (shouldShowE2EIInfo) { - MLSVerificationIcon(device.mlsClientIdentity?.e2eiStatus) + if (shouldShowE2EIInfo && device.mlsClientIdentity != null) { + MLSVerificationIcon(device.mlsClientIdentity.e2eiStatus) } if (device.isVerifiedProteus && !isCurrentClient) { ProteusVerifiedIcon( diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt index 0bbb477802e..bd52be7feb8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsScreen.kt @@ -216,7 +216,7 @@ fun DeviceDetailsContent( } } - if (state.isE2EIEnabled) { + if (state.isE2EIEnabled && state.isE2eiCertificateDataAvailable) { item { EndToEndIdentityCertificateItem( isE2eiCertificateActivated = state.isE2eiCertificateActivated, @@ -363,8 +363,8 @@ private fun DeviceDetailsTopBar( maxLines = 2 ) - if (shouldShowE2EIInfo) { - MLSVerificationIcon(device.mlsClientIdentity?.e2eiStatus) + if (shouldShowE2EIInfo && device.mlsClientIdentity != null) { + MLSVerificationIcon(device.mlsClientIdentity.e2eiStatus) } if (!isCurrentDevice && device.isVerifiedProteus) { diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt index e46729a53e4..90194d66399 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt @@ -142,6 +142,13 @@ class DeviceDetailsViewModel @Inject constructor( private fun getE2eiCertificate() { viewModelScope.launch { state = when (val result = mlsClientIdentity(deviceId)) { + is GetMLSClientIdentityResult.Failure.E2EINotAvailable -> { + state.copy( + isE2eiCertificateActivated = false, + isLoadingCertificate = false, + isE2eiCertificateDataAvailable = false + ) + } is GetMLSClientIdentityResult.Failure -> { state.copy(isE2eiCertificateActivated = false, isLoadingCertificate = false) } diff --git a/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt b/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt index 0933e19cb7c..b059dd65def 100644 --- a/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/settings/devices/model/DeviceDetailsState.kt @@ -40,4 +40,5 @@ data class DeviceDetailsState( val startGettingE2EICertificate: Boolean = false, val mlsCipherSuiteSignature: String? = null, val deviceRemoved: Boolean = false, + val isE2eiCertificateDataAvailable: Boolean = true, ) From ee6cfc7336448eb4363f9232c4b167d524538f27 Mon Sep 17 00:00:00 2001 From: "sergei.bakhtiarov" Date: Mon, 9 Feb 2026 17:19:33 +0100 Subject: [PATCH 2/2] fixing build issue --- .../NewConversationViewModel.kt | 18 +++++++++--------- .../MessageComposerViewModelArrangement.kt | 2 +- .../EditGuestAccessViewModelTest.kt | 4 ++-- .../IsFileSharingEnabledViewModelTest.kt | 4 ++-- .../NewConversationViewModelArrangement.kt | 5 ++--- .../FeatureFlagNotificationViewModelTest.kt | 3 +-- .../debug/DebugDataOptionsViewModelTest.kt | 6 +++--- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModel.kt b/app/src/main/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModel.kt index 02d4c9203b6..5f8ef8557b8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModel.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModel.kt @@ -71,14 +71,7 @@ class NewConversationViewModel @Inject constructor( ) : ViewModel() { var newGroupNameTextState: TextFieldState = TextFieldState() - var newGroupState: GroupMetadataState by mutableStateOf( - GroupMetadataState().let { - val defaultProtocol = CreateConversationParam - .Protocol - .fromSupportedProtocolToConversationOptionsProtocol(getDefaultProtocol()) - it.copy(groupProtocol = defaultProtocol) - } - ) + var newGroupState: GroupMetadataState by mutableStateOf(GroupMetadataState()) var groupOptionsState: GroupOptionState by mutableStateOf(GroupOptionState()) var isChannelCreationPossible: Boolean by mutableStateOf(true) @@ -86,12 +79,19 @@ class NewConversationViewModel @Inject constructor( var createGroupState: CreateGroupState by mutableStateOf(CreateGroupState.Default) init { + initNewGroupState() observeAllowanceOfAppsUsageInitialState() setConversationCreationParam() observeChannelCreationPermission() getWireCellFeatureState() } + private fun initNewGroupState() = viewModelScope.launch { + val defaultProtocol = CreateConversationParam + .Protocol.fromSupportedProtocolToConversationOptionsProtocol(getDefaultProtocol()) + newGroupState = newGroupState.copy(groupProtocol = defaultProtocol) + } + private fun observeAllowanceOfAppsUsageInitialState() { viewModelScope.launch { observeIsAppsAllowedForUsage() @@ -118,7 +118,7 @@ class NewConversationViewModel @Inject constructor( appsAllowed } - fun resetState() { + fun resetState() = viewModelScope.launch { newGroupNameTextState.clearText() newGroupState = GroupMetadataState().let { val defaultProtocol = CreateConversationParam diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt index 0a40f151b3f..66826990e96 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/composer/MessageComposerViewModelArrangement.kt @@ -89,7 +89,7 @@ internal class MessageComposerViewModelArrangement { every { savedStateHandle.navArgs() } returns ConversationNavArgs(conversationId = conversationId) // Default empty values - every { isFileSharingEnabledUseCase() } returns FileSharingStatus(FileSharingStatus.Value.EnabledAll, null) + coEvery { isFileSharingEnabledUseCase() } returns FileSharingStatus(FileSharingStatus.Value.EnabledAll, null) coEvery { observeOngoingCallsUseCase() } returns flowOf(listOf()) coEvery { observeEstablishedCallsUseCase() } returns flowOf(listOf()) coEvery { observeSyncState() } returns flowOf(SyncState.Live) diff --git a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt index 5361ff14c17..7cbd1947da5 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/conversations/details/editguestaccess/EditGuestAccessViewModelTest.kt @@ -326,7 +326,7 @@ class EditGuestAccessViewModelTest { coEvery { observeGuestRoomLinkFeatureFlag() } returns flowOf() coEvery { canCreatePasswordProtectedLinks() } returns true coEvery { observeSelfUserUseCase() } returns flowOf(TestUser.SELF_USER) - every { getDefaultProtocolUseCase() } returns SupportedProtocol.PROTEUS + coEvery { getDefaultProtocolUseCase() } returns SupportedProtocol.PROTEUS } fun withSyncConversationCodeSuccess() = apply { @@ -355,7 +355,7 @@ class EditGuestAccessViewModelTest { } fun withDefaultProtocol(protocol: SupportedProtocol) = apply { - every { getDefaultProtocolUseCase() } returns protocol + coEvery { getDefaultProtocolUseCase() } returns protocol } fun arrange() = this to editGuestAccessViewModel diff --git a/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/attachments/IsFileSharingEnabledViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/attachments/IsFileSharingEnabledViewModelTest.kt index eb21f7612f5..da1bd72be57 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/attachments/IsFileSharingEnabledViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/messagecomposer/attachments/IsFileSharingEnabledViewModelTest.kt @@ -21,8 +21,8 @@ import com.wire.android.config.CoroutineTestExtension import com.wire.kalium.logic.configuration.FileSharingStatus import com.wire.kalium.logic.feature.user.IsFileSharingEnabledUseCase import io.mockk.MockKAnnotations +import io.mockk.coEvery import io.mockk.coVerify -import io.mockk.every import io.mockk.impl.annotations.MockK import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue @@ -80,7 +80,7 @@ class IsFileSharingEnabledViewModelTest { } fun withFileSharingStatus(result: FileSharingStatus.Value) = apply { - every { isFileSharingEnabledUseCase() } returns FileSharingStatus( + coEvery { isFileSharingEnabledUseCase() } returns FileSharingStatus( result, true ) diff --git a/app/src/test/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModelArrangement.kt b/app/src/test/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModelArrangement.kt index 5b5dbe386cb..0a2946b720e 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModelArrangement.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/newconversation/NewConversationViewModelArrangement.kt @@ -46,7 +46,6 @@ import com.wire.kalium.logic.feature.user.GetSelfUserUseCase import com.wire.kalium.logic.feature.user.IsMLSEnabledUseCase import io.mockk.MockKAnnotations import io.mockk.coEvery -import io.mockk.every import io.mockk.impl.annotations.MockK import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -61,7 +60,7 @@ internal class NewConversationViewModelArrangement { coEvery { isMLSEnabledUseCase() } returns true coEvery { createRegularGroup(any(), any(), any()) } returns ConversationCreationResult.Success(CONVERSATION) coEvery { observeChannelsCreationPermissionUseCase() } returns flowOf(ChannelCreationPermission.Forbidden) - every { getDefaultProtocol() } returns SupportedProtocol.PROTEUS + coEvery { getDefaultProtocol() } returns SupportedProtocol.PROTEUS coEvery { isWireCellsEnabled() } returns false withAppsAllowedResult(false) } @@ -209,7 +208,7 @@ internal class NewConversationViewModelArrangement { } fun withDefaultProtocol(supportedProtocol: SupportedProtocol) = apply { - every { getDefaultProtocol() } returns supportedProtocol + coEvery { getDefaultProtocol() } returns supportedProtocol } fun withAppsAllowedResult(result: Boolean) = apply { diff --git a/app/src/test/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModelTest.kt index e02ba58e036..c4cb6f65492 100644 --- a/app/src/test/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModelTest.kt @@ -43,7 +43,6 @@ import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every import io.mockk.impl.annotations.MockK -import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -98,7 +97,7 @@ class FeatureFlagNotificationViewModelTest { viewModel.dismissGuestRoomLinkDialog() advanceUntilIdle() - verify(exactly = 1) { arrangement.markGuestLinkFeatureFlagAsNotChanged() } + coVerify(exactly = 1) { arrangement.markGuestLinkFeatureFlagAsNotChanged() } assertEquals( false, viewModel.featureFlagState.shouldShowGuestRoomLinkDialog diff --git a/app/src/test/kotlin/com/wire/android/ui/settings/debug/DebugDataOptionsViewModelTest.kt b/app/src/test/kotlin/com/wire/android/ui/settings/debug/DebugDataOptionsViewModelTest.kt index ec51305b186..ad533d966a3 100644 --- a/app/src/test/kotlin/com/wire/android/ui/settings/debug/DebugDataOptionsViewModelTest.kt +++ b/app/src/test/kotlin/com/wire/android/ui/settings/debug/DebugDataOptionsViewModelTest.kt @@ -333,7 +333,7 @@ internal class DebugDataOptionsHiltArrangement { ) ) ) - every { + coEvery { getDefaultProtocolUseCase() } returns SupportedProtocol.PROTEUS @@ -378,13 +378,13 @@ internal class DebugDataOptionsHiltArrangement { } fun withProteusProtocolSetup() = apply { - every { + coEvery { getDefaultProtocolUseCase() } returns SupportedProtocol.PROTEUS } fun withMlsProtocolSetup() = apply { - every { + coEvery { getDefaultProtocolUseCase() } returns SupportedProtocol.MLS }