From 893008a93d5ad76d00c5f4877f3921a188567a4f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 10 Oct 2022 17:09:46 +0200 Subject: [PATCH 01/12] Adding changelog entry --- changelog.d/7327.wip | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/7327.wip 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 From b18b95d1a7b40fd626f0ab64768f25fc3500a131 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Mon, 10 Oct 2022 17:32:51 +0200 Subject: [PATCH 02/12] Adding new unknown shield icon --- .../app/core/ui/views/ShieldImageView.kt | 6 +++--- .../main/res/drawable/ic_shield_unknown.xml | 20 +++++++++++++++++++ .../drawable/ic_shield_unknown_no_border.xml | 18 +++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 vector/src/main/res/drawable/ic_shield_unknown.xml create mode 100644 vector/src/main/res/drawable/ic_shield_unknown_no_border.xml 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/res/drawable/ic_shield_unknown.xml b/vector/src/main/res/drawable/ic_shield_unknown.xml new file mode 100644 index 0000000000..7f5fbc730b --- /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..89331edf9e --- /dev/null +++ b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml @@ -0,0 +1,18 @@ + + + + + From ea0c99011f5603f26e866b112a0ed5116efa5443 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 10:41:38 +0200 Subject: [PATCH 03/12] Updating descriptions when unknown verification status --- .../ui-strings/src/main/res/values/strings.xml | 2 ++ .../devices/v2/list/OtherSessionsController.kt | 7 +++++-- .../settings/devices/v2/list/SessionInfoView.kt | 15 +++++++++++---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index c997fb5639..97db6ea627 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3250,10 +3250,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/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..bef3f5c9e4 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,11 @@ 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 +143,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 From a48131baad8d4c5e289b06990f758ce91f8c1ad7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 11:05:02 +0200 Subject: [PATCH 04/12] Removing duplication of rendering session info --- .../v2/overview/SessionOverviewFragment.kt | 23 ------------------- 1 file changed, 23 deletions(-) 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..606f647b91 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) { @@ -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 - } } From 7be958e6a5ca84921e42c1d0fa810161b8cc4df7 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 11:06:34 +0200 Subject: [PATCH 05/12] Removing learn more in case of unknown verification status --- .../settings/devices/v2/overview/SessionOverviewFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 606f647b91..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 @@ -207,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) From 7ce56ced4d31a5d5c6029700f422377d6c72559b Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 11:08:57 +0200 Subject: [PATCH 06/12] Replacing error prone takeIf structure --- .../devices/v2/list/SessionInfoView.kt | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) 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 bef3f5c9e4..9603085a57 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 @@ -162,34 +162,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 }) } From f77bceb918a81fd7655022ce08b0607fa4b73b5c Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 11:33:32 +0200 Subject: [PATCH 07/12] Hide unverified sessions section when current session is not verified --- .../v2/VectorSettingsDevicesFragment.kt | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) 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..3cf3b33942 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,13 +181,18 @@ class VectorSettingsDevicesFragment : handleLoadingStatus(state.isLoading) } - private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) { + private fun renderSecurityRecommendations( + inactiveSessionsCount: Int, + unverifiedSessionsCount: Int, + isCurrentSessionVerified: Boolean, + ) { if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) { hideSecurityRecommendations() } else { views.deviceListHeaderSectionSecurityRecommendations.isVisible = true views.deviceListSecurityRecommendationsDivider.isVisible = true - views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0 + + views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0 && isCurrentSessionVerified views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0 val unverifiedSessionsViewState = SecurityRecommendationViewState( description = getString(R.string.device_manager_unverified_sessions_description), @@ -206,11 +211,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?) { From 28ce915f186119bb85b82d38e4668c40922ffc05 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 15:01:31 +0200 Subject: [PATCH 08/12] Checking current session status in the filter use case --- .../settings/devices/v2/GetDeviceFullInfoListUseCase.kt | 2 +- .../settings/devices/v2/filter/FilterDevicesUseCase.kt | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) 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/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 } } From 316f1efe324c97fe8f8fc86066b65c15792738f5 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 15:21:25 +0200 Subject: [PATCH 09/12] Updating unit tests --- .../v2/GetDeviceFullInfoListUseCaseTest.kt | 6 ++- .../v2/filter/FilterDevicesUseCaseTest.kt | 39 ++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) 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 = "" + ) } From f72ab9b51e8da1f21ad17b52558dc7eb172c3933 Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Thu, 13 Oct 2022 15:22:50 +0200 Subject: [PATCH 10/12] Fixing code style issue --- .../app/features/settings/devices/v2/list/SessionInfoView.kt | 1 - 1 file changed, 1 deletion(-) 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 9603085a57..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 @@ -94,7 +94,6 @@ class SessionInfoView @JvmOverloads constructor( encryptionTrustLevel == RoomEncryptionTrustLevel.Trusted -> renderCrossSigningVerified(isCurrentSession) encryptionTrustLevel == RoomEncryptionTrustLevel.Default && !isCurrentSession -> renderCrossSigningUnknown() else -> renderCrossSigningUnverified(isCurrentSession, isVerifyButtonVisible) - } if (hasLearnMoreLink) { appendLearnMoreToVerificationStatus() From cf25b81e9aa420d284b841176e79eb907c8a3dad Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 14 Oct 2022 14:00:01 +0200 Subject: [PATCH 11/12] Adding new shield color into resources --- .../ui-styles/src/main/res/values/colors.xml | 1 + vector/build.gradle | 2 ++ .../main/res/drawable/ic_shield_unknown.xml | 28 +++++++++---------- .../drawable/ic_shield_unknown_no_border.xml | 24 ++++++++-------- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/library/ui-styles/src/main/res/values/colors.xml b/library/ui-styles/src/main/res/values/colors.xml index f4384adb40..c0bdde1ba9 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/res/drawable/ic_shield_unknown.xml b/vector/src/main/res/drawable/ic_shield_unknown.xml index 7f5fbc730b..de56aa475e 100644 --- a/vector/src/main/res/drawable/ic_shield_unknown.xml +++ b/vector/src/main/res/drawable/ic_shield_unknown.xml @@ -3,18 +3,18 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - - - + + + 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 index 89331edf9e..bdc35ed891 100644 --- a/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml +++ b/vector/src/main/res/drawable/ic_shield_unknown_no_border.xml @@ -3,16 +3,16 @@ android:height="24dp" android:viewportWidth="24" android:viewportHeight="24"> - - - + + + From 81038bdd2367e1a15e554dbb9866424c1caeaf4f Mon Sep 17 00:00:00 2001 From: Maxime NATUREL Date: Fri, 14 Oct 2022 15:19:51 +0200 Subject: [PATCH 12/12] Fixing security section recommendation visibility --- .../settings/devices/v2/VectorSettingsDevicesFragment.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 3cf3b33942..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 @@ -186,14 +186,16 @@ class VectorSettingsDevicesFragment : unverifiedSessionsCount: Int, isCurrentSessionVerified: Boolean, ) { - if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) { + 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 && isCurrentSessionVerified - 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,