From 2b47b8fce0c12b5f6d15cd11c92f901dbe5d1b71 Mon Sep 17 00:00:00 2001 From: Sungyong An Date: Mon, 20 Jan 2025 19:21:47 +0900 Subject: [PATCH 1/3] Change CoroutineScope of preferences datastore, instead of redispatching --- .../data/settings/impl/AppSettingsImpl.kt | 44 +++++-------------- .../data/settings/impl/di/DataStoreModule.kt | 41 +++++++++++++++++ 2 files changed, 52 insertions(+), 33 deletions(-) create mode 100644 data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt diff --git a/data/settings/impl/src/main/java/soup/movie/data/settings/impl/AppSettingsImpl.kt b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/AppSettingsImpl.kt index 4cdaca6c8..9451cedd3 100644 --- a/data/settings/impl/src/main/java/soup/movie/data/settings/impl/AppSettingsImpl.kt +++ b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/AppSettingsImpl.kt @@ -15,24 +15,17 @@ */ package soup.movie.data.settings.impl -import android.content.Context import androidx.datastore.core.DataStore -import androidx.datastore.preferences.SharedPreferencesMigration import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.intPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey -import androidx.datastore.preferences.preferencesDataStore -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import soup.movie.common.ApplicationScope -import soup.movie.common.IoDispatcher import soup.movie.data.settings.AppSettings import soup.movie.model.settings.AgeFilter import soup.movie.model.settings.TheaterFilter @@ -41,21 +34,10 @@ import javax.inject.Singleton @Singleton class AppSettingsImpl @Inject constructor( - @ApplicationContext private val context: Context, - @IoDispatcher private val ioDispatcher: CoroutineDispatcher, + private val preferences: DataStore, @ApplicationScope private val coroutineScope: CoroutineScope, ) : AppSettings { - private val Context.preferencesName: String - get() = packageName + "_preferences" - - private val Context.dataStore: DataStore by preferencesDataStore( - name = context.preferencesName, - produceMigrations = { context -> - listOf(SharedPreferencesMigration(context, context.preferencesName)) - }, - ) - private val theaterFilterKey = intPreferencesKey("theater_filter") init { @@ -65,13 +47,13 @@ class AppSettingsImpl @Inject constructor( } override suspend fun setTheaterFilter(theaterFilter: TheaterFilter) { - context.dataStore.edit { settings -> + preferences.edit { settings -> settings[theaterFilterKey] = theaterFilter.toFlags() } } override fun getTheaterFilterFlow(): Flow { - return context.dataStore.data.map { preferences -> + return preferences.data.map { preferences -> TheaterFilter( preferences[theaterFilterKey] ?: TheaterFilter.FLAG_THEATER_ALL, @@ -82,13 +64,13 @@ class AppSettingsImpl @Inject constructor( private val ageFilterKey = intPreferencesKey("age_filter") override suspend fun setAgeFilter(ageFilter: AgeFilter) { - context.dataStore.edit { settings -> + preferences.edit { settings -> settings[ageFilterKey] = ageFilter.toFlags() } } override fun getAgeFilterFlow(): Flow { - return context.dataStore.data.map { preferences -> + return preferences.data.map { preferences -> AgeFilter( preferences[ageFilterKey] ?: AgeFilter.FLAG_AGE_DEFAULT, ) @@ -98,10 +80,8 @@ class AppSettingsImpl @Inject constructor( private val themeOptionKey = stringPreferencesKey("theme_option") override suspend fun setThemeOption(themeOption: String) { - withContext(ioDispatcher) { - context.dataStore.edit { settings -> - settings[themeOptionKey] = themeOption - } + preferences.edit { settings -> + settings[themeOptionKey] = themeOption } } @@ -110,17 +90,15 @@ class AppSettingsImpl @Inject constructor( } override fun getThemeOptionFlow(): Flow { - return context.dataStore.data.map { preferences -> + return preferences.data.map { preferences -> preferences[themeOptionKey].orEmpty() } } private suspend fun clearStaleData() { - withContext(ioDispatcher) { - context.dataStore.edit { settings -> - settings.remove(stringPreferencesKey("favorite_theaters")) - settings.remove(stringPreferencesKey("favorite_genre")) - } + preferences.edit { settings -> + settings.remove(stringPreferencesKey("favorite_theaters")) + settings.remove(stringPreferencesKey("favorite_genre")) } } } diff --git a/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt new file mode 100644 index 000000000..b12596cf3 --- /dev/null +++ b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt @@ -0,0 +1,41 @@ +package soup.movie.data.settings.impl.di + +import android.content.Context +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.SharedPreferencesMigration +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.preferencesDataStoreFile +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import soup.movie.common.ApplicationScope +import soup.movie.common.IoDispatcher +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataStoreModule { + + @Provides + @Singleton + fun providesPreferencesDataStore( + @ApplicationContext context: Context, + @IoDispatcher ioDispatcher: CoroutineDispatcher, + @ApplicationScope scope: CoroutineScope, + ): DataStore { + val name = context.packageName + "_preferences" + return PreferenceDataStoreFactory.create( + scope = CoroutineScope(scope.coroutineContext + ioDispatcher), + migrations = listOf( + SharedPreferencesMigration(context = context, sharedPreferencesName = name), + ), + ) { + context.preferencesDataStoreFile(name) + } + } +} From 141f8b6bf7f971617e6fc142c812ad4276799fd7 Mon Sep 17 00:00:00 2001 From: Sungyong An Date: Mon, 20 Jan 2025 19:22:18 +0900 Subject: [PATCH 2/3] Change dispatcher of application scope to default, instead of IO dispatcher --- .../main/java/soup/movie/common/di/CoroutineScopesModule.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/kotlin/src/main/java/soup/movie/common/di/CoroutineScopesModule.kt b/core/kotlin/src/main/java/soup/movie/common/di/CoroutineScopesModule.kt index f92051c15..c3d547b7e 100644 --- a/core/kotlin/src/main/java/soup/movie/common/di/CoroutineScopesModule.kt +++ b/core/kotlin/src/main/java/soup/movie/common/di/CoroutineScopesModule.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob import soup.movie.common.ApplicationScope -import soup.movie.common.MainImmediateDispatcher +import soup.movie.common.DefaultDispatcher import javax.inject.Singleton @Module @@ -34,6 +34,6 @@ object CoroutineScopesModule { @Provides @ApplicationScope fun providesCoroutineScope( - @MainImmediateDispatcher dispatcher: CoroutineDispatcher, + @DefaultDispatcher dispatcher: CoroutineDispatcher, ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher) } From a5da5766be395e8b6cb59b8953cc8afe54542800 Mon Sep 17 00:00:00 2001 From: Sungyong An Date: Mon, 20 Jan 2025 19:29:11 +0900 Subject: [PATCH 3/3] Fix spotless --- .../data/settings/impl/di/DataStoreModule.kt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt index b12596cf3..16db90680 100644 --- a/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt +++ b/data/settings/impl/src/main/java/soup/movie/data/settings/impl/di/DataStoreModule.kt @@ -1,3 +1,18 @@ +/* + * Copyright 2025 SOUP + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package soup.movie.data.settings.impl.di import android.content.Context