diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index c9cc641201..c062a49f84 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -35,7 +35,7 @@ import androidx.test.espresso.Espresso import androidx.test.espresso.NoActivityResumedException import com.google.samples.apps.nowinandroid.MainActivity import com.google.samples.apps.nowinandroid.R -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule import dagger.hilt.android.testing.BindValue diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt index ad95c297f5..8fff6c48b1 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivity.kt @@ -41,10 +41,10 @@ import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt index 09f4597a74..db3dee5b7b 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/MainActivityViewModel.kt @@ -20,7 +20,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Success -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.UserData import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted diff --git a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt index b653d89104..a9cdb626cb 100644 --- a/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt +++ b/app/src/main/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppState.kt @@ -30,9 +30,9 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import androidx.navigation.navOptions import androidx.tracing.trace -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.ui.TrackDisposableJank import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.BOOKMARKS_ROUTE import com.google.samples.apps.nowinandroid.feature.bookmarks.navigation.navigateToBookmarks diff --git a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt index 83ca1bb3dc..d68cbc26a3 100644 --- a/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt +++ b/app/src/testDemo/kotlin/com/google/samples/apps/nowinandroid/ui/NiaAppScreenSizesScreenshotTests.kt @@ -33,12 +33,12 @@ import androidx.work.testing.SynchronousExecutor import androidx.work.testing.WorkManagerTestInitHelper import com.github.takahirom.roborazzi.captureRoboImage import com.google.accompanist.testharness.TestHarness -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.testing.util.DefaultRoborazziOptions import com.google.samples.apps.nowinandroid.uitesthiltmanifest.HiltComponentActivity import dagger.hilt.android.testing.BindValue diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt index 46158479c9..4ebd776464 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/TestDataModule.kt @@ -17,11 +17,6 @@ package com.google.samples.apps.nowinandroid.core.data.test import com.google.samples.apps.nowinandroid.core.data.di.DataModule -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeNewsRepository import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeRecentSearchRepository import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeSearchContentsRepository @@ -29,6 +24,11 @@ import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeTopics import com.google.samples.apps.nowinandroid.core.data.test.repository.FakeUserDataRepository import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import dagger.Binds import dagger.Module import dagger.hilt.components.SingletonComponent diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt index 5ceff4dd0b..2a3c76dba9 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt @@ -16,12 +16,12 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.model.asEntity -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt index b8d949efed..c1aece1935 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeRecentSearchRepository.kt @@ -16,8 +16,8 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository -import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import javax.inject.Inject diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt index 1feeb6dcc6..ae99efbdb9 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeSearchContentsRepository.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt index f8ebca29ef..7221501c23 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeTopicsRepository.kt @@ -16,8 +16,8 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt index cdd23429fc..e86e8fcdf1 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeUserDataRepository.kt @@ -16,8 +16,8 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts index 142637ff9c..e047c0e873 100644 --- a/core/data/build.gradle.kts +++ b/core/data/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { api(projects.core.database) api(projects.core.datastore) api(projects.core.network) + api(projects.core.domain) implementation(projects.core.analytics) implementation(projects.core.notifications) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt deleted file mode 100644 index 5d069dbafb..0000000000 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/SyncUtilities.kt +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * 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 com.google.samples.apps.nowinandroid.core.data - -import android.util.Log -import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions -import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlin.coroutines.cancellation.CancellationException - -/** - * Interface marker for a class that manages synchronization between local data and a remote - * source for a [Syncable]. - */ -interface Synchronizer { - suspend fun getChangeListVersions(): ChangeListVersions - - suspend fun updateChangeListVersions(update: ChangeListVersions.() -> ChangeListVersions) - - /** - * Syntactic sugar to call [Syncable.syncWith] while omitting the synchronizer argument - */ - suspend fun Syncable.sync() = this@sync.syncWith(this@Synchronizer) -} - -/** - * Interface marker for a class that is synchronized with a remote source. Syncing must not be - * performed concurrently and it is the [Synchronizer]'s responsibility to ensure this. - */ -interface Syncable { - /** - * Synchronizes the local database backing the repository with the network. - * Returns if the sync was successful or not. - */ - suspend fun syncWith(synchronizer: Synchronizer): Boolean -} - -/** - * Attempts [block], returning a successful [Result] if it succeeds, otherwise a [Result.Failure] - * taking care not to break structured concurrency - */ -private suspend fun suspendRunCatching(block: suspend () -> T): Result = try { - Result.success(block()) -} catch (cancellationException: CancellationException) { - throw cancellationException -} catch (exception: Exception) { - Log.i( - "suspendRunCatching", - "Failed to evaluate a suspendRunCatchingBlock. Returning failure Result", - exception, - ) - Result.failure(exception) -} - -/** - * Utility function for syncing a repository with the network. - * [versionReader] Reads the current version of the model that needs to be synced - * [changeListFetcher] Fetches the change list for the model - * [versionUpdater] Updates the [ChangeListVersions] after a successful sync - * [modelDeleter] Deletes models by consuming the ids of the models that have been deleted. - * [modelUpdater] Updates models by consuming the ids of the models that have changed. - * - * Note that the blocks defined above are never run concurrently, and the [Synchronizer] - * implementation must guarantee this. - */ -suspend fun Synchronizer.changeListSync( - versionReader: (ChangeListVersions) -> Int, - changeListFetcher: suspend (Int) -> List, - versionUpdater: ChangeListVersions.(Int) -> ChangeListVersions, - modelDeleter: suspend (List) -> Unit, - modelUpdater: suspend (List) -> Unit, -) = suspendRunCatching { - // Fetch the change list since last sync (akin to a git fetch) - val currentVersion = versionReader(getChangeListVersions()) - val changeList = changeListFetcher(currentVersion) - if (changeList.isEmpty()) return@suspendRunCatching true - - val (deleted, updated) = changeList.partition(NetworkChangeList::isDelete) - - // Delete models that have been deleted server-side - modelDeleter(deleted.map(NetworkChangeList::id)) - - // Using the change list, pull down and save the changes (akin to a git pull) - modelUpdater(updated.map(NetworkChangeList::id)) - - // Update the last synced version (akin to updating local git HEAD) - val latestVersion = changeList.last().changeListVersion - updateChangeListVersions { - versionUpdater(latestVersion) - } -}.isSuccess - -/** - * Returns a [Flow] whose values are generated by [transform] function that process the most - * recently emitted values by each flow. - */ -fun combine( - flow: Flow, - flow2: Flow, - flow3: Flow, - flow4: Flow, - flow5: Flow, - flow6: Flow, - transform: suspend (T1, T2, T3, T4, T5, T6) -> R, -): Flow = combine( - combine(flow, flow2, flow3, ::Triple), - combine(flow4, flow5, flow6, ::Triple), -) { t1, t2 -> - transform( - t1.first, - t1.second, - t1.third, - t2.first, - t2.second, - t2.third, - ) -} diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt index fa4bde8b83..422fa5f3a6 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/DataModule.kt @@ -18,18 +18,18 @@ package com.google.samples.apps.nowinandroid.core.data.di import com.google.samples.apps.nowinandroid.core.data.repository.DefaultRecentSearchRepository import com.google.samples.apps.nowinandroid.core.data.repository.DefaultSearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.data.repository.OfflineFirstNewsRepository import com.google.samples.apps.nowinandroid.core.data.repository.OfflineFirstTopicsRepository import com.google.samples.apps.nowinandroid.core.data.repository.OfflineFirstUserDataRepository -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.data.util.ConnectivityManagerNetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneBroadcastMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt index 7f4e27b413..32dded0d70 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/di/UserNewsResourceRepositoryModule.kt @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.core.data.di import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import dagger.Binds import dagger.Module import dagger.hilt.InstallIn diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt index c3ad91dfe3..a74ad72418 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt index 76dd088113..c909dc7d2f 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/RecentSearchQuery.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,15 +17,10 @@ package com.google.samples.apps.nowinandroid.core.data.model import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery -data class RecentSearchQuery( - val query: String, - val queriedDate: Instant = Clock.System.now(), -) - -fun RecentSearchQueryEntity.asExternalModel() = RecentSearchQuery( - query = query, - queriedDate = queriedDate, -) +fun RecentSearchQueryEntity.asExternalModel() = + RecentSearchQuery( + query = query, + queriedDate = queriedDate, + ) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt index 188b1e06d0..ccebbf81e2 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/Topic.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt index 64e02e7d94..5f6fc36ddd 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/CompositeUserNewsResourceRepository.kt @@ -16,6 +16,10 @@ package com.google.samples.apps.nowinandroid.core.data.repository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources import kotlinx.coroutines.flow.Flow diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt index 32239362d0..e518bbeb7a 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultRecentSearchRepository.kt @@ -16,10 +16,11 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.datetime.Clock diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt index 3bacb8a147..264d06dbdb 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/DefaultSearchContentsRepository.kt @@ -23,6 +23,7 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt index d33c904e5b..492328e92d 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepository.kt @@ -16,18 +16,20 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.changeListSync import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.model.topicCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.topicEntityShells +import com.google.samples.apps.nowinandroid.core.data.util.changeListSync import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel -import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt index 5c8cecce8b..763da65a33 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepository.kt @@ -16,13 +16,14 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.changeListSync import com.google.samples.apps.nowinandroid.core.data.model.asEntity +import com.google.samples.apps.nowinandroid.core.data.util.changeListSync import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel -import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt index c0b1bcc333..22531feb39 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepository.kt @@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.core.data.repository import androidx.annotation.VisibleForTesting import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt index d72fa27a6e..558a4b16a1 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/SyncManager.kt @@ -16,7 +16,12 @@ package com.google.samples.apps.nowinandroid.core.data.util +import android.util.Log +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions +import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import kotlinx.coroutines.flow.Flow +import kotlin.coroutines.cancellation.CancellationException /** * Reports on if synchronization is in progress @@ -25,3 +30,58 @@ interface SyncManager { val isSyncing: Flow fun requestSync() } + +/** + * Attempts [block], returning a successful [Result] if it succeeds, otherwise a [Result.Failure] + * taking care not to break structured concurrency + */ +private suspend fun suspendRunCatching(block: suspend () -> T): Result = try { + Result.success(block()) +} catch (cancellationException: CancellationException) { + throw cancellationException +} catch (exception: Exception) { + Log.i( + "suspendRunCatching", + "Failed to evaluate a suspendRunCatchingBlock. Returning failure Result", + exception, + ) + Result.failure(exception) +} + +/** + * Utility function for syncing a repository with the network. + * [versionReader] Reads the current version of the model that needs to be synced + * [changeListFetcher] Fetches the change list for the model + * [versionUpdater] Updates the [ChangeListVersions] after a successful sync + * [modelDeleter] Deletes models by consuming the ids of the models that have been deleted. + * [modelUpdater] Updates models by consuming the ids of the models that have changed. + * + * Note that the blocks defined above are never run concurrently, and the [Synchronizer] + * implementation must guarantee this. + */ +suspend fun Synchronizer.changeListSync( + versionReader: (ChangeListVersions) -> Int, + changeListFetcher: suspend (Int) -> List, + versionUpdater: ChangeListVersions.(Int) -> ChangeListVersions, + modelDeleter: suspend (List) -> Unit, + modelUpdater: suspend (List) -> Unit, +) = suspendRunCatching { + // Fetch the change list since last sync (akin to a git fetch) + val currentVersion = versionReader(getChangeListVersions()) + val changeList = changeListFetcher(currentVersion) + if (changeList.isEmpty()) return@suspendRunCatching true + + val (deleted, updated) = changeList.partition(NetworkChangeList::isDelete) + + // Delete models that have been deleted server-side + modelDeleter(deleted.map(NetworkChangeList::id)) + + // Using the change list, pull down and save the changes (akin to a git pull) + modelUpdater(updated.map(NetworkChangeList::id)) + + // Update the last synced version (akin to updating local git HEAD) + val latestVersion = changeList.last().changeListVersion + updateChangeListVersions { + versionUpdater(latestVersion) + } +}.isSuccess diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt index 05811f4be1..2abe9fda1f 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/CompositeUserNewsResourceRepositoryTest.kt @@ -17,7 +17,7 @@ package com.google.samples.apps.nowinandroid.core.data import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt index c7dfd99d08..2e2b0be140 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/UserNewsResourceTest.kt @@ -23,7 +23,7 @@ import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource -import kotlinx.datetime.Clock +import kotlinx.datetime.Clock.System import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test @@ -43,7 +43,7 @@ class UserNewsResourceTest { content = "Test news content", url = "Test URL", headerImageUrl = "Test image URL", - publishDate = Clock.System.now(), + publishDate = System.now(), type = "Article 📚", topics = listOf( Topic( @@ -75,7 +75,10 @@ class UserNewsResourceTest { shouldHideOnboarding = true, ) - val userNewsResource = UserNewsResource(newsResource1, userData) + val userNewsResource = UserNewsResource( + newsResource1, + userData, + ) // Check that the simple field mappings have been done correctly. assertEquals(newsResource1.id, userNewsResource.id) diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt index 47c3996c44..2f14fba211 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstNewsRepositoryTest.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.model.topicCrossReferences import com.google.samples.apps.nowinandroid.core.data.model.topicEntityShells @@ -33,6 +32,8 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt index 3bd314eae5..f2d2043650 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstTopicsRepositoryTest.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.testdoubles.CollectionType import com.google.samples.apps.nowinandroid.core.data.testdoubles.TestNiaNetworkDataSource @@ -26,6 +25,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import kotlinx.coroutines.flow.first diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt index 27e86f2f4c..351eef382f 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/OfflineFirstUserDataRepositoryTest.kt @@ -20,7 +20,9 @@ import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.data.UserData import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -68,8 +70,8 @@ class OfflineFirstUserDataRepositoryTest { bookmarkedNewsResources = emptySet(), viewedNewsResources = emptySet(), followedTopics = emptySet(), - themeBrand = ThemeBrand.DEFAULT, - darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + themeBrand = DEFAULT, + darkThemeConfig = FOLLOW_SYSTEM, useDynamicColor = false, shouldHideOnboarding = false, ), diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt index 270dd010e3..0229983a16 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TestSynchronizer.kt @@ -16,9 +16,9 @@ package com.google.samples.apps.nowinandroid.core.data.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions /** * Test synchronizer that delegates to [NiaPreferencesDataSource] diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 4a6bcb66af..d972da6d47 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -30,7 +30,7 @@ android { } dependencies { - api(projects.core.model) + implementation(projects.core.model) implementation(libs.kotlinx.datetime) diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt index 929b88ce60..cdc8976e8d 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt @@ -25,7 +25,6 @@ import androidx.room.Upsert import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource -import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import kotlinx.coroutines.flow.Flow /** diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt index 9450a24ad6..be2d472253 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt @@ -41,13 +41,14 @@ data class NewsResourceEntity( val type: String, ) -fun NewsResourceEntity.asExternalModel() = NewsResource( - id = id, - title = title, - content = content, - url = url, - headerImageUrl = headerImageUrl, - publishDate = publishDate, - type = type, - topics = listOf(), -) +fun NewsResourceEntity.asExternalModel() = + NewsResource( + id = id, + title = title, + content = content, + url = url, + headerImageUrl = headerImageUrl, + publishDate = publishDate, + type = type, + topics = listOf(), + ) diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt index a703424012..dd29730c3c 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt @@ -39,16 +39,17 @@ data class PopulatedNewsResource( val topics: List, ) -fun PopulatedNewsResource.asExternalModel() = NewsResource( - id = entity.id, - title = entity.title, - content = entity.content, - url = entity.url, - headerImageUrl = entity.headerImageUrl, - publishDate = entity.publishDate, - type = entity.type, - topics = topics.map(TopicEntity::asExternalModel), -) +fun PopulatedNewsResource.asExternalModel() = + NewsResource( + id = entity.id, + title = entity.title, + content = entity.content, + url = entity.url, + headerImageUrl = entity.headerImageUrl, + publishDate = entity.publishDate, + type = entity.type, + topics = topics.map(TopicEntity::asExternalModel), + ) fun PopulatedNewsResource.asFtsEntity() = NewsResourceFtsEntity( newsResourceId = entity.id, diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt index 9a76a75a17..3bd7f6c6f3 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSource.kt @@ -18,6 +18,15 @@ package com.google.samples.apps.nowinandroid.core.datastore import android.util.Log import androidx.datastore.core.DataStore +import com.google.samples.apps.nowinandroid.core.datastore.DarkThemeConfigProto.DARK_THEME_CONFIG_DARK +import com.google.samples.apps.nowinandroid.core.datastore.DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM +import com.google.samples.apps.nowinandroid.core.datastore.DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT +import com.google.samples.apps.nowinandroid.core.datastore.DarkThemeConfigProto.DARK_THEME_CONFIG_UNSPECIFIED +import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_ANDROID +import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_DEFAULT +import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_UNSPECIFIED +import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.UNRECOGNIZED +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.data.UserData @@ -37,22 +46,25 @@ class NiaPreferencesDataSource @Inject constructor( followedTopics = it.followedTopicIdsMap.keys, themeBrand = when (it.themeBrand) { null, - ThemeBrandProto.THEME_BRAND_UNSPECIFIED, - ThemeBrandProto.UNRECOGNIZED, - ThemeBrandProto.THEME_BRAND_DEFAULT, + THEME_BRAND_UNSPECIFIED, + UNRECOGNIZED, + THEME_BRAND_DEFAULT, -> ThemeBrand.DEFAULT - ThemeBrandProto.THEME_BRAND_ANDROID -> ThemeBrand.ANDROID + + THEME_BRAND_ANDROID -> ThemeBrand.ANDROID }, darkThemeConfig = when (it.darkThemeConfig) { null, - DarkThemeConfigProto.DARK_THEME_CONFIG_UNSPECIFIED, + DARK_THEME_CONFIG_UNSPECIFIED, DarkThemeConfigProto.UNRECOGNIZED, - DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM, + DARK_THEME_CONFIG_FOLLOW_SYSTEM, -> DarkThemeConfig.FOLLOW_SYSTEM - DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT -> + + DARK_THEME_CONFIG_LIGHT -> DarkThemeConfig.LIGHT - DarkThemeConfigProto.DARK_THEME_CONFIG_DARK -> DarkThemeConfig.DARK + + DARK_THEME_CONFIG_DARK -> DarkThemeConfig.DARK }, useDynamicColor = it.useDynamicColor, shouldHideOnboarding = it.shouldHideOnboarding, diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts index 1918774597..2cd419736c 100644 --- a/core/domain/build.gradle.kts +++ b/core/domain/build.gradle.kts @@ -16,7 +16,7 @@ plugins { alias(libs.plugins.nowinandroid.android.library) alias(libs.plugins.nowinandroid.android.library.jacoco) - id("com.google.devtools.ksp") + alias(libs.plugins.nowinandroid.android.hilt) } android { @@ -24,8 +24,8 @@ android { } dependencies { - api(projects.core.data) - api(projects.core.model) + api(libs.kotlinx.datetime) + implementation(projects.core.model) implementation(libs.javax.inject) diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/NewsRepository.kt similarity index 88% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/NewsRepository.kt index 0e53f1239c..23f5a99771 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/NewsRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/NewsRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository -import com.google.samples.apps.nowinandroid.core.data.Syncable +import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import kotlinx.coroutines.flow.Flow diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/RecentSearchRepository.kt similarity index 86% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/RecentSearchRepository.kt index 87a2ce9dc0..bd406fac2a 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/RecentSearchRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/RecentSearchRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository -import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery import kotlinx.coroutines.flow.Flow /** diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/SearchContentsRepository.kt similarity index 90% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/SearchContentsRepository.kt index 2fe6bd8205..9872c9305d 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/SearchContentsRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/SearchContentsRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import kotlinx.coroutines.flow.Flow diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/TopicsRepository.kt similarity index 82% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/TopicsRepository.kt index fcf875cddc..08d0773539 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/TopicsRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/TopicsRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository -import com.google.samples.apps.nowinandroid.core.data.Syncable +import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable import com.google.samples.apps.nowinandroid.core.model.data.Topic import kotlinx.coroutines.flow.Flow diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserDataRepository.kt similarity index 94% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserDataRepository.kt index ff616c1790..dae921d568 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserDataRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserDataRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt similarity index 91% rename from core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt index c0f4c013a0..c60cac4c29 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/UserNewsResourceRepository.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/repository/UserNewsResourceRepository.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.data.repository +package com.google.samples.apps.nowinandroid.core.domain.repository import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.coroutines.flow.Flow diff --git a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetFollowableTopicsUseCase.kt similarity index 79% rename from core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetFollowableTopicsUseCase.kt index 0167a31921..dd9aafe0a8 100644 --- a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCase.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetFollowableTopicsUseCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.domain.usecase -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NAME -import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NONE +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME +import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NONE import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine diff --git a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetRecentSearchQueriesUseCase.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetRecentSearchQueriesUseCase.kt similarity index 78% rename from core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetRecentSearchQueriesUseCase.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetRecentSearchQueriesUseCase.kt index 51f87d6fdd..dc5624a8b3 100644 --- a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetRecentSearchQueriesUseCase.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetRecentSearchQueriesUseCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.domain.usecase -import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery import kotlinx.coroutines.flow.Flow import javax.inject.Inject diff --git a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsCountUseCase.kt similarity index 81% rename from core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsCountUseCase.kt index 3e3e1952e1..b6628955e8 100644 --- a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsCountUseCase.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsCountUseCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.domain.usecase -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import kotlinx.coroutines.flow.Flow import javax.inject.Inject diff --git a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsUseCase.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsUseCase.kt similarity index 88% rename from core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsUseCase.kt rename to core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsUseCase.kt index d1065e87cc..f5fc1d8e98 100644 --- a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetSearchContentsUseCase.kt +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/usecase/GetSearchContentsUseCase.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,10 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.domain +package com.google.samples.apps.nowinandroid.core.domain.usecase -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.model.data.UserData diff --git a/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/utils/SyncUtilities.kt b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/utils/SyncUtilities.kt new file mode 100644 index 0000000000..1f9d2eafd7 --- /dev/null +++ b/core/domain/src/main/kotlin/com/google/samples/apps/nowinandroid/core/domain/utils/SyncUtilities.kt @@ -0,0 +1,74 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * 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 com.google.samples.apps.nowinandroid.core.domain.utils + +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** + * Interface marker for a class that manages synchronization between local data and a remote + * source for a [Syncable]. + */ +interface Synchronizer { + suspend fun getChangeListVersions(): ChangeListVersions + + suspend fun updateChangeListVersions(update: ChangeListVersions.() -> ChangeListVersions) + + /** + * Syntactic sugar to call [Syncable.syncWith] while omitting the synchronizer argument + */ + suspend fun Syncable.sync() = this@sync.syncWith(this@Synchronizer) +} + +/** + * Interface marker for a class that is synchronized with a remote source. Syncing must not be + * performed concurrently and it is the [Synchronizer]'s responsibility to ensure this. + */ +interface Syncable { + /** + * Synchronizes the local database backing the repository with the network. + * Returns if the sync was successful or not. + */ + suspend fun syncWith(synchronizer: Synchronizer): Boolean +} + +/** + * Returns a [Flow] whose values are generated by [transform] function that process the most + * recently emitted values by each flow. + */ +fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + transform: suspend (T1, T2, T3, T4, T5, T6) -> R, +): Flow = combine( + combine(flow, flow2, flow3, ::Triple), + combine(flow4, flow5, flow6, ::Triple), +) { t1, t2 -> + transform( + t1.first, + t1.second, + t1.third, + t2.first, + t2.second, + t2.third, + ) +} diff --git a/core/domain/src/test/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt b/core/domain/src/test/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt index 42a31f8580..fce02a86b8 100644 --- a/core/domain/src/test/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt +++ b/core/domain/src/test/kotlin/com/google/samples/apps/nowinandroid/core/domain/GetFollowableTopicsUseCaseTest.kt @@ -16,7 +16,8 @@ package com.google.samples.apps.nowinandroid.core.domain -import com.google.samples.apps.nowinandroid.core.domain.TopicSortField.NAME +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository @@ -53,9 +54,18 @@ class GetFollowableTopicsUseCaseTest { // Check that the order hasn't changed and that the correct topics are marked as followed. assertEquals( listOf( - FollowableTopic(testTopics[0], true), - FollowableTopic(testTopics[1], false), - FollowableTopic(testTopics[2], true), + FollowableTopic( + testTopics[0], + true, + ), + FollowableTopic( + testTopics[1], + false, + ), + FollowableTopic( + testTopics[2], + true, + ), ), followableTopics.first(), ) diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ChangeListVersions.kt similarity index 86% rename from core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt rename to core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ChangeListVersions.kt index 8b91b43a1c..643ceb72e1 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ChangeListVersions.kt +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ChangeListVersions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.samples.apps.nowinandroid.core.datastore +package com.google.samples.apps.nowinandroid.core.model.data /** * Class summarizing the local version of each model for sync diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/RecentSearchQuery.kt b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/RecentSearchQuery.kt new file mode 100644 index 0000000000..83310872c2 --- /dev/null +++ b/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/RecentSearchQuery.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * 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 com.google.samples.apps.nowinandroid.core.model.data + +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant + +data class RecentSearchQuery( + val query: String, + val queriedDate: Instant = Clock.System.now(), +) diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt index 89af19c990..f5dac9928c 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt @@ -16,7 +16,6 @@ package com.google.samples.apps.nowinandroid.core.network.model -import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import kotlinx.datetime.Instant import kotlinx.serialization.Serializable diff --git a/core/notifications/build.gradle.kts b/core/notifications/build.gradle.kts index 92871b72be..7fbc98c826 100644 --- a/core/notifications/build.gradle.kts +++ b/core/notifications/build.gradle.kts @@ -23,8 +23,7 @@ android { } dependencies { - api(projects.core.model) - + implementation(projects.core.model) implementation(projects.core.common) compileOnly(platform(libs.androidx.compose.bom)) diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts index 02729ceffc..878f89ae43 100644 --- a/core/testing/build.gradle.kts +++ b/core/testing/build.gradle.kts @@ -28,7 +28,7 @@ dependencies { api(libs.androidx.compose.ui.test) api(projects.core.analytics) api(projects.core.data) - api(projects.core.model) + api(projects.core.domain) api(projects.core.notifications) debugApi(libs.androidx.compose.ui.testManifest) diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt index 4174391df7..418b4abc93 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/data/UserNewsResourcesTestData.kt @@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.core.testing.data -import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.data.NewsResource -import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import kotlinx.datetime.Instant @@ -32,8 +32,8 @@ val userNewsResourcesTestData: List = UserData( bookmarkedNewsResources = setOf("1", "4"), viewedNewsResources = setOf("1", "2", "4"), followedTopics = emptySet(), - themeBrand = ThemeBrand.ANDROID, - darkThemeConfig = DarkThemeConfig.DARK, + themeBrand = ANDROID, + darkThemeConfig = DARK, shouldHideOnboarding = true, useDynamicColor = false, ).let { userData -> diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt index ef065a9f8e..b484a8fd8a 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestNewsRepository.kt @@ -16,9 +16,9 @@ package com.google.samples.apps.nowinandroid.core.testing.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.Topic import kotlinx.coroutines.channels.BufferOverflow diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt index f700bdc310..ed2d0daf63 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestRecentSearchRepository.kt @@ -16,8 +16,8 @@ package com.google.samples.apps.nowinandroid.core.testing.repository -import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.model.data.RecentSearchQuery import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -29,7 +29,11 @@ class TestRecentSearchRepository : RecentSearchRepository { flowOf(cachedRecentSearches.sortedByDescending { it.queriedDate }.take(limit)) override suspend fun insertOrReplaceRecentSearch(searchQuery: String) { - cachedRecentSearches.add(RecentSearchQuery(searchQuery)) + cachedRecentSearches.add( + RecentSearchQuery( + searchQuery, + ), + ) } override suspend fun clearRecentSearches() = cachedRecentSearches.clear() diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt index 5436cd10f7..bd5de9c24a 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestSearchContentsRepository.kt @@ -16,7 +16,7 @@ package com.google.samples.apps.nowinandroid.core.testing.repository -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.model.data.Topic diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt index ddccbbe35e..ee3b85b78c 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestTopicsRepository.kt @@ -16,8 +16,8 @@ package com.google.samples.apps.nowinandroid.core.testing.repository -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.model.data.Topic import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow diff --git a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt index 504e792170..97b9fe20ad 100644 --- a/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt +++ b/core/testing/src/main/kotlin/com/google/samples/apps/nowinandroid/core/testing/repository/TestUserDataRepository.kt @@ -16,9 +16,11 @@ package com.google.samples.apps.nowinandroid.core.testing.repository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig +import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand +import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.data.UserData import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST import kotlinx.coroutines.flow.Flow @@ -29,8 +31,8 @@ val emptyUserData = UserData( bookmarkedNewsResources = emptySet(), viewedNewsResources = emptySet(), followedTopics = emptySet(), - themeBrand = ThemeBrand.DEFAULT, - darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + themeBrand = DEFAULT, + darkThemeConfig = FOLLOW_SYSTEM, useDynamicColor = false, shouldHideOnboarding = false, ) diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt index 3189e84030..5994738972 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/UserNewsResourcePreviewParameterProvider.kt @@ -42,15 +42,16 @@ class UserNewsResourcePreviewParameterProvider : PreviewParameterProvider = sequenceOf( SearchResultUiState.Success( topics = topics.mapIndexed { i, topic -> - FollowableTopic(topic = topic, isFollowed = i % 2 == 0) + FollowableTopic( + topic = topic, + isFollowed = i % 2 == 0, + ) }, newsResources = newsResources, ), diff --git a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt index 7c05f81c52..d2bf027206 100644 --- a/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt +++ b/feature/search/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModel.kt @@ -22,10 +22,10 @@ import androidx.lifecycle.viewModelScope import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent.Param import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper -import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository -import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase +import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetRecentSearchQueriesUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsCountUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.SharingStarted diff --git a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt index da0d5654e8..da4cce004c 100644 --- a/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt +++ b/feature/search/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/search/SearchViewModelTest.kt @@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.feature.search import androidx.lifecycle.SavedStateHandle import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper -import com.google.samples.apps.nowinandroid.core.domain.GetRecentSearchQueriesUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsCountUseCase -import com.google.samples.apps.nowinandroid.core.domain.GetSearchContentsUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetRecentSearchQueriesUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsCountUseCase +import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsUseCase import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.data.topicsTestData import com.google.samples.apps.nowinandroid.core.testing.repository.TestRecentSearchRepository diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt index 123c84d1c6..2a712b1f68 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModel.kt @@ -18,7 +18,7 @@ package com.google.samples.apps.nowinandroid.feature.settings import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.model.data.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt index 9fe6a2dd26..dc98bb7ac6 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModel.kt @@ -19,10 +19,10 @@ package com.google.samples.apps.nowinandroid.feature.topic import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository -import com.google.samples.apps.nowinandroid.core.data.repository.UserNewsResourceRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource diff --git a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt index ea5f36042e..52e333b741 100644 --- a/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt +++ b/sync/work/src/main/kotlin/com/google/samples/apps/nowinandroid/sync/workers/SyncWorker.kt @@ -25,12 +25,12 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.OutOfQuotaPolicy import androidx.work.WorkerParameters import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper -import com.google.samples.apps.nowinandroid.core.data.Synchronizer -import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.SearchContentsRepository -import com.google.samples.apps.nowinandroid.core.data.repository.TopicsRepository -import com.google.samples.apps.nowinandroid.core.datastore.ChangeListVersions import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource +import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository +import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository +import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer +import com.google.samples.apps.nowinandroid.core.model.data.ChangeListVersions import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.sync.initializers.SyncConstraints