mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Add storage for the last sync date (#732)
This commit is contained in:
parent
c3cb61e43a
commit
bc1f5cb020
4 changed files with 96 additions and 0 deletions
|
@ -4,6 +4,7 @@ import com.x8bit.bitwarden.data.platform.repository.model.VaultTimeoutAction
|
|||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppLanguage
|
||||
import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Primary access point for general settings-related disk information.
|
||||
|
@ -41,6 +42,23 @@ interface SettingsDiskSource {
|
|||
*/
|
||||
fun clearData(userId: String)
|
||||
|
||||
/**
|
||||
* Gets the last time the app synced the vault data for a given [userId] (or `null` if the
|
||||
* vault has never been synced).
|
||||
*/
|
||||
fun getLastSyncTime(userId: String): Instant?
|
||||
|
||||
/**
|
||||
* Emits updates that track [getLastSyncTime] for the given [userId]. This will replay the
|
||||
* last known value, if any.
|
||||
*/
|
||||
fun getLastSyncTimeFlow(userId: String): Flow<Instant?>
|
||||
|
||||
/**
|
||||
* Stores the given [lastSyncTime] for the given [userId].
|
||||
*/
|
||||
fun storeLastSyncTime(userId: String, lastSyncTime: Instant?)
|
||||
|
||||
/**
|
||||
* Gets the current vault timeout (in minutes) for the given [userId] (or `null` if the vault
|
||||
* should never time out).
|
||||
|
|
|
@ -11,12 +11,14 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||
import kotlinx.coroutines.flow.onSubscription
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.time.Instant
|
||||
|
||||
private const val APP_LANGUAGE_KEY = "$BASE_KEY:appLocale"
|
||||
private const val APP_THEME_KEY = "$BASE_KEY:theme"
|
||||
private const val PULL_TO_REFRESH_KEY = "$BASE_KEY:syncOnRefresh"
|
||||
private const val INLINE_AUTOFILL_ENABLED_KEY = "$BASE_KEY:inlineAutofillEnabled"
|
||||
private const val BLOCKED_AUTOFILL_URIS_KEY = "$BASE_KEY:autofillBlacklistedUris"
|
||||
private const val VAULT_LAST_SYNC_TIME = "$BASE_KEY:vaultLastSyncTime"
|
||||
private const val VAULT_TIMEOUT_ACTION_KEY = "$BASE_KEY:vaultTimeoutAction"
|
||||
private const val VAULT_TIME_IN_MINUTES_KEY = "$BASE_KEY:vaultTimeout"
|
||||
private const val DISABLE_ICON_LOADING_KEY = "$BASE_KEY:disableFavicon"
|
||||
|
@ -34,6 +36,8 @@ class SettingsDiskSourceImpl(
|
|||
private val mutableAppThemeFlow =
|
||||
bufferedMutableSharedFlow<AppTheme>(replay = 1)
|
||||
|
||||
private val mutableLastSyncFlowMap = mutableMapOf<String, MutableSharedFlow<Instant?>>()
|
||||
|
||||
private val mutableVaultTimeoutActionFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<VaultTimeoutAction?>>()
|
||||
|
||||
|
@ -97,8 +101,24 @@ class SettingsDiskSourceImpl(
|
|||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = null,
|
||||
)
|
||||
storeLastSyncTime(userId = userId, lastSyncTime = null)
|
||||
}
|
||||
|
||||
override fun getLastSyncTime(userId: String): Instant? =
|
||||
getLong(key = "${VAULT_LAST_SYNC_TIME}_$userId")?.let { Instant.ofEpochMilli(it) }
|
||||
|
||||
override fun storeLastSyncTime(userId: String, lastSyncTime: Instant?) {
|
||||
putLong(
|
||||
key = "${VAULT_LAST_SYNC_TIME}_$userId",
|
||||
value = lastSyncTime?.toEpochMilli(),
|
||||
)
|
||||
getMutableLastSyncFlow(userId = userId).tryEmit(lastSyncTime)
|
||||
}
|
||||
|
||||
override fun getLastSyncTimeFlow(userId: String): Flow<Instant?> =
|
||||
getMutableLastSyncFlow(userId = userId)
|
||||
.onSubscription { emit(getLastSyncTime(userId = userId)) }
|
||||
|
||||
override fun getVaultTimeoutInMinutes(userId: String): Int? =
|
||||
getInt(key = "${VAULT_TIME_IN_MINUTES_KEY}_$userId")
|
||||
|
||||
|
@ -177,6 +197,13 @@ class SettingsDiskSourceImpl(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getMutableLastSyncFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<Instant?> =
|
||||
mutableLastSyncFlowMap.getOrPut(userId) {
|
||||
bufferedMutableSharedFlow(replay = 1)
|
||||
}
|
||||
|
||||
private fun getMutableVaultTimeoutActionFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<VaultTimeoutAction?> =
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.junit.jupiter.api.Assertions.assertFalse
|
|||
import org.junit.jupiter.api.Assertions.assertNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Instant
|
||||
|
||||
class SettingsDiskSourceTest {
|
||||
private val fakeSharedPreferences = FakeSharedPreferences()
|
||||
|
@ -89,6 +90,10 @@ class SettingsDiskSourceTest {
|
|||
userId = userId,
|
||||
isApprovePasswordlessLoginsEnabled = true,
|
||||
)
|
||||
settingsDiskSource.storeLastSyncTime(
|
||||
userId = userId,
|
||||
lastSyncTime = Instant.parse("2023-10-27T12:00:00Z"),
|
||||
)
|
||||
|
||||
settingsDiskSource.clearData(userId = userId)
|
||||
|
||||
|
@ -98,6 +103,29 @@ class SettingsDiskSourceTest {
|
|||
assertNull(settingsDiskSource.getInlineAutofillEnabled(userId = userId))
|
||||
assertNull(settingsDiskSource.getBlockedAutofillUris(userId = userId))
|
||||
assertNull(settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId = userId))
|
||||
assertNull(settingsDiskSource.getLastSyncTime(userId = userId))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getLastSyncTime should pull from and update SharedPreferences`() {
|
||||
val userId = "userId-1234"
|
||||
val lastVaultSync = "bwPreferencesStorage:vaultLastSyncTime_$userId"
|
||||
val instantLong = 1_698_408_000_000L
|
||||
val instant = Instant.ofEpochMilli(instantLong)
|
||||
|
||||
// Assert that the default value in disk source is null
|
||||
assertNull(settingsDiskSource.getLastSyncTime(userId = userId))
|
||||
|
||||
// Updating the shared preferences should update disk source.
|
||||
fakeSharedPreferences.edit { putLong(lastVaultSync, instantLong) }
|
||||
assertEquals(instant, settingsDiskSource.getLastSyncTime(userId = userId))
|
||||
|
||||
// Updating the disk source updates the shared preferences
|
||||
settingsDiskSource.storeLastSyncTime(userId = userId, lastSyncTime = instant)
|
||||
assertEquals(
|
||||
fakeSharedPreferences.getLong(lastVaultSync, 0L),
|
||||
instantLong,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.x8bit.bitwarden.ui.platform.feature.settings.appearance.model.AppThem
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
* Fake, memory-based implementation of [SettingsDiskSource].
|
||||
|
@ -17,6 +18,8 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
private val mutableAppThemeFlow =
|
||||
bufferedMutableSharedFlow<AppTheme>(replay = 1)
|
||||
|
||||
private val mutableLastSyncCallFlowMap = mutableMapOf<String, MutableSharedFlow<Instant?>>()
|
||||
|
||||
private val mutableVaultTimeoutActionsFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<VaultTimeoutAction?>>()
|
||||
|
||||
|
@ -30,6 +33,7 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
bufferedMutableSharedFlow<Boolean?>()
|
||||
|
||||
private var storedAppTheme: AppTheme = AppTheme.DEFAULT
|
||||
private val storedLastSyncTime = mutableMapOf<String, Instant?>()
|
||||
private val storedVaultTimeoutActions = mutableMapOf<String, VaultTimeoutAction?>()
|
||||
private val storedVaultTimeoutInMinutes = mutableMapOf<String, Int?>()
|
||||
|
||||
|
@ -76,6 +80,18 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
|
||||
mutableVaultTimeoutActionsFlowMap.remove(userId)
|
||||
mutableVaultTimeoutInMinutesFlowMap.remove(userId)
|
||||
mutableLastSyncCallFlowMap.remove(userId)
|
||||
}
|
||||
|
||||
override fun getLastSyncTime(userId: String): Instant? = storedLastSyncTime[userId]
|
||||
|
||||
override fun getLastSyncTimeFlow(userId: String): Flow<Instant?> =
|
||||
getMutableLastSyncTimeFlow(userId = userId)
|
||||
.onSubscription { emit(getLastSyncTime(userId = userId)) }
|
||||
|
||||
override fun storeLastSyncTime(userId: String, lastSyncTime: Instant?) {
|
||||
storedLastSyncTime[userId] = lastSyncTime
|
||||
getMutableLastSyncTimeFlow(userId = userId).tryEmit(lastSyncTime)
|
||||
}
|
||||
|
||||
override fun getVaultTimeoutInMinutes(userId: String): Int? =
|
||||
|
@ -152,6 +168,13 @@ class FakeSettingsDiskSource : SettingsDiskSource {
|
|||
|
||||
//region Private helper functions
|
||||
|
||||
private fun getMutableLastSyncTimeFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<Instant?> =
|
||||
mutableLastSyncCallFlowMap.getOrPut(userId) {
|
||||
bufferedMutableSharedFlow(replay = 1)
|
||||
}
|
||||
|
||||
private fun getMutableVaultTimeoutActionsFlow(
|
||||
userId: String,
|
||||
): MutableSharedFlow<VaultTimeoutAction?> =
|
||||
|
|
Loading…
Reference in a new issue