diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt index b1877b3ae5..f35fa88442 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/AppPreferencesStore.kt @@ -13,48 +13,111 @@ import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.TraceLogPack import kotlinx.coroutines.flow.Flow +/** + * Local, app-wide user preferences that are not tied to a session, stored on the device only. + * + * Each getter returns a flow that emits the current value straight away and then again on every change. + * The default each preference falls back to when it has never been set is given on the getter. + */ interface AppPreferencesStore { + /** + * @param enabled true to expose the developer options in the settings. + */ suspend fun setDeveloperModeEnabled(enabled: Boolean) + + /** Whether developer mode is on; defaults to `true` on debug builds and `false` otherwise. */ fun isDeveloperModeEnabledFlow(): Flow + /** + * @param string the Element Call deployment to use, or `null` to go back to the one from the homeserver. + */ suspend fun setCustomElementCallBaseUrl(string: String?) + + /** The Element Call URL the user has overridden, or `null` when the homeserver's own value should be used. */ fun getCustomElementCallBaseUrlFlow(): Flow + /** + * @param theme the name of the theme to apply: light, dark, or follow the system. + */ suspend fun setTheme(theme: String) + + /** The name of the chosen theme, or `null` when the user has not picked one and the system setting should be followed. */ fun getThemeFlow(): Flow + /** + * @param value the distance in metres the user must move before a new live location is published. + */ suspend fun setLiveLocationMinimumDistanceInMetersUpdate(value: Int) + + /** The minimum distance in metres between two live location updates; defaults to 10. */ fun getLiveLocationMinimumDistanceInMetersUpdateFlow(): Flow + /** + * Only used to clear the local value once it has been migrated to the server. + * + * @param hide the local setting, or `null` to erase it. + */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") suspend fun setHideInviteAvatars(hide: Boolean?) + + /** The local setting to migrate to the server, or `null` once there is nothing left to migrate. */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") fun getHideInviteAvatarsFlow(): Flow + + /** + * Only used to clear the local value once it has been migrated to the server. + * + * @param mediaPreviewValue the local setting, or `null` to erase it. + */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") suspend fun setTimelineMediaPreviewValue(mediaPreviewValue: MediaPreviewValue?) + + /** The local setting to migrate to the server, or `null` once there is nothing left to migrate. */ @Deprecated("Use MediaPreviewService instead. Kept only for migration.") fun getTimelineMediaPreviewValueFlow(): Flow + /** + * @param logLevel how verbose the SDK and app logs should be. + */ suspend fun setTracingLogLevel(logLevel: LogLevel) + + /** The configured log level; defaults to the one the build was compiled with. */ fun getTracingLogLevelFlow(): Flow + /** + * @param targets the extra log packs to enable on top of the default targets. + */ suspend fun setTracingLogPacks(targets: Set) + + /** The extra log packs enabled by the user; defaults to an empty set. */ fun getTracingLogPacksFlow(): Flow> + /** The sound played for message notifications. */ fun getMessageSoundFlow(): Flow /** * Atomically persists [sound] (with copy-time [title] for Custom; cleared otherwise) and * bumps the channel version. Single transaction so process death can't desync URI and version. + * + * @param sound the sound to persist. + * @param title the display name captured when the sound was copied, or `null` for the built-in choices. + * @return the new channel version, to be passed to the channel recreation. */ suspend fun setMessageSoundAndIncrementVersion(sound: NotificationSound, title: String?): Int /** Title captured at copy time. Null for SystemDefault / Silent or pre-title persisted data. */ fun getMessageSoundDisplayNameFlow(): Flow + /** The sound played for incoming call notifications. */ fun getCallRingtoneFlow(): Flow - /** See [setMessageSoundAndIncrementVersion]. */ + /** + * See [setMessageSoundAndIncrementVersion]. + * + * @param sound the ringtone to persist. + * @param title the display name captured when the sound was copied, or `null` for the built-in choices. + * @return the new channel version, to be passed to the channel recreation. + */ suspend fun setCallRingtoneAndIncrementVersion(sound: NotificationSound, title: String?): Int /** See [getMessageSoundDisplayNameFlow]. */ @@ -63,5 +126,6 @@ interface AppPreferencesStore { /** Single-snapshot read of all sound prefs; used at boot to seed channels without N reads. */ suspend fun getNotificationSoundChannelConfig(): NotificationSoundChannelConfig + /** Erases every app preference, so they all fall back to their defaults. */ suspend fun reset() } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt index 50f6eb2431..a3d609e205 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/PreferenceDataStoreFactory.kt @@ -17,5 +17,10 @@ import androidx.datastore.preferences.core.Preferences * It's a wrapper around AndroidX's `PreferenceDataStoreFactory` to make testing easier. */ interface PreferenceDataStoreFactory { + /** + * Creates or opens the preference store with the given name. + * + * @param name the store name, which also determines the file it is persisted to. + */ fun create(name: String): DataStore } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt index d3f3fb4e28..b554fff460 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStore.kt @@ -10,30 +10,77 @@ package io.element.android.libraries.preferences.api.store import kotlinx.coroutines.flow.Flow +/** + * Local, per-session user preferences, stored on the device only and never synced to the homeserver. + * + * Each getter returns a flow that emits the current value straight away and then again on every change. + * The default each preference falls back to when it has never been set is given on the getter. + */ interface SessionPreferencesStore { + /** + * @param enabled true to let the homeserver share this user's presence with others. + */ suspend fun setSharePresence(enabled: Boolean) + + /** Whether presence is shared; defaults to `true`. */ fun isSharePresenceEnabled(): Flow + /** + * @param enabled true to send read receipts that other members can see. + */ suspend fun setSendPublicReadReceipts(enabled: Boolean) + + /** Whether read receipts are sent publicly rather than privately; defaults to `true`. */ fun isSendPublicReadReceiptsEnabled(): Flow + /** + * @param enabled true to display the read receipts of other members in the timeline. + */ suspend fun setRenderReadReceipts(enabled: Boolean) + + /** Whether other members' read receipts are displayed; defaults to `true`. */ fun isRenderReadReceiptsEnabled(): Flow + /** + * @param enabled true to tell other members when this user is typing. + */ suspend fun setSendTypingNotifications(enabled: Boolean) + + /** Whether typing notifications are sent; defaults to `true`. */ fun isSendTypingNotificationsEnabled(): Flow + /** + * @param enabled true to display which other members are typing. + */ suspend fun setRenderTypingNotifications(enabled: Boolean) + + /** Whether other members' typing notifications are displayed; defaults to `true`. */ fun isRenderTypingNotificationsEnabled(): Flow + /** + * @param skip true to remember that the user dismissed the session verification prompt. + */ suspend fun setSkipSessionVerification(skip: Boolean) + + /** Whether the user chose to skip verifying this session, so they are not asked again; defaults to `false`. */ fun isSessionVerificationSkipped(): Flow + /** + * @param compress true to downscale images before uploading them. + */ suspend fun setOptimizeImages(compress: Boolean) + + /** Whether images are downscaled before upload; defaults to `true`. */ fun doesOptimizeImages(): Flow + /** + * @param preset the quality and size trade-off to apply when compressing videos. + */ suspend fun setVideoCompressionPreset(preset: VideoCompressionPreset) + + /** The video compression preset; defaults to [VideoCompressionPreset.STANDARD], including when the stored value is unreadable. */ fun getVideoCompressionPreset(): Flow + /** Erases every preference of this session, so they all fall back to their defaults. */ suspend fun clear() } diff --git a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt index 2c0038d06e..05b4845026 100644 --- a/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt +++ b/libraries/preferences/api/src/main/kotlin/io/element/android/libraries/preferences/api/store/SessionPreferencesStoreFactory.kt @@ -11,7 +11,22 @@ package io.element.android.libraries.preferences.api.store import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.coroutines.CoroutineScope +/** + * Caches one [SessionPreferencesStore] per session, so that every caller of a given session shares the same instance. + */ interface SessionPreferencesStoreFactory { + /** + * Returns the store of a session, creating it on first use. + * + * @param sessionId the session whose store is requested. + * @param sessionCoroutineScope the scope the store uses for its own work, only honoured when the store is actually created. + */ fun get(sessionId: SessionId, sessionCoroutineScope: CoroutineScope): SessionPreferencesStore + + /** + * Drops the cached store of a session, to be called when that session goes away. + * + * @param sessionId the session whose store is dropped. + */ fun remove(sessionId: SessionId) }