diff --git a/changelog.d/7327.wip b/changelog.d/7327.wip new file mode 100644 index 0000000000..8f0191f948 --- /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 77f16efff8..20d00563dc 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 85646adb42..9d8645a707 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 c59e1a3028..048bb885bc 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 1990859668..6327daec86 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 42e4cebe4c..6adb33d5ab 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 47ea96c09d..bd68cbc0ce 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 hideUnverifiedSessionsRecommendation() { + views.deviceListUnverifiedSessionsRecommendation.isVisible = false + } + + private fun hideInactiveSessionsRecommendation() { + views.deviceListInactiveSessionsRecommendation.isVisible = false + } + private fun hideSecurityRecommendations() { views.deviceListHeaderSectionSecurityRecommendations.isVisible = false - views.deviceListUnverifiedSessionsRecommendation.isVisible = false - views.deviceListInactiveSessionsRecommendation.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 a23a7a7108..8f23fd06cc 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 b0ba8baa1a..59e7e1888e 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 6f6c5b24e2..3d9c3a8f37 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 7c133bc229..463d5bb495 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 0000000000..de56aa475e --- /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 0000000000..bdc35ed891 --- /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 ebdb74b74d..17ad58668a 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 3a418cf2c0..79dff5bc16 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 = "" + ) }