From 7db222af0c59f2f27acbed2ce21ed5cd1412d1d1 Mon Sep 17 00:00:00 2001 From: Onuray Sahin Date: Mon, 19 Sep 2022 15:37:13 +0300 Subject: [PATCH] Render security recommendation sessions. --- .../src/main/res/values/strings.xml | 1 + .../settings/devices/v2/DeviceFullInfo.kt | 1 + .../v2/VectorSettingsDevicesFragment.kt | 25 +++++++++++++ .../devices/v2/list/OtherSessionItem.kt | 8 +++++ .../v2/list/OtherSessionsController.kt | 36 ++++++++++++------- .../v2/list/SecurityRecommendationView.kt | 9 +++++ .../othersessions/OtherSessionsViewModel.kt | 4 +-- .../othersessions/OtherSessionsViewState.kt | 6 +++- .../v2/overview/GetDeviceFullInfoUseCase.kt | 4 ++- 9 files changed, 78 insertions(+), 16 deletions(-) diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index 714b48e8b4..82e2177581 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3246,6 +3246,7 @@ Verified · Last activity %1$s Unverified · Last activity %1$s + Unverified · Your current session Inactive for %1$d+ day (%2$s) diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt index f0a91c6183..373df53b1b 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/DeviceFullInfo.kt @@ -25,4 +25,5 @@ data class DeviceFullInfo( val cryptoDeviceInfo: CryptoDeviceInfo?, val roomEncryptionTrustLevel: RoomEncryptionTrustLevel, val isInactive: Boolean, + val isCurrentDevice: Boolean, ) 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 f4725d5571..da1a9a2fbd 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 @@ -41,6 +41,7 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER import im.vector.app.features.settings.devices.v2.list.OtherSessionsView import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS +import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationView import im.vector.app.features.settings.devices.v2.list.SecurityRecommendationViewState import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState import javax.inject.Inject @@ -84,6 +85,7 @@ class VectorSettingsDevicesFragment : initLearnMoreButtons() initWaitingView() initOtherSessionsView() + initSecurityRecommendationsView() observeViewEvents() } @@ -126,6 +128,29 @@ class VectorSettingsDevicesFragment : views.deviceListOtherSessions.callback = this } + private fun initSecurityRecommendationsView() { + views.deviceListUnverifiedSessionsRecommendation.callback = object : SecurityRecommendationView.Callback { + override fun onViewAllClicked() { + viewNavigator.navigateToOtherSessions( + requireActivity(), + R.string.device_manager_header_section_security_recommendations_title, + DeviceManagerFilterType.UNVERIFIED, + includeCurrentSession = true + ) + } + } + views.deviceListInactiveSessionsRecommendation.callback = object : SecurityRecommendationView.Callback { + override fun onViewAllClicked() { + viewNavigator.navigateToOtherSessions( + requireActivity(), + R.string.device_manager_header_section_security_recommendations_title, + DeviceManagerFilterType.INACTIVE, + includeCurrentSession = true + ) + } + } + } + override fun onDestroyView() { cleanUpLearnMoreButtonsListeners() super.onDestroyView() diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt index c73389d775..283e64fffe 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/OtherSessionItem.kt @@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices.v2.list import android.graphics.drawable.Drawable import android.widget.ImageView import android.widget.TextView +import androidx.annotation.ColorInt import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyModelClass import im.vector.app.R @@ -45,6 +46,10 @@ abstract class OtherSessionItem : VectorEpoxyModel(R.la @EpoxyAttribute var sessionDescription: String? = null + @EpoxyAttribute + @ColorInt + var sessionDescriptionColor: Int? = null + @EpoxyAttribute var sessionDescriptionDrawable: Drawable? = null @@ -82,6 +87,9 @@ abstract class OtherSessionItem : VectorEpoxyModel(R.la holder.otherSessionVerificationStatusImageView.render(roomEncryptionTrustLevel) holder.otherSessionNameTextView.text = sessionName holder.otherSessionDescriptionTextView.text = sessionDescription + sessionDescriptionColor?.let { + holder.otherSessionDescriptionTextView.setTextColor(it) + } holder.otherSessionDescriptionTextView.setCompoundDrawablesWithIntrinsicBounds(sessionDescriptionDrawable, null, null, null) } 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 06f3373f61..bcf6e87575 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,20 +53,14 @@ 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 = if (device.isInactive) { - stringProvider.getQuantityString( - R.plurals.device_manager_other_sessions_description_inactive, - SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, - SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, - formattedLastActivityDate - ) - } else if (device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) { - stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate) + val description = calculateDescription(device, formattedLastActivityDate) + val descriptionColor = if (device.isCurrentDevice) { + host.colorProvider.getColorFromAttribute(R.attr.colorError) } else { - stringProvider.getString(R.string.device_manager_other_sessions_description_unverified, formattedLastActivityDate) + host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) } - val drawableColor = colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) - val descriptionDrawable = if (device.isInactive) drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) else null + val drawableColor = host.colorProvider.getColorFromAttribute(R.attr.vctr_content_secondary) + val descriptionDrawable = if (device.isInactive) host.drawableProvider.getDrawable(R.drawable.ic_inactive_sessions, drawableColor) else null otherSessionItem { id(device.deviceInfo.deviceId) @@ -75,10 +69,28 @@ class OtherSessionsController @Inject constructor( sessionName(device.deviceInfo.displayName) sessionDescription(description) sessionDescriptionDrawable(descriptionDrawable) + sessionDescriptionColor(descriptionColor) stringProvider(this@OtherSessionsController.stringProvider) clickListener { device.deviceInfo.deviceId?.let { host.callback?.onItemClicked(it) } } } } } } + + private fun calculateDescription(device: DeviceFullInfo, formattedLastActivityDate: String): String { + return if (device.isInactive) { + stringProvider.getQuantityString( + R.plurals.device_manager_other_sessions_description_inactive, + SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, + SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS, + formattedLastActivityDate + ) + } else if (device.roomEncryptionTrustLevel == RoomEncryptionTrustLevel.Trusted) { + stringProvider.getString(R.string.device_manager_other_sessions_description_verified, formattedLastActivityDate) + } else if (device.isCurrentDevice) { + stringProvider.getString(R.string.device_manager_other_sessions_description_unverified_current_session) + } 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/SecurityRecommendationView.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt index 93cf3c0501..c4d86af8c3 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/SecurityRecommendationView.kt @@ -31,7 +31,12 @@ class SecurityRecommendationView @JvmOverloads constructor( defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { + interface Callback { + fun onViewAllClicked() + } + private val views: ViewSecurityRecommendationBinding + var callback: Callback? = null init { inflate(context, R.layout.view_security_recommendation, this) @@ -47,6 +52,10 @@ class SecurityRecommendationView @JvmOverloads constructor( setDescription(it) setImage(it) } + + views.recommendationViewAllButton.setOnClickListener { + callback?.onViewAllClicked() + } } private fun setTitle(typedArray: TypedArray) { diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt index a40d95c6d9..2ca24dd92a 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewModel.kt @@ -30,7 +30,7 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import kotlinx.coroutines.Job class OtherSessionsViewModel @AssistedInject constructor( - @Assisted initialState: OtherSessionsViewState, + @Assisted private val initialState: OtherSessionsViewState, activeSessionHolder: ActiveSessionHolder, private val getDeviceFullInfoListUseCase: GetDeviceFullInfoListUseCase, refreshDevicesUseCase: RefreshDevicesUseCase @@ -55,7 +55,7 @@ class OtherSessionsViewModel @AssistedInject constructor( observeDevicesJob?.cancel() observeDevicesJob = getDeviceFullInfoListUseCase.execute( filterType = currentFilter, - excludeCurrentDevice = true + excludeCurrentDevice = !initialState.includeCurrentSession ) .execute { async -> copy( diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt index d03cba03f9..cb30490845 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/othersessions/OtherSessionsViewState.kt @@ -25,4 +25,8 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType data class OtherSessionsViewState( val devices: Async> = Uninitialized, val currentFilter: DeviceManagerFilterType = DeviceManagerFilterType.ALL_SESSIONS, -) : MavericksState + val includeCurrentSession: Boolean = false, +) : MavericksState { + + constructor(args: OtherSessionsArgs) : this(includeCurrentSession = args.includeCurrentSession) +} diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt index 5a8106f2fd..72a48238b0 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/overview/GetDeviceFullInfoUseCase.kt @@ -48,11 +48,13 @@ class GetDeviceFullInfoUseCase @Inject constructor( val fullInfo = if (info != null && cryptoInfo != null) { val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo) val isInactive = checkIfSessionIsInactiveUseCase.execute(info.lastSeenTs ?: 0) + val isCurrentDevice = currentSessionCrossSigningInfo.deviceId == cryptoInfo.deviceId DeviceFullInfo( deviceInfo = info, cryptoDeviceInfo = cryptoInfo, roomEncryptionTrustLevel = roomEncryptionTrustLevel, - isInactive = isInactive + isInactive = isInactive, + isCurrentDevice = isCurrentDevice, ) } else { null