mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-10-31 16:22:49 +00:00
Add persistent account provider history to app preferences
This commit is contained in:
+14
@@ -38,6 +38,20 @@ interface AppPreferencesStore {
|
||||
suspend fun setTracingLogLevel(logLevel: LogLevel)
|
||||
fun getTracingLogLevelFlow(): Flow<LogLevel>
|
||||
|
||||
/**
|
||||
* The list of account providers (homeserver URLs) the user has previously authenticated
|
||||
* against, most recent first. Used to default the account provider and to power autocomplete
|
||||
* suggestions during sign-in. Stored locally only; never synced across devices.
|
||||
*/
|
||||
fun getHomeserverHistoryFlow(): Flow<List<String>>
|
||||
|
||||
/**
|
||||
* Add [url] to the front of the account provider history (see [getHomeserverHistoryFlow]).
|
||||
* The value is normalised (trimmed + lowercased); existing case-insensitive duplicates are
|
||||
* moved to the front rather than duplicated, and the list is capped in size. No-op for blanks.
|
||||
*/
|
||||
suspend fun addHomeserverToHistory(url: String)
|
||||
|
||||
suspend fun setTracingLogPacks(targets: Set<TraceLogPack>)
|
||||
fun getTracingLogPacksFlow(): Flow<Set<TraceLogPack>>
|
||||
|
||||
|
||||
+27
@@ -8,6 +8,7 @@
|
||||
|
||||
package io.element.android.libraries.preferences.impl.store
|
||||
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.intPreferencesKey
|
||||
@@ -36,6 +37,7 @@ private val timelineMediaPreviewValueKey = stringPreferencesKey("timelineMediaPr
|
||||
private val liveLocationMinimumDistanceUpdateKey = intPreferencesKey("liveLocationMinimumDistanceUpdate")
|
||||
private val logLevelKey = stringPreferencesKey("logLevel")
|
||||
private val traceLogPacksKey = stringPreferencesKey("traceLogPacks")
|
||||
private val homeserverHistoryKey = stringPreferencesKey("homeserverHistory")
|
||||
private val messageSoundUriKey = stringPreferencesKey("notificationMessageSoundUri")
|
||||
private val messageSoundChannelVersionKey = intPreferencesKey("notificationMessageSoundChannelVersion")
|
||||
private val messageSoundDisplayNameKey = stringPreferencesKey("notificationMessageSoundDisplayName")
|
||||
@@ -43,6 +45,10 @@ private val callRingtoneUriKey = stringPreferencesKey("notificationCallRingtoneU
|
||||
private val callRingtoneChannelVersionKey = intPreferencesKey("notificationCallRingtoneChannelVersion")
|
||||
private val callRingtoneDisplayNameKey = stringPreferencesKey("notificationCallRingtoneDisplayName")
|
||||
|
||||
// URLs never contain a newline, so it is a safe delimiter to persist an ordered list in a single String.
|
||||
private const val HOMESERVER_HISTORY_DELIMITER = "\n"
|
||||
private const val MAX_HOMESERVER_HISTORY_SIZE = 10
|
||||
|
||||
@ContributesBinding(AppScope::class)
|
||||
class DefaultAppPreferencesStore(
|
||||
private val buildMeta: BuildMeta,
|
||||
@@ -168,6 +174,20 @@ class DefaultAppPreferencesStore(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHomeserverHistoryFlow(): Flow<List<String>> {
|
||||
return store.data.map { prefs -> prefs.readHomeserverHistory() }
|
||||
}
|
||||
|
||||
override suspend fun addHomeserverToHistory(url: String) {
|
||||
val normalized = url.trim().lowercase()
|
||||
if (normalized.isEmpty()) return
|
||||
store.edit { prefs ->
|
||||
val updated = (listOf(normalized) + prefs.readHomeserverHistory().filter { it != normalized })
|
||||
.take(MAX_HOMESERVER_HISTORY_SIZE)
|
||||
prefs[homeserverHistoryKey] = updated.joinToString(HOMESERVER_HISTORY_DELIMITER)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessageSoundFlow(): Flow<NotificationSound> {
|
||||
return store.data.map { prefs -> NotificationSound.fromStored(prefs[messageSoundUriKey]) }
|
||||
}
|
||||
@@ -242,6 +262,13 @@ class DefaultAppPreferencesStore(
|
||||
}
|
||||
}
|
||||
|
||||
private fun Preferences.readHomeserverHistory(): List<String> {
|
||||
return this[homeserverHistoryKey]
|
||||
?.split(HOMESERVER_HISTORY_DELIMITER)
|
||||
?.filter { it.isNotEmpty() }
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
private fun BuildMeta.defaultLogLevel(): LogLevel {
|
||||
return when (buildType) {
|
||||
BuildType.DEBUG -> LogLevel.TRACE
|
||||
|
||||
+70
@@ -54,4 +54,74 @@ class DefaultAppPreferencesStoreTest {
|
||||
|
||||
assertThat(store.getLiveLocationMinimumDistanceInMetersUpdateFlow().first()).isEqualTo(25)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `homeserver history is empty by default`() = runTest {
|
||||
val store = createStore()
|
||||
assertThat(store.getHomeserverHistoryFlow().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adding homeservers keeps the most recent first`() = runTest {
|
||||
val store = createStore()
|
||||
|
||||
store.addHomeserverToHistory("https://matrix.org")
|
||||
store.addHomeserverToHistory("https://example.com")
|
||||
|
||||
assertThat(store.getHomeserverHistoryFlow().first())
|
||||
.containsExactly("https://example.com", "https://matrix.org")
|
||||
.inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `adding an existing homeserver moves it to the front without duplicating`() = runTest {
|
||||
val store = createStore()
|
||||
|
||||
store.addHomeserverToHistory("https://matrix.org")
|
||||
store.addHomeserverToHistory("https://example.com")
|
||||
store.addHomeserverToHistory("https://matrix.org")
|
||||
|
||||
assertThat(store.getHomeserverHistoryFlow().first())
|
||||
.containsExactly("https://matrix.org", "https://example.com")
|
||||
.inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `homeservers are normalised and deduplicated case-insensitively`() = runTest {
|
||||
val store = createStore()
|
||||
|
||||
store.addHomeserverToHistory(" HTTPS://Matrix.ORG ")
|
||||
store.addHomeserverToHistory("https://matrix.org")
|
||||
|
||||
assertThat(store.getHomeserverHistoryFlow().first())
|
||||
.containsExactly("https://matrix.org")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `blank homeservers are ignored`() = runTest {
|
||||
val store = createStore()
|
||||
|
||||
store.addHomeserverToHistory(" ")
|
||||
|
||||
assertThat(store.getHomeserverHistoryFlow().first()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `homeserver history is capped to the most recent entries`() = runTest {
|
||||
val store = createStore()
|
||||
|
||||
repeat(15) { index ->
|
||||
store.addHomeserverToHistory("https://server$index.org")
|
||||
}
|
||||
|
||||
val history = store.getHomeserverHistoryFlow().first()
|
||||
assertThat(history).hasSize(10)
|
||||
assertThat(history.first()).isEqualTo("https://server14.org")
|
||||
assertThat(history.last()).isEqualTo("https://server5.org")
|
||||
}
|
||||
|
||||
private fun createStore() = DefaultAppPreferencesStore(
|
||||
buildMeta = buildMeta,
|
||||
preferenceDataStoreFactory = FakePreferenceDataStoreFactory(),
|
||||
)
|
||||
}
|
||||
|
||||
+15
@@ -16,6 +16,7 @@ import io.element.android.libraries.preferences.api.store.NotificationSound
|
||||
import io.element.android.libraries.preferences.api.store.NotificationSoundChannelConfig
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.flow.updateAndGet
|
||||
|
||||
class InMemoryAppPreferencesStore(
|
||||
@@ -27,6 +28,7 @@ class InMemoryAppPreferencesStore(
|
||||
liveLocationMinimumDistanceUpdate: Int = 10,
|
||||
logLevel: LogLevel = LogLevel.INFO,
|
||||
traceLogPacks: Set<TraceLogPack> = emptySet(),
|
||||
homeserverHistory: List<String> = emptyList(),
|
||||
messageSound: NotificationSound = NotificationSound.SystemDefault,
|
||||
messageSoundChannelVersion: Int = 0,
|
||||
messageSoundDisplayName: String? = null,
|
||||
@@ -40,6 +42,7 @@ class InMemoryAppPreferencesStore(
|
||||
private val liveLocationMinimumDistanceUpdate = MutableStateFlow(liveLocationMinimumDistanceUpdate)
|
||||
private val logLevel = MutableStateFlow(logLevel)
|
||||
private val tracingLogPacks = MutableStateFlow(traceLogPacks)
|
||||
private val homeserverHistory = MutableStateFlow(homeserverHistory)
|
||||
private val hideInviteAvatars = MutableStateFlow(hideInviteAvatars)
|
||||
private val timelineMediaPreviewValue = MutableStateFlow(timelineMediaPreviewValue)
|
||||
private val messageSound = MutableStateFlow(messageSound)
|
||||
@@ -117,6 +120,18 @@ class InMemoryAppPreferencesStore(
|
||||
return tracingLogPacks
|
||||
}
|
||||
|
||||
override fun getHomeserverHistoryFlow(): Flow<List<String>> {
|
||||
return homeserverHistory
|
||||
}
|
||||
|
||||
override suspend fun addHomeserverToHistory(url: String) {
|
||||
val normalized = url.trim().lowercase()
|
||||
if (normalized.isEmpty()) return
|
||||
homeserverHistory.update { current ->
|
||||
(listOf(normalized) + current.filter { it != normalized }).take(10)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessageSoundFlow(): Flow<NotificationSound> {
|
||||
return messageSound
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user