diff --git a/library/ui-strings/src/main/res/values/strings.xml b/library/ui-strings/src/main/res/values/strings.xml index ed21c2de8a..8cf89196e1 100644 --- a/library/ui-strings/src/main/res/values/strings.xml +++ b/library/ui-strings/src/main/res/values/strings.xml @@ -3231,6 +3231,6 @@ Unverified sessions Verify or sign out from unverified sessions. Inactive sessions - Consider signing out from old sessions (90 days or older) you don’t use anymore. + Consider signing out from old sessions (%1$d days or more) that you don’t use anymore. 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 245cfe9809..9d1f446b33 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 @@ -40,6 +40,7 @@ import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.settings.devices.DevicesAction import im.vector.app.features.settings.devices.DevicesViewEvents import im.vector.app.features.settings.devices.DevicesViewModel +import im.vector.app.features.settings.devices.v2.list.SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS /** * Display the list of the user's devices and sessions. @@ -153,6 +154,9 @@ class VectorSettingsDevicesFragment : views.deviceListInactiveSessionsRecommendation.isVisible = inactiveSessionsCount > 0 views.deviceListUnverifiedSessionsRecommendation.setCount(unverifiedSessionsCount) views.deviceListInactiveSessionsRecommendation.setCount(inactiveSessionsCount) + views.deviceListInactiveSessionsRecommendation.setDescription( + getString(R.string.device_manager_inactive_sessions_description, SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS) + ) } } diff --git a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCase.kt b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCase.kt index c5d5f804a6..8991ad1e3d 100644 --- a/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCase.kt +++ b/vector/src/main/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCase.kt @@ -16,18 +16,19 @@ package im.vector.app.features.settings.devices.v2.list -import im.vector.app.core.resources.DateProvider -import im.vector.app.core.resources.toTimestamp +import im.vector.app.core.time.Clock import java.util.concurrent.TimeUnit import javax.inject.Inject -class CheckIfSessionIsInactiveUseCase @Inject constructor() { +class CheckIfSessionIsInactiveUseCase @Inject constructor( + private val clock: Clock, +) { fun execute(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 + // In case of the server doesn't send the last seen date. + if (lastSeenTs == 0L) return true + + val diffMilliseconds = clock.epochMillis() - lastSeenTs + return diffMilliseconds >= TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) } } 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 73d8d74bfd..c715704444 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 @@ -66,6 +66,14 @@ class SecurityRecommendationView @JvmOverloads constructor( views.recommendationShieldImageView.backgroundTintList = ColorStateList.valueOf(backgroundTint) } + fun setTitle(title: String) { + views.recommendationTitleTextView.text = title + } + + fun setDescription(description: String) { + views.recommendationDescriptionTextView.text = description + } + fun setCount(sessionsCount: Int) { views.recommendationViewAllButton.text = context.getString(R.string.device_manager_other_sessions_view_all, sessionsCount) } diff --git a/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCaseTest.kt b/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCaseTest.kt index 32f809057b..b7d56a88bf 100644 --- a/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCaseTest.kt +++ b/vector/src/test/java/im/vector/app/features/settings/devices/v2/list/CheckIfSessionIsInactiveUseCaseTest.kt @@ -16,62 +16,44 @@ package im.vector.app.features.settings.devices.v2.list -import im.vector.app.core.resources.DateProvider -import im.vector.app.core.resources.toTimestamp -import io.mockk.every -import io.mockk.mockk -import io.mockk.mockkStatic +import im.vector.app.test.fakes.FakeClock import org.amshove.kluent.shouldBeEqualTo -import org.junit.Before import org.junit.Test -import org.threeten.bp.Instant -import org.threeten.bp.LocalDateTime -import org.threeten.bp.ZoneId -import org.threeten.bp.ZoneId.systemDefault -import org.threeten.bp.ZoneOffset -import org.threeten.bp.zone.ZoneRules +import java.util.concurrent.TimeUnit + +private const val A_TIMESTAMP = 1654689143L class CheckIfSessionIsInactiveUseCaseTest { - private val checkIfSessionIsInactiveUseCase = CheckIfSessionIsInactiveUseCase() - private val zoneId = mockk() - private val zoneRules = mockk() - - @Before - fun setup() { - mockkStatic(ZoneId::class) - every { systemDefault() } returns zoneId - every { zoneId.rules } returns zoneRules - every { zoneRules.getOffset(any()) } returns ZoneOffset.UTC - every { zoneRules.getOffset(any()) } returns ZoneOffset.UTC - } + private val clock = FakeClock().apply { givenEpoch(A_TIMESTAMP) } + private val checkIfSessionIsInactiveUseCase = CheckIfSessionIsInactiveUseCase(clock) @Test fun `given an old last seen date then session is inactive`() { - val lastSeenDate = DateProvider.currentLocalDateTime().minusDays((SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS + 1).toLong()) + val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) - 1 - checkIfSessionIsInactiveUseCase.execute(lastSeenDate.toTimestamp()) shouldBeEqualTo true + checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true } @Test fun `given a last seen date equal to the threshold then session is inactive`() { - val lastSeenDate = DateProvider.currentLocalDateTime().minusDays((SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS).toLong()) + val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) - checkIfSessionIsInactiveUseCase.execute(lastSeenDate.toTimestamp()) shouldBeEqualTo true + checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true } @Test fun `given a recent last seen date then session is active`() { - val lastSeenDate = DateProvider.currentLocalDateTime().minusDays((SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS - 1).toLong()) + val lastSeenDate = A_TIMESTAMP - TimeUnit.DAYS.toMillis(SESSION_IS_MARKED_AS_INACTIVE_AFTER_DAYS.toLong()) + 1 - checkIfSessionIsInactiveUseCase.execute(lastSeenDate.toTimestamp()) shouldBeEqualTo false + checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo false } @Test fun `given a last seen date as zero then session is inactive`() { // In case of the server doesn't send the last seen date. - val lastSeenDate = DateProvider.toLocalDateTime(0) + val lastSeenDate = 0L - checkIfSessionIsInactiveUseCase.execute(lastSeenDate.toTimestamp()) shouldBeEqualTo true + checkIfSessionIsInactiveUseCase.execute(lastSeenDate) shouldBeEqualTo true } }