From 00169317261222bd7b81d99d27ce9744a65c7667 Mon Sep 17 00:00:00 2001 From: Emma Vandewalle Date: Tue, 23 Jul 2024 19:05:56 +0000 Subject: [PATCH] feat: datastore for settings with repository --- .../writand/data/local/SettingsSerializer.kt | 31 ++++++++ .../writand/data/local/models/UserSettings.kt | 35 +++++++++ .../repos/settings/IUserSettingsRepository.kt | 23 ++++++ .../repos/settings/UserSettingsRepository.kt | 72 +++++++++++++++++++ .../java/be/re/writand/di/DataStoreModule.kt | 31 ++++++++ app/src/main/java/be/re/writand/utils/.keep | 0 .../java/be/re/writand/utils/ObjectMapping.kt | 50 +++++++++++++ app/src/main/proto/settings.proto | 32 +++++++++ 8 files changed, 274 insertions(+) create mode 100644 app/src/main/java/be/re/writand/data/local/SettingsSerializer.kt create mode 100644 app/src/main/java/be/re/writand/data/local/models/UserSettings.kt create mode 100644 app/src/main/java/be/re/writand/data/repos/settings/IUserSettingsRepository.kt create mode 100644 app/src/main/java/be/re/writand/data/repos/settings/UserSettingsRepository.kt create mode 100644 app/src/main/java/be/re/writand/di/DataStoreModule.kt delete mode 100644 app/src/main/java/be/re/writand/utils/.keep create mode 100644 app/src/main/java/be/re/writand/utils/ObjectMapping.kt create mode 100644 app/src/main/proto/settings.proto diff --git a/app/src/main/java/be/re/writand/data/local/SettingsSerializer.kt b/app/src/main/java/be/re/writand/data/local/SettingsSerializer.kt new file mode 100644 index 0000000..3131ccc --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/SettingsSerializer.kt @@ -0,0 +1,31 @@ +package be.re.writand.data.local + +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.Serializer +import be.re.writand.ProtoSettings +import com.google.protobuf.InvalidProtocolBufferException +import java.io.InputStream +import java.io.OutputStream +import javax.inject.Inject + +/** + * Serializer that helps with storing and transferring the data of the UserSettings DataStore. + */ +class SettingsSerializer @Inject constructor() : Serializer { + + override val defaultValue: ProtoSettings = ProtoSettings.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): ProtoSettings { + try { + return ProtoSettings.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } + } + + override suspend fun writeTo( + t: ProtoSettings, + output: OutputStream + ) = t.writeTo(output) + +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/local/models/UserSettings.kt b/app/src/main/java/be/re/writand/data/local/models/UserSettings.kt new file mode 100644 index 0000000..adf09f0 --- /dev/null +++ b/app/src/main/java/be/re/writand/data/local/models/UserSettings.kt @@ -0,0 +1,35 @@ +package be.re.writand.data.local.models + +/** + * Enum class containing the possible languages supported by the app. + */ +enum class UserLanguage { + ENGLISH, + DUTCH +} + +/** + * Enum class containing the possible themes supported by the app. + */ +enum class UserTheme { + DARK, + LIGHT +} + +/** + * Data class that stores all the information of the user settings. + * @param[userLanguage] the language used in the app, by default English. + * @param[userTheme] the theme that is currently used by the user or the default dark theme. + * @param[maxSavedProjects] the amount of different projects to save extra information + * on the local device such as UserSettings and ProjectEntity. + * @param[maxSavedFiles] the amount of open files to be saved per project in local storage + * for when the project is opened again. + * @param[fontSize] font size used in the GUI. + */ +data class UserSettings( + var userLanguage: UserLanguage = UserLanguage.ENGLISH, + var userTheme: UserTheme = UserTheme.DARK, + var maxSavedProjects: Int = 3, + var maxSavedFiles: Int = 5, + var fontSize: Int = 10 +) diff --git a/app/src/main/java/be/re/writand/data/repos/settings/IUserSettingsRepository.kt b/app/src/main/java/be/re/writand/data/repos/settings/IUserSettingsRepository.kt new file mode 100644 index 0000000..905b018 --- /dev/null +++ b/app/src/main/java/be/re/writand/data/repos/settings/IUserSettingsRepository.kt @@ -0,0 +1,23 @@ +package be.re.writand.data.repos.settings + +import be.re.writand.data.local.models.UserLanguage +import be.re.writand.data.local.models.UserSettings +import be.re.writand.data.local.models.UserTheme + +/** + * Repository containing getters and setters to interact with all of the user settings + */ +interface IUserSettingsRepository { + + suspend fun getUserSettings(): UserSettings + + suspend fun setLanguage(userLanguage: UserLanguage) + + suspend fun setTheme(userTheme: UserTheme) + + suspend fun setMaxSavedProjects(maxSaved: Int) + + suspend fun setMaxSavedFiles(maxSaved: Int) + + suspend fun setFontSize(size: Int) +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/data/repos/settings/UserSettingsRepository.kt b/app/src/main/java/be/re/writand/data/repos/settings/UserSettingsRepository.kt new file mode 100644 index 0000000..233e89d --- /dev/null +++ b/app/src/main/java/be/re/writand/data/repos/settings/UserSettingsRepository.kt @@ -0,0 +1,72 @@ +package be.re.writand.data.repos.settings + +import androidx.datastore.core.DataStore +import be.re.writand.ProtoSettings +import be.re.writand.data.local.models.UserLanguage +import be.re.writand.data.local.models.UserSettings +import be.re.writand.data.local.models.UserTheme +import be.re.writand.utils.toProtoLanguage +import be.re.writand.utils.toProtoTheme +import be.re.writand.utils.toUserLanguage +import be.re.writand.utils.toUserTheme +import kotlinx.coroutines.flow.first +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class UserSettingsRepository @Inject constructor( + private val store: DataStore +) : IUserSettingsRepository { + + private val userSettings = store.data + + override suspend fun getUserSettings(): UserSettings { + return UserSettings( + userLanguage = userSettings.first().language.toUserLanguage(), + userTheme = userSettings.first().theme.toUserTheme(), + maxSavedProjects = userSettings.first().maxSavedProjects, + maxSavedFiles = userSettings.first().maxSavedFiles, + fontSize = userSettings.first().fontSize + ) + } + + override suspend fun setLanguage(userLanguage: UserLanguage) { + store.updateData { + val builder = it.toBuilder() + builder.setLanguage(userLanguage.toProtoLanguage()) + builder.build() + } + } + + override suspend fun setTheme(userTheme: UserTheme) { + store.updateData { + val builder = it.toBuilder() + builder.setTheme(userTheme.toProtoTheme()) + builder.build() + } + } + + override suspend fun setMaxSavedProjects(maxSaved: Int) { + store.updateData { + val builder = it.toBuilder() + builder.setMaxSavedProjects(maxSaved) + builder.build() + } + } + + override suspend fun setMaxSavedFiles(maxSaved: Int) { + store.updateData { + val builder = it.toBuilder() + builder.setMaxSavedFiles(maxSaved) + builder.build() + } + } + + override suspend fun setFontSize(size: Int) { + store.updateData { + val builder = it.toBuilder() + builder.setFontSize(size) + builder.build() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/di/DataStoreModule.kt b/app/src/main/java/be/re/writand/di/DataStoreModule.kt new file mode 100644 index 0000000..bb4032b --- /dev/null +++ b/app/src/main/java/be/re/writand/di/DataStoreModule.kt @@ -0,0 +1,31 @@ +package be.re.writand.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.core.DataStoreFactory +import androidx.datastore.dataStoreFile +import be.re.writand.ProtoSettings +import be.re.writand.data.local.SettingsSerializer +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +/** + * Stores data locally, contains the settings information (see file: settings.proto). + */ +@InstallIn(SingletonComponent::class) +@Module +class DataStoreModule { + @Singleton + @Provides + fun provideDataStore( + @ApplicationContext context: Context, + serializer: SettingsSerializer + ): DataStore = + DataStoreFactory.create(serializer = serializer) { + context.dataStoreFile("settings.proto") + } +} \ No newline at end of file diff --git a/app/src/main/java/be/re/writand/utils/.keep b/app/src/main/java/be/re/writand/utils/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/java/be/re/writand/utils/ObjectMapping.kt b/app/src/main/java/be/re/writand/utils/ObjectMapping.kt new file mode 100644 index 0000000..706d068 --- /dev/null +++ b/app/src/main/java/be/re/writand/utils/ObjectMapping.kt @@ -0,0 +1,50 @@ +package be.re.writand.utils + +import be.re.writand.ProtoLanguage +import be.re.writand.ProtoTheme +import be.re.writand.data.local.models.UserLanguage +import be.re.writand.data.local.models.UserTheme + +/** + * Method of ProtoLanguage to get the corresponding UserLanguage. + */ +fun ProtoLanguage.toUserLanguage(): UserLanguage { + return if (this == ProtoLanguage.PROTO_LANGUAGE_ENGLISH) { + UserLanguage.ENGLISH + } else { + UserLanguage.DUTCH + } +} + +/** + * Method of UserLanguage to get the corresponding ProtoLanguage. + */ +fun UserLanguage.toProtoLanguage(): ProtoLanguage { + return if (this == UserLanguage.ENGLISH) { + ProtoLanguage.PROTO_LANGUAGE_ENGLISH + } else { + ProtoLanguage.PROTO_LANGUAGE_DUTCH + } +} + +/** + * Method of ProtoTheme to get the corresponding UserTheme. + */ +fun ProtoTheme.toUserTheme(): UserTheme { + return if (this == ProtoTheme.PROTO_THEME_DARK) { + UserTheme.DARK + } else { + UserTheme.LIGHT + } +} + +/** + * Method of UserTheme to get the corresponding ProtoTheme. + */ +fun UserTheme.toProtoTheme(): ProtoTheme { + return if (this == UserTheme.DARK) { + ProtoTheme.PROTO_THEME_DARK + } else { + ProtoTheme.PROTO_THEME_LIGHT + } +} diff --git a/app/src/main/proto/settings.proto b/app/src/main/proto/settings.proto new file mode 100644 index 0000000..032e8d1 --- /dev/null +++ b/app/src/main/proto/settings.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +option java_package = "be.re.writand"; +option java_multiple_files = true; + +/** + * The available languages supported by the app that will be stored locally. + */ +enum ProtoLanguage { + PROTO_LANGUAGE_ENGLISH = 0; + PROTO_LANGUAGE_DUTCH = 1; +} + +/** + * The available themes supported by the app that will be stored locally. + */ +enum ProtoTheme { + PROTO_THEME_DARK = 0; + PROTO_THEME_LIGHT = 1; +} + +/** + * The structure of what user settings holds, this is stored locally. + * NOTE: the "= x" does not initialize the values of the elements, these are field numbers. + */ +message ProtoSettings { + ProtoLanguage language = 1; + ProtoTheme theme = 2; + uint32 max_saved_projects = 3; + uint32 max_saved_files = 4; + uint32 font_size = 5; +}; \ No newline at end of file