Render security recommendation sessions.
This commit is contained in:
		
							parent
							
								
									740b69d48c
								
							
						
					
					
						commit
						7db222af0c
					
				@ -3246,6 +3246,7 @@
 | 
			
		||||
    <string name="device_manager_other_sessions_description_verified">Verified · Last activity %1$s</string>
 | 
			
		||||
    <!--  Examples: Unverified · Last activity Yesterday at 6PM, Unverified · Last activity Aug 31 at 5:47PM -->
 | 
			
		||||
    <string name="device_manager_other_sessions_description_unverified">Unverified · Last activity %1$s</string>
 | 
			
		||||
    <string name="device_manager_other_sessions_description_unverified_current_session">Unverified · Your current session</string>
 | 
			
		||||
    <!--  Example: Inactive for 90+ days (Dec 25, 2021) -->
 | 
			
		||||
    <plurals name="device_manager_other_sessions_description_inactive">
 | 
			
		||||
        <item quantity="one">Inactive for %1$d+ day (%2$s)</item>
 | 
			
		||||
 | 
			
		||||
@ -25,4 +25,5 @@ data class DeviceFullInfo(
 | 
			
		||||
        val cryptoDeviceInfo: CryptoDeviceInfo?,
 | 
			
		||||
        val roomEncryptionTrustLevel: RoomEncryptionTrustLevel,
 | 
			
		||||
        val isInactive: Boolean,
 | 
			
		||||
        val isCurrentDevice: Boolean,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -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()
 | 
			
		||||
 | 
			
		||||
@ -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<OtherSessionItem.Holder>(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<OtherSessionItem.Holder>(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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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) {
 | 
			
		||||
 | 
			
		||||
@ -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(
 | 
			
		||||
 | 
			
		||||
@ -25,4 +25,8 @@ import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
 | 
			
		||||
data class OtherSessionsViewState(
 | 
			
		||||
        val devices: Async<List<DeviceFullInfo>> = Uninitialized,
 | 
			
		||||
        val currentFilter: DeviceManagerFilterType = DeviceManagerFilterType.ALL_SESSIONS,
 | 
			
		||||
) : MavericksState
 | 
			
		||||
        val includeCurrentSession: Boolean = false,
 | 
			
		||||
) : MavericksState {
 | 
			
		||||
 | 
			
		||||
    constructor(args: OtherSessionsArgs) : this(includeCurrentSession = args.includeCurrentSession)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user