Render security recommendations.

This commit is contained in:
Onuray Sahin 2022-08-30 14:20:01 +03:00 committed by Maxime NATUREL
parent c23a4e4601
commit 58846038ce
5 changed files with 53 additions and 1 deletions

View file

@ -30,10 +30,13 @@ import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.DateProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.resources.toTimestamp
import im.vector.app.core.utils.PublishDataSource
import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.login.ReAuthHelper
import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
import im.vector.lib.core.utils.flow.throttleFirst
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
@ -52,6 +55,7 @@ import org.matrix.android.sdk.api.auth.UserPasswordAuth
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
import org.matrix.android.sdk.api.auth.registration.nextUncompletedStage
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
@ -67,6 +71,7 @@ import org.matrix.android.sdk.api.util.awaitCallback
import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.flow.flow
import timber.log.Timber
import java.util.concurrent.TimeUnit
import javax.net.ssl.HttpsURLConnection
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
@ -81,6 +86,8 @@ data class DevicesViewState(
val request: Async<Unit> = Uninitialized,
val hasAccountCrossSigning: Boolean = false,
val accountCrossSigningIsTrusted: Boolean = false,
val unverifiedSessionsCount: Int = 0,
val inactiveSessionsCount: Int = 0,
) : MavericksState
data class DeviceFullInfo(
@ -125,6 +132,14 @@ class DevicesViewModel @AssistedInject constructor(
session.flow().liveUserCryptoDevices(session.myUserId),
session.flow().liveMyDevicesInfo()
) { cryptoList, infoList ->
val unverifiedSessionsCount = cryptoList.count { !it.trustLevel?.isVerified().orFalse() }
val inactiveSessionsCount = infoList.count { isInactiveSession(it.date) }
setState {
copy(
unverifiedSessionsCount = unverifiedSessionsCount,
inactiveSessionsCount = inactiveSessionsCount
)
}
infoList
.sortedByDescending { it.lastSeenTs }
.map { deviceInfo ->
@ -188,6 +203,14 @@ class DevicesViewModel @AssistedInject constructor(
queryRefreshDevicesList()
}
private fun isInactiveSession(lastSeenTs: Long): Boolean {
val lastSeenDate = DateProvider.toLocalDateTime(lastSeenTs)
val currentDate = DateProvider.currentLocalDateTime()
val diffMilliseconds = currentDate.toTimestamp() - lastSeenDate.toTimestamp()
val diffDays = TimeUnit.MILLISECONDS.toDays(diffMilliseconds)
return diffDays > SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS
}
override fun onCleared() {
session.cryptoService().verificationService().removeListener(this)
super.onCleared()

View file

@ -131,9 +131,11 @@ class VectorSettingsDevicesFragment :
}
val otherDevices = devices?.filter { it.deviceInfo.deviceId != state.myDeviceId }
renderSecurityRecommendations(state.inactiveSessionsCount, state.unverifiedSessionsCount)
renderCurrentDevice(currentDeviceInfo)
renderOtherSessionsView(otherDevices)
} else {
hideSecurityRecommendations()
hideCurrentSessionView()
hideOtherSessionsView()
}
@ -141,6 +143,26 @@ class VectorSettingsDevicesFragment :
handleRequestStatus(state.request)
}
private fun renderSecurityRecommendations(inactiveSessionsCount: Int, unverifiedSessionsCount: Int) {
if (unverifiedSessionsCount == 0 && inactiveSessionsCount == 0) {
hideSecurityRecommendations()
} else {
views.deviceListHeaderSectionSecurityRecommendations.isVisible = true
views.deviceListSecurityRecommendationsDivider.isVisible = true
views.deviceListUnverifiedSessionsRecommendation.isVisible = unverifiedSessionsCount > 0
views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0
views.deviceListUnverifiedSessionsRecommendation.setCount(unverifiedSessionsCount)
views.deviceListInactiveSessionsRecommendation.setCount(inactiveSessionsCount)
}
}
private fun hideSecurityRecommendations() {
views.deviceListHeaderSectionSecurityRecommendations.isVisible = false
views.deviceListUnverifiedSessionsRecommendation.isVisible = false
views.deviceListInactiveSessionsRecommendation.isVisible = false
views.deviceListSecurityRecommendationsDivider.isVisible = false
}
private fun renderOtherSessionsView(otherDevices: List<DeviceFullInfo>?) {
if (otherDevices.isNullOrEmpty()) {
hideOtherSessionsView()
@ -169,6 +191,7 @@ class VectorSettingsDevicesFragment :
private fun hideCurrentSessionView() {
views.deviceListHeaderCurrentSession.isVisible = false
views.deviceListCurrentSession.isVisible = false
views.deviceListCurrentSessionDivider.isVisible = false
}
private fun handleRequestStatus(unIgnoreRequest: Async<Unit>) {

View file

@ -65,4 +65,8 @@ class SecurityRecommendationView @JvmOverloads constructor(
views.recommendationShieldImageView.setImageResource(imageResource)
views.recommendationShieldImageView.backgroundTintList = ColorStateList.valueOf(backgroundTint)
}
fun setCount(sessionsCount: Int) {
views.recommendationViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, sessionsCount)
}
}

View file

@ -17,3 +17,4 @@
package im.vector.app.features.settings.devices.v2.list
internal const val NUMBER_OF_OTHER_DEVICES_TO_RENDER = 5
internal const val SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS = 90

View file

@ -32,10 +32,11 @@
<TextView
android:id="@+id/recommendationDescriptionTextView"
style="@style/TextAppearance.Vector.Body.DevicesManagement"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintStart_toStartOf="@id/recommendationTitleTextView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/recommendationTitleTextView"
tools:text="@string/device_manager_unverified_sessions_description" />