diff --git a/changelog.d/7327.wip b/changelog.d/7327.wip new file mode 100644 index 00000000000..8f0191f948f --- /dev/null +++ b/changelog.d/7327.wip @@ -0,0 +1 @@ +[Device management] Update the unknown verification status icon diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 77f16efff80..20d00563dce 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3254,10 +3254,12 @@ Unknown device type Verified session Unverified session + Unknown verification status Your current session is ready for secure messaging. This session is ready for secure messaging. Verify your current session for enhanced secure messaging. Verify or sign out from this session for best security and reliability. + Verify your current session to reveal this session\'s verification status. Verify Session View Details View All (%1$d) diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml index 85646adb425..9d8645a7076 100644 --- a/library/ui-styles/src/main/res/values/colors.xml +++ b/library/ui-styles/src/main/res/values/colors.xml @@ -146,6 +146,7 @@ #91A1C0 #FF4B55 #0FFF4B55 + @color/palette_gray_200 diff --git a/vector/build.gradle b/vector/build.gradle index c59e1a30281..048bb885bcf 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -53,6 +53,8 @@ android { // "pm clear" command after each test invocation. This command ensures // that the app's state is completely cleared between tests. testInstrumentationRunnerArguments clearPackageData: 'true' + + vectorDrawables.useSupportLibrary = true } testOptions { diff --git a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt index 19908596682..6327daec86d 100644 --- a/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt +++ b/vector/src/main/java/im/vector/app/core/ui/views/ShieldImageView.kt @@ -45,8 +45,8 @@ class ShieldImageView @JvmOverloads constructor( RoomEncryptionTrustLevel.Default -> { contentDescription = context.getString(R.string.a11y_trust_level_default) setImageResource( - if (borderLess) R.drawable.ic_shield_black_no_border - else R.drawable.ic_shield_black + if (borderLess) R.drawable.ic_shield_unknown_no_border + else R.drawable.ic_shield_unknown ) } RoomEncryptionTrustLevel.Warning -> { @@ -137,7 +137,7 @@ class ShieldImageView @JvmOverloads constructor( @DrawableRes fun RoomEncryptionTrustLevel.toDrawableRes(): Int { return when (this) { - RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_black + RoomEncryptionTrustLevel.Default -> R.drawable.ic_shield_unknown RoomEncryptionTrustLevel.Warning -> R.drawable.ic_shield_warning RoomEncryptionTrustLevel.Trusted -> R.drawable.ic_shield_trusted RoomEncryptionTrustLevel.E2EWithUnsupportedAlgorithm -> R.drawable.ic_warning_badge 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 42e4cebe4cf..6adb33d5abe 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 @@ -57,7 +57,7 @@ class GetDeviceFullInfoListUseCase @Inject constructor( } else { emptyList() } - filterDevicesUseCase.execute(deviceFullInfoList, filterType, excludedDeviceIds) + filterDevicesUseCase.execute(currentSessionCrossSigningInfo, deviceFullInfoList, filterType, excludedDeviceIds) } deviceFullInfoFlow.distinctUntilChanged() 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 47ea96c09d2..bd68cbc0ceb 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 @@ -44,6 +44,7 @@ import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INAC import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState +import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import javax.inject.Inject /** @@ -164,12 +165,11 @@ class VectorSettingsDevicesFragment : if (state.devices is Success) { val devices = state.devices() val currentDeviceId = state.currentSessionCrossSigningInfo.deviceId - val currentDeviceInfo = devices?.firstOrNull { - it.deviceInfo.deviceId == currentDeviceId - } + val currentDeviceInfo = devices?.firstOrNull { it.deviceInfo.deviceId == currentDeviceId } + val isCurrentSessionVerified = currentDeviceInfo?.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted val otherDevices = devices?.filter { it.deviceInfo.deviceId != currentDeviceId } - renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount) + renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount, isCurrentSessionVerified) renderCurrentDevice(currentDeviceInfo) renderOtherSessionsView(otherDevices) } else { @@ -181,14 +181,21 @@ class VectorSettingsDevicesFragment : handleLoadingStatus(state.isLoading) } - private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) { - if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) { + private fun renderSecurityRecommendations( + inactiveSessionsCount: Int, + unverifiedSessionsCount: Int, + isCurrentSessionVerified: Boolean, + ) { + val isUnverifiedSectionVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified + val isInactiveSectionVisible = inactiveSessionsCount > 0 + if (isUnverifiedSectionVisible.not() && isInactiveSectionVisible.not()) { hideSecurityRecommendations() } else { views.deviceListHeaderSectionSecurityRecommendations.isVisible = true views.deviceListSecurityRecommendationsDivider.isVisible = true - views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0 - views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0 + + views.deviceListUnverifiedSessionsRecommendation.isVisible = isUnverifiedSectionVisible + views.deviceListInactiveSessionsRecommendation.isVisible = isInactiveSectionVisible val unverifiedSessionsViewState = SecurityRecommendationViewState( description = getString(R.string.device_manager_unverified_sessions_description), sessionsCount = unverifiedSessionsCount, @@ -206,11 +213,19 @@ class VectorSettingsDevicesFragment : } } - private fun hideSecurityRecommendations() { - views.deviceListHeaderSectionSecurityRecommendations.isVisible = false + private fun hideUnverifiedSessionsRecommendation() { views.deviceListUnverifiedSessionsRecommendation.isVisible = false + } + + private fun hideInactiveSessionsRecommendation() { views.deviceListInactiveSessionsRecommendation.isVisible = false + } + + private fun hideSecurityRecommendations() { + views.deviceListHeaderSectionSecurityRecommendations.isVisible = false views.deviceListSecurityRecommendationsDivider.isVisible = false + hideUnverifiedSessionsRecommendation() + hideInactiveSessionsRecommendation() } private fun renderOtherSessionsView(otherDevices: List?) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt index a23a7a71088..8f23fd06cc2 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCase.kt @@ -17,22 +17,27 @@ package im.vector.app.features.settings.devices.v2.filter import im.vector.app.features.settings.devices.v2.DeviceFullInfo +import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import org.matrix.android.sdk.api.extensions.orFalse import javax.inject.Inject class FilterDevicesUseCase @Inject constructor() { fun execute( + currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, devices: List, filterType: DeviceManagerFilterType, excludedDeviceIds: List = emptyList(), ): List { + val isCurrentSessionVerified = currentSessionCrossSigningInfo.isCrossSigningVerified.orFalse() return devices .filter { when (filterType) { DeviceManagerFilterType.ALL_SESSIONS -> true - DeviceManagerFilterType.VERIFIED -> it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() - DeviceManagerFilterType.UNVERIFIED -> !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() + // when current session is not verified, other session status cannot be trusted + DeviceManagerFilterType.VERIFIED -> isCurrentSessionVerified && it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() + // when current session is not verified, other session status cannot be trusted + DeviceManagerFilterType.UNVERIFIED -> isCurrentSessionVerified && !it.cryptoDeviceInfo?.trustLevel?.isCrossSigningVerified().orFalse() DeviceManagerFilterType.INACTIVE -> it.isInactive } } 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 b0ba8baa1af..59e7e1888e3 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 @@ -53,7 +53,7 @@ class OtherSessionsController @Inject constructor( data.forEach { device -> val dateFormatKind = if (device.isInactive) DateFormatKind.TIMELINE_DAY_DIVIDER else DateFormatKind.DEFAULT_DATE_AND_TIME val formattedLastActivityDate = host.dateFormatter.format(device.deviceInfo.lastSeenTs, dateFormatKind) - val description = calculateDescription(device, formattedLastActivityDate) + val description = buildDescription(device, formattedLastActivityDate) val descriptionColor = if (device.isCurrentDevice) { host.colorProvider.getColorFromAttribute(R.attr.colorError) } else { @@ -77,7 +77,7 @@ class OtherSessionsController @Inject constructor( } } - private fun calculateDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String { + private fun buildDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String { return when { device.isInactive -> { stringProvider.getQuantityString( @@ -93,6 +93,9 @@ class OtherSessionsController @Inject constructor( device.isCurrentDevice -> { stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session) } + device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Default -> { + stringProvider.getString(R.string.device_manager_session_last_activity, formattedLastActivityDate) + } else -> { stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate) } 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 6f6c5b24e20..3d9c3a8f372 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 @@ -90,10 +90,10 @@ class SessionInfoView @JvmOverloads constructor( isVerifyButtonVisible: Boolean, ) { views.sessionInfoVerificationStatusImageView.render(encryptionTrustLevel) - if (encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) { - renderCrossSigningVerified(isCurrentSession) - } else { - renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible) + when { + encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession) + encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown() + else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible) } if (hasLearnMoreLink) { appendLearnMoreToVerificationStatus() @@ -142,6 +142,12 @@ class SessionInfoView @JvmOverloads constructor( views.sessionInfoVerifySessionButton.isVisible = isVerifyButtonVisible } + private fun renderCrossSigningUnknown() { + views.sessionInfoVerificationStatusTextView.text = context.getString(R.string.device_manager_verification_status_unknown) + views.sessionInfoVerificationStatusDetailTextView.text = context.getString(R.string.device_manager_verification_status_detail_other_session_unknown) + views.sessionInfoVerifySessionButton.isVisible = false + } + private fun renderDeviceInfo(sessionName: String, deviceType: DeviceType, stringProvider: StringProvider) { setDeviceTypeIconUseCase.execute(deviceType, views.sessionInfoDeviceTypeImageView, stringProvider) views.sessionInfoNameTextView.text = sessionName @@ -155,34 +161,31 @@ class SessionInfoView @JvmOverloads constructor( drawableProvider: DrawableProvider, colorProvider: ColorProvider, ) { - deviceInfo.lastSeenTs - ?.takeIf { isLastSeenDetailsVisible } - ?.let { timestamp -> - views.sessionInfoLastActivityTextView.isVisible = true - views.sessionInfoLastActivityTextView.text = if (isInactive) { - val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER) - context.resources.getQuantityString( - R.plurals.device_manager_other_sessions_description_inactive, - SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, - SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, - formattedTs - ) - } else { - val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME) - context.getString(R.string.device_manager_session_last_activity, formattedTs) - } - val drawable = if (isInactive) { - val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) - drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) - } else { - null - } - views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) - } - ?: run { - views.sessionInfoLastActivityTextView.isGone = true - } - + if (deviceInfo.lastSeenTs != null && isLastSeenDetailsVisible) { + val timestamp = deviceInfo.lastSeenTs + views.sessionInfoLastActivityTextView.isVisible = true + views.sessionInfoLastActivityTextView.text = if (isInactive) { + val formattedTs = dateFormatter.format(timestamp, DateFormatKind.TIMELINE_DAY_DIVIDER) + context.resources.getQuantityString( + R.plurals.device_manager_other_sessions_description_inactive, + SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, + SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, + formattedTs + ) + } else { + val formattedTs = dateFormatter.format(timestamp, DateFormatKind.DEFAULT_DATE_AND_TIME) + context.getString(R.string.device_manager_session_last_activity, formattedTs) + } + val drawable = if (isInactive) { + val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) + drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) + } else { + null + } + views.sessionInfoLastActivityTextView.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) + } else { + views.sessionInfoLastActivityTextView.isGone = true + } views.sessionInfoLastIPAddressTextView.setTextOrHide(deviceInfo.lastSeenIp?.takeIf { isLastSeenDetailsVisible }) } 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 7c133bc2296..463d5bb4952 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 @@ -24,7 +24,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import androidx.core.view.isGone import androidx.core.view.isVisible import com.airbnb.mvrx.Success import com.airbnb.mvrx.fragmentViewModel @@ -42,7 +41,6 @@ import im.vector.app.core.resources.StringProvider import im.vector.app.databinding.FragmentSessionOverviewBinding import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.crypto.recover.SetupMode -import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet import im.vector.app.features.workers.signout.SignOutUiWorker @@ -181,11 +179,6 @@ class SessionOverviewFragment : updateSessionInfo(state) updateLoading(state.isLoading) updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty()) - if (state.deviceInfo is Success) { - renderSessionInfo(state.isCurrentSessionTrusted, state.deviceInfo.invoke()) - } else { - hideSessionInfo() - } } private fun updateToolbar(viewState: SessionOverviewViewState) { @@ -214,7 +207,7 @@ class SessionOverviewFragment : deviceFullInfo = deviceInfo, isVerifyButtonVisible = isCurrentSession || viewState.isCurrentSessionTrusted, isDetailsButtonVisible = false, - isLearnMoreLinkVisible = true, + isLearnMoreLinkVisible = deviceInfo.roomEncryptionTrustLevel != RoomEncryptionTrustLevel.Default, isLastSeenDetailsVisible = !isCurrentSession, ) views.sessionOverviewInfo.render(infoViewState, dateFormatter, drawableProvider, colorProvider, stringProvider) @@ -243,18 +236,6 @@ class SessionOverviewFragment : } } - private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) { - views.sessionOverviewInfo.isVisible = true - val viewState = SessionInfoViewState( - isCurrentSession = isCurrentSession, - deviceFullInfo = deviceFullInfo, - isDetailsButtonVisible = false, - isLearnMoreLinkVisible = true, - isLastSeenDetailsVisible = true, - ) - views.sessionOverviewInfo.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider) - } - private fun updateLoading(isLoading: Boolean) { if (isLoading) { showLoading(null) @@ -313,8 +294,4 @@ class SessionOverviewFragment : ) SessionLearnMoreBottomSheet.show(childFragmentManager, args) } - - private fun hideSessionInfo() { - views.sessionOverviewInfo.isGone = true - } } diff --git a/vector/src/main/res/drawable/ic_shield_unknown.xml b/vector/src/main/res/drawable/ic_shield_unknown.xml new file mode 100644 index 00000000000..de56aa475e7 --- /dev/null +++ b/vector/src/main/res/drawable/ic_shield_unknown.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml new file mode 100644 index 00000000000..bdc35ed8916 --- /dev/null +++ b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml @@ -0,0 +1,18 @@ + + + + + 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 ebdb74b74d3..17ad58668ae 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 @@ -144,10 +144,11 @@ class GetDeviceFullInfoListUseCaseTest { matrixClientInfo = matrixClientInfo3, ) val expectedResult = listOf(expectedResult3, expectedResult2, expectedResult1) - every { filterDevicesUseCase.execute(any(), any()) } returns expectedResult + every { filterDevicesUseCase.execute(any(), any(), any()) } returns expectedResult + val filterType = DeviceManagerFilterType.ALL_SESSIONS // When - val result = getDeviceFullInfoListUseCase.execute(DeviceManagerFilterType.ALL_SESSIONS, excludeCurrentDevice = false) + val result = getDeviceFullInfoListUseCase.execute(filterType, excludeCurrentDevice = false) .test(this) // Then @@ -166,6 +167,7 @@ class GetDeviceFullInfoListUseCaseTest { getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_1) getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_2) getMatrixClientInfoUseCase.execute(fakeActiveSessionHolder.fakeSession, A_DEVICE_ID_3) + filterDevicesUseCase.execute(currentSessionCrossSigningInfo, expectedResult, filterType, emptyList()) } } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt index 3a418cf2c07..79dff5bc169 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/filter/FilterDevicesUseCaseTest.kt @@ -20,6 +20,7 @@ import im.vector.app.core.session.clientinfo.MatrixClientInfoContent import im.vector.app.features.settings.devices.v2.DeviceFullInfo import im.vector.app.features.settings.devices.v2.details.extended.DeviceExtendedInfo import im.vector.app.features.settings.devices.v2.list.DeviceType +import im.vector.app.features.settings.devices.v2.verification.CurrentSessionCrossSigningInfo import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldContainAll import org.junit.Test @@ -94,32 +95,58 @@ class FilterDevicesUseCaseTest { @Test fun `given a device list when filter type is ALL_SESSIONS then returns the same list`() { - val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList()) + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.ALL_SESSIONS, emptyList()) filteredDeviceList.size shouldBeEqualTo devices.size } @Test - fun `given a device list when filter type is VERIFIED then returns only verified devices`() { - val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.VERIFIED, emptyList()) + fun `given a device list and current session is verified when filter type is VERIFIED then returns only verified devices`() { + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList()) filteredDeviceList.size shouldBeEqualTo 2 filteredDeviceList shouldContainAll listOf(activeVerifiedDevice, inactiveVerifiedDevice) } @Test - fun `given a device list when filter type is UNVERIFIED then returns only unverified devices`() { - val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.UNVERIFIED, emptyList()) + fun `given a device list and current session is unverified when filter type is VERIFIED then returns empty list of devices`() { + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.VERIFIED, emptyList()) + + filteredDeviceList.size shouldBeEqualTo 0 + } + + @Test + fun `given a device list and current session is verified when filter type is UNVERIFIED then returns only unverified devices`() { + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList()) filteredDeviceList.size shouldBeEqualTo 2 filteredDeviceList shouldContainAll listOf(activeUnverifiedDevice, inactiveUnverifiedDevice) } + @Test + fun `given a device list and current session is unverified when filter type is UNVERIFIED then returns empty list of devices`() { + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(false) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.UNVERIFIED, emptyList()) + + filteredDeviceList.size shouldBeEqualTo 0 + } + @Test fun `given a device list when filter type is INACTIVE then returns only inactive devices`() { - val filteredDeviceList = filterDevicesUseCase.execute(devices, DeviceManagerFilterType.INACTIVE, emptyList()) + val currentSessionCrossSigningInfo = givenCurrentSessionVerified(true) + val filteredDeviceList = filterDevicesUseCase.execute(currentSessionCrossSigningInfo, devices, DeviceManagerFilterType.INACTIVE, emptyList()) filteredDeviceList.size shouldBeEqualTo 2 filteredDeviceList shouldContainAll listOf(inactiveVerifiedDevice, inactiveUnverifiedDevice) } + + private fun givenCurrentSessionVerified(isVerified: Boolean): CurrentSessionCrossSigningInfo = CurrentSessionCrossSigningInfo( + isCrossSigningVerified = isVerified, + isCrossSigningInitialized = true, + deviceId = "" + ) }