diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/BookmarksBaselineProfile.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/BookmarksBaselineProfile.kt index eca3f059b..7f9406bf5 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/BookmarksBaselineProfile.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/BookmarksBaselineProfile.kt @@ -30,11 +30,10 @@ class BookmarksBaselineProfile { @get:Rule val baselineProfileRule = BaselineProfileRule() @Test - fun generate() = - baselineProfileRule.collect(PACKAGE_NAME) { - startActivityAndAllowNotifications() + fun generate() = baselineProfileRule.collect(PACKAGE_NAME) { + startActivityAndAllowNotifications() - // Navigate to saved screen - goToBookmarksScreen() - } + // Navigate to saved screen + goToBookmarksScreen() + } } diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/ForYouBaselineProfile.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/ForYouBaselineProfile.kt index e8722e116..9b7c3e9fa 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/ForYouBaselineProfile.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/ForYouBaselineProfile.kt @@ -32,13 +32,12 @@ class ForYouBaselineProfile { @get:Rule val baselineProfileRule = BaselineProfileRule() @Test - fun generate() = - baselineProfileRule.collect(PACKAGE_NAME) { - startActivityAndAllowNotifications() + fun generate() = baselineProfileRule.collect(PACKAGE_NAME) { + startActivityAndAllowNotifications() - // Scroll the feed critical user journey - forYouWaitForContent() - forYouSelectTopics(true) - forYouScrollFeedDownUp() - } + // Scroll the feed critical user journey + forYouWaitForContent() + forYouSelectTopics(true) + forYouScrollFeedDownUp() + } } diff --git a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/InterestsBaselineProfile.kt b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/InterestsBaselineProfile.kt index dd2166dc2..bd6af8b64 100644 --- a/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/InterestsBaselineProfile.kt +++ b/benchmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/baselineprofile/InterestsBaselineProfile.kt @@ -31,12 +31,11 @@ class InterestsBaselineProfile { @get:Rule val baselineProfileRule = BaselineProfileRule() @Test - fun generate() = - baselineProfileRule.collect(PACKAGE_NAME) { - startActivityAndAllowNotifications() + fun generate() = baselineProfileRule.collect(PACKAGE_NAME) { + startActivityAndAllowNotifications() - // Navigate to interests screen - goToInterestsScreen() - interestsScrollTopicsDownUp() - } + // Navigate to interests screen + goToInterestsScreen() + interestsScrollTopicsDownUp() + } } diff --git a/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt b/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt index 6e7ca6bb3..f67f11cae 100644 --- a/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt +++ b/core/common/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/CoroutineScopesModule.kt @@ -22,11 +22,11 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import javax.inject.Qualifier +import javax.inject.Singleton import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.SupervisorJob -import javax.inject.Qualifier -import javax.inject.Singleton @Retention(AnnotationRetention.RUNTIME) @Qualifier diff --git a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt index 2c3c7b763..d7ff05297 100644 --- a/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt +++ b/core/common/src/test/kotlin/com/google/samples/apps/nowinandroid/core/result/ResultKtTest.kt @@ -17,10 +17,10 @@ package com.google.samples.apps.nowinandroid.core.result import app.cash.turbine.test +import kotlin.test.assertEquals import kotlinx.coroutines.flow.flow import kotlinx.coroutines.test.runTest import org.junit.Test -import kotlin.test.assertEquals class ResultKtTest { diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/AlwaysOnlineNetworkMonitor.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/AlwaysOnlineNetworkMonitor.kt index c00c99ded..91e47b688 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/AlwaysOnlineNetworkMonitor.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/AlwaysOnlineNetworkMonitor.kt @@ -17,9 +17,9 @@ package com.google.samples.apps.nowinandroid.core.data.test import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject class AlwaysOnlineNetworkMonitor @Inject constructor() : NetworkMonitor { override val isOnline: Flow = flowOf(true) diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt index 5a21ae337..34419a2f8 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/DefaultZoneIdTimeZoneMonitor.kt @@ -17,10 +17,10 @@ package com.google.samples.apps.nowinandroid.core.data.test import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf import kotlinx.datetime.TimeZone -import javax.inject.Inject class DefaultZoneIdTimeZoneMonitor @Inject constructor() : TimeZoneMonitor { override val currentTimeZone: Flow = flowOf(TimeZone.of("Europe/Warsaw")) 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 46158479c..9911171a7 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 @@ -41,19 +41,13 @@ import dagger.hilt.testing.TestInstallIn ) internal interface TestDataModule { @Binds - fun bindsTopicRepository( - fakeTopicsRepository: FakeTopicsRepository, - ): TopicsRepository + fun bindsTopicRepository(fakeTopicsRepository: FakeTopicsRepository): TopicsRepository @Binds - fun bindsNewsResourceRepository( - fakeNewsRepository: FakeNewsRepository, - ): NewsRepository + fun bindsNewsResourceRepository(fakeNewsRepository: FakeNewsRepository): NewsRepository @Binds - fun bindsUserDataRepository( - userDataRepository: FakeUserDataRepository, - ): UserDataRepository + fun bindsUserDataRepository(userDataRepository: FakeUserDataRepository): UserDataRepository @Binds fun bindsRecentSearchRepository( @@ -66,9 +60,7 @@ internal interface TestDataModule { ): SearchContentsRepository @Binds - fun bindsNetworkMonitor( - networkMonitor: AlwaysOnlineNetworkMonitor, - ): NetworkMonitor + fun bindsNetworkMonitor(networkMonitor: AlwaysOnlineNetworkMonitor): NetworkMonitor @Binds fun binds(impl: DefaultZoneIdTimeZoneMonitor): TimeZoneMonitor 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 070c7ed38..714f50486 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 @@ -27,11 +27,11 @@ 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.core.network.demo.DemoNiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn -import javax.inject.Inject /** * Fake implementation of the [NewsRepository] that retrieves the news resources from a JSON String. @@ -44,30 +44,27 @@ internal class FakeNewsRepository @Inject constructor( private val datasource: DemoNiaNetworkDataSource, ) : NewsRepository { - override fun getNewsResources( - query: NewsResourceQuery, - ): Flow> = - flow { - emit( - datasource - .getNewsResources() - .filter { networkNewsResource -> - // Filter out any news resources which don't match the current query. - // If no query parameters (filterTopicIds or filterNewsIds) are specified - // then the news resource is returned. - listOfNotNull( - true, - query.filterNewsIds?.contains(networkNewsResource.id), - query.filterTopicIds?.let { filterTopicIds -> - networkNewsResource.topics.intersect(filterTopicIds).isNotEmpty() - }, - ) - .all(true::equals) - } - .map(NetworkNewsResource::asEntity) - .map(NewsResourceEntity::asExternalModel), - ) - }.flowOn(ioDispatcher) + override fun getNewsResources(query: NewsResourceQuery): Flow> = flow { + emit( + datasource + .getNewsResources() + .filter { networkNewsResource -> + // Filter out any news resources which don't match the current query. + // If no query parameters (filterTopicIds or filterNewsIds) are specified + // then the news resource is returned. + listOfNotNull( + true, + query.filterNewsIds?.contains(networkNewsResource.id), + query.filterTopicIds?.let { filterTopicIds -> + networkNewsResource.topics.intersect(filterTopicIds).isNotEmpty() + }, + ) + .all(true::equals) + } + .map(NetworkNewsResource::asEntity) + .map(NewsResourceEntity::asExternalModel), + ) + }.flowOn(ioDispatcher) override suspend fun syncWith(synchronizer: Synchronizer) = true } 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 b8d949efe..649ad2113 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 @@ -18,9 +18,9 @@ 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 javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject /** * Fake implementation of the [RecentSearchRepository] 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 1feeb6dcc..73962860c 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 @@ -18,9 +18,9 @@ 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.model.data.SearchResult +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject /** * Fake implementation of the [SearchContentsRepository] 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 0b81dd309..869841bde 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 @@ -22,12 +22,12 @@ 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 import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import javax.inject.Inject /** * Fake implementation of the [TopicsRepository] that retrieves the topics from a JSON String, and 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 4871baad9..5da241a28 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 @@ -21,8 +21,8 @@ import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSou 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 -import kotlinx.coroutines.flow.Flow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow /** * Fake implementation of the [UserDataRepository] that returns hardcoded user data. 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 c3ad91dfe..d300ef8af 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 @@ -46,17 +46,16 @@ fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity( * A shell [TopicEntity] to fulfill the foreign key constraint when inserting * a [NewsResourceEntity] into the DB */ -fun NetworkNewsResource.topicEntityShells() = - topics.map { topicId -> - TopicEntity( - id = topicId, - name = "", - url = "", - imageUrl = "", - shortDescription = "", - longDescription = "", - ) - } +fun NetworkNewsResource.topicEntityShells() = topics.map { topicId -> + TopicEntity( + id = topicId, + name = "", + url = "", + imageUrl = "", + shortDescription = "", + longDescription = "", + ) +} fun NetworkNewsResource.topicCrossReferences(): List = topics.map { topicId -> diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt index 3d2f657dd..14ce23789 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/repository/AnalyticsExtensions.kt @@ -20,7 +20,10 @@ 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 -internal fun AnalyticsHelper.logNewsResourceBookmarkToggled(newsResourceId: String, isBookmarked: Boolean) { +internal fun AnalyticsHelper.logNewsResourceBookmarkToggled( + newsResourceId: String, + isBookmarked: Boolean, +) { val eventType = if (isBookmarked) "news_resource_saved" else "news_resource_unsaved" val paramKey = if (isBookmarked) "saved_news_resource_id" else "unsaved_news_resource_id" logEvent( @@ -46,35 +49,32 @@ internal fun AnalyticsHelper.logTopicFollowToggled(followedTopicId: String, isFo ) } -internal fun AnalyticsHelper.logThemeChanged(themeName: String) = - logEvent( - AnalyticsEvent( - type = "theme_changed", - extras = listOf( - Param(key = "theme_name", value = themeName), - ), +internal fun AnalyticsHelper.logThemeChanged(themeName: String) = logEvent( + AnalyticsEvent( + type = "theme_changed", + extras = listOf( + Param(key = "theme_name", value = themeName), ), - ) + ), +) -internal fun AnalyticsHelper.logDarkThemeConfigChanged(darkThemeConfigName: String) = - logEvent( - AnalyticsEvent( - type = "dark_theme_config_changed", - extras = listOf( - Param(key = "dark_theme_config", value = darkThemeConfigName), - ), +internal fun AnalyticsHelper.logDarkThemeConfigChanged(darkThemeConfigName: String) = logEvent( + AnalyticsEvent( + type = "dark_theme_config_changed", + extras = listOf( + Param(key = "dark_theme_config", value = darkThemeConfigName), ), - ) + ), +) -internal fun AnalyticsHelper.logDynamicColorPreferenceChanged(useDynamicColor: Boolean) = - logEvent( - AnalyticsEvent( - type = "dynamic_color_preference_changed", - extras = listOf( - Param(key = "dynamic_color_preference", value = useDynamicColor.toString()), - ), +internal fun AnalyticsHelper.logDynamicColorPreferenceChanged(useDynamicColor: Boolean) = logEvent( + AnalyticsEvent( + type = "dynamic_color_preference_changed", + extras = listOf( + Param(key = "dynamic_color_preference", value = useDynamicColor.toString()), ), - ) + ), +) internal fun AnalyticsHelper.logOnboardingStateChanged(shouldHideOnboarding: Boolean) { val eventType = if (shouldHideOnboarding) "onboarding_complete" else "onboarding_reset" 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 64e02e7d9..adcc08af9 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 @@ -18,13 +18,13 @@ package com.google.samples.apps.nowinandroid.core.data.repository import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResources +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import javax.inject.Inject /** * Implements a [UserNewsResourceRepository] by combining a [NewsRepository] with a @@ -38,9 +38,7 @@ class CompositeUserNewsResourceRepository @Inject constructor( /** * Returns available news resources (joined with user data) matching the given query. */ - override fun observeAll( - query: NewsResourceQuery, - ): Flow> = + override fun observeAll(query: NewsResourceQuery): Flow> = newsRepository.getNewsResources(query) .combine(userDataRepository.userData) { newsResources, userData -> newsResources.mapToUserNewsResources(userData) 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 32239362d..4edc335ad 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 @@ -20,10 +20,10 @@ 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 javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.datetime.Clock -import javax.inject.Inject internal class DefaultRecentSearchRepository @Inject constructor( private val recentSearchQueryDao: RecentSearchQueryDao, 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 3bacb8a14..c10ff1e3b 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 @@ -26,6 +26,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity 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 +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -34,7 +35,6 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext -import javax.inject.Inject internal class DefaultSearchContentsRepository @Inject constructor( private val newsResourceDao: NewsResourceDao, @@ -82,11 +82,10 @@ internal class DefaultSearchContentsRepository @Inject constructor( } } - override fun getSearchContentsCount(): Flow = - combine( - newsResourceFtsDao.getCount(), - topicFtsDao.getCount(), - ) { newsResourceCount, topicsCount -> - newsResourceCount + topicsCount - } + override fun getSearchContentsCount(): Flow = combine( + newsResourceFtsDao.getCount(), + topicFtsDao.getCount(), + ) { newsResourceCount, topicsCount -> + newsResourceCount + topicsCount + } } 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 d33c904e5..254eba482 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 @@ -32,10 +32,10 @@ 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 import com.google.samples.apps.nowinandroid.core.notifications.Notifier +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import javax.inject.Inject // Heuristic value to optimize for serialization and deserialization cost on client and server // for each news resource batch. @@ -53,15 +53,14 @@ internal class OfflineFirstNewsRepository @Inject constructor( private val notifier: Notifier, ) : NewsRepository { - override fun getNewsResources( - query: NewsResourceQuery, - ): Flow> = newsResourceDao.getNewsResources( - useFilterTopicIds = query.filterTopicIds != null, - filterTopicIds = query.filterTopicIds ?: emptySet(), - useFilterNewsIds = query.filterNewsIds != null, - filterNewsIds = query.filterNewsIds ?: emptySet(), - ) - .map { it.map(PopulatedNewsResource::asExternalModel) } + override fun getNewsResources(query: NewsResourceQuery): Flow> = + newsResourceDao.getNewsResources( + useFilterTopicIds = query.filterTopicIds != null, + filterTopicIds = query.filterTopicIds ?: emptySet(), + useFilterNewsIds = query.filterNewsIds != null, + filterNewsIds = query.filterNewsIds ?: emptySet(), + ) + .map { it.map(PopulatedNewsResource::asExternalModel) } override suspend fun syncWith(synchronizer: Synchronizer): Boolean { var isFirstSync = false 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 5c8cecce8..5eeed0bd1 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 @@ -26,9 +26,9 @@ import com.google.samples.apps.nowinandroid.core.datastore.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 +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -import javax.inject.Inject /** * Disk storage backed implementation of the [TopicsRepository]. @@ -39,9 +39,8 @@ internal class OfflineFirstTopicsRepository @Inject constructor( private val network: NiaNetworkDataSource, ) : TopicsRepository { - override fun getTopics(): Flow> = - topicDao.getTopicEntities() - .map { it.map(TopicEntity::asExternalModel) } + override fun getTopics(): Flow> = topicDao.getTopicEntities() + .map { it.map(TopicEntity::asExternalModel) } override fun getTopic(id: String): Flow = topicDao.getTopicEntity(id).map { it.asExternalModel() } 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 089b7087d..2adcfb594 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 @@ -22,8 +22,8 @@ import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSou 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 -import kotlinx.coroutines.flow.Flow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow internal class OfflineFirstUserDataRepository @Inject constructor( private val niaPreferencesDataSource: NiaPreferencesDataSource, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt index e9599c555..abe5ad0f2 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/ConnectivityManagerNetworkMonitor.kt @@ -27,11 +27,11 @@ import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate -import javax.inject.Inject internal class ConnectivityManagerNetworkMonitor @Inject constructor( @ApplicationContext private val context: Context, diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt index 031bc9388..e8c4aee32 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/util/TimeZoneMonitor.kt @@ -27,6 +27,9 @@ 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.core.network.di.ApplicationScope import dagger.hilt.android.qualifiers.ApplicationContext +import java.time.ZoneId +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose @@ -40,9 +43,6 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.shareIn import kotlinx.datetime.TimeZone import kotlinx.datetime.toKotlinTimeZone -import java.time.ZoneId -import javax.inject.Inject -import javax.inject.Singleton /** * Utility for reporting current timezone the device has set. 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 05811f4be..fd1f07b87 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 @@ -24,11 +24,11 @@ import com.google.samples.apps.nowinandroid.core.model.data.mapToUserNewsResourc import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData +import kotlin.test.assertEquals import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import org.junit.Test -import kotlin.test.assertEquals class CompositeUserNewsResourceRepositoryTest { diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt index 7dd251a99..ae8abfac0 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt @@ -19,9 +19,9 @@ package com.google.samples.apps.nowinandroid.core.data.model import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import kotlin.test.assertEquals import kotlinx.datetime.Instant import org.junit.Test -import kotlin.test.assertEquals class NetworkEntityKtTest { 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 47c3996c4..199788247 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 @@ -38,6 +38,8 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.testing.notifications.TestNotifier +import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -46,8 +48,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import kotlin.test.assertEquals -import kotlin.test.assertTrue class OfflineFirstNewsRepositoryTest { @@ -134,34 +134,33 @@ class OfflineFirstNewsRepositoryTest { } @Test - fun offlineFirstNewsRepository_sync_pulls_from_network() = - testScope.runTest { - // User has not onboarded - niaPreferencesDataSource.setShouldHideOnboarding(false) - subject.syncWith(synchronizer) - - val newsResourcesFromNetwork = network.getNewsResources() - .map(NetworkNewsResource::asEntity) - .map(NewsResourceEntity::asExternalModel) - - val newsResourcesFromDb = newsResourceDao.getNewsResources() - .first() - .map(PopulatedNewsResource::asExternalModel) - - assertEquals( - newsResourcesFromNetwork.map(NewsResource::id).sorted(), - newsResourcesFromDb.map(NewsResource::id).sorted(), - ) + fun offlineFirstNewsRepository_sync_pulls_from_network() = testScope.runTest { + // User has not onboarded + niaPreferencesDataSource.setShouldHideOnboarding(false) + subject.syncWith(synchronizer) + + val newsResourcesFromNetwork = network.getNewsResources() + .map(NetworkNewsResource::asEntity) + .map(NewsResourceEntity::asExternalModel) + + val newsResourcesFromDb = newsResourceDao.getNewsResources() + .first() + .map(PopulatedNewsResource::asExternalModel) + + assertEquals( + newsResourcesFromNetwork.map(NewsResource::id).sorted(), + newsResourcesFromDb.map(NewsResource::id).sorted(), + ) - // After sync version should be updated - assertEquals( - expected = network.latestChangeListVersion(CollectionType.NewsResources), - actual = synchronizer.getChangeListVersions().newsResourceVersion, - ) + // After sync version should be updated + assertEquals( + expected = network.latestChangeListVersion(CollectionType.NewsResources), + actual = synchronizer.getChangeListVersions().newsResourceVersion, + ) - // Notifier should not have been called - assertTrue(notifier.addedNewsResources.isEmpty()) - } + // Notifier should not have been called + assertTrue(notifier.addedNewsResources.isEmpty()) + } @Test fun offlineFirstNewsRepository_sync_deletes_items_marked_deleted_on_network() = @@ -211,93 +210,89 @@ class OfflineFirstNewsRepositoryTest { } @Test - fun offlineFirstNewsRepository_incremental_sync_pulls_from_network() = - testScope.runTest { - // User has not onboarded - niaPreferencesDataSource.setShouldHideOnboarding(false) - - // Set news version to 7 - synchronizer.updateChangeListVersions { - copy(newsResourceVersion = 7) - } + fun offlineFirstNewsRepository_incremental_sync_pulls_from_network() = testScope.runTest { + // User has not onboarded + niaPreferencesDataSource.setShouldHideOnboarding(false) - subject.syncWith(synchronizer) - - val changeList = network.changeListsAfter( - CollectionType.NewsResources, - version = 7, - ) - val changeListIds = changeList - .map(NetworkChangeList::id) - .toSet() - - val newsResourcesFromNetwork = network.getNewsResources() - .map(NetworkNewsResource::asEntity) - .map(NewsResourceEntity::asExternalModel) - .filter { it.id in changeListIds } + // Set news version to 7 + synchronizer.updateChangeListVersions { + copy(newsResourceVersion = 7) + } - val newsResourcesFromDb = newsResourceDao.getNewsResources() - .first() - .map(PopulatedNewsResource::asExternalModel) + subject.syncWith(synchronizer) - assertEquals( - expected = newsResourcesFromNetwork.map(NewsResource::id).sorted(), - actual = newsResourcesFromDb.map(NewsResource::id).sorted(), - ) + val changeList = network.changeListsAfter( + CollectionType.NewsResources, + version = 7, + ) + val changeListIds = changeList + .map(NetworkChangeList::id) + .toSet() + + val newsResourcesFromNetwork = network.getNewsResources() + .map(NetworkNewsResource::asEntity) + .map(NewsResourceEntity::asExternalModel) + .filter { it.id in changeListIds } + + val newsResourcesFromDb = newsResourceDao.getNewsResources() + .first() + .map(PopulatedNewsResource::asExternalModel) + + assertEquals( + expected = newsResourcesFromNetwork.map(NewsResource::id).sorted(), + actual = newsResourcesFromDb.map(NewsResource::id).sorted(), + ) - // After sync version should be updated - assertEquals( - expected = changeList.last().changeListVersion, - actual = synchronizer.getChangeListVersions().newsResourceVersion, - ) + // After sync version should be updated + assertEquals( + expected = changeList.last().changeListVersion, + actual = synchronizer.getChangeListVersions().newsResourceVersion, + ) - // Notifier should not have been called - assertTrue(notifier.addedNewsResources.isEmpty()) - } + // Notifier should not have been called + assertTrue(notifier.addedNewsResources.isEmpty()) + } @Test - fun offlineFirstNewsRepository_sync_saves_shell_topic_entities() = - testScope.runTest { - subject.syncWith(synchronizer) - - assertEquals( - expected = network.getNewsResources() - .map(NetworkNewsResource::topicEntityShells) - .flatten() - .distinctBy(TopicEntity::id) - .sortedBy(TopicEntity::toString), - actual = topicDao.getTopicEntities() - .first() - .sortedBy(TopicEntity::toString), - ) - } + fun offlineFirstNewsRepository_sync_saves_shell_topic_entities() = testScope.runTest { + subject.syncWith(synchronizer) + + assertEquals( + expected = network.getNewsResources() + .map(NetworkNewsResource::topicEntityShells) + .flatten() + .distinctBy(TopicEntity::id) + .sortedBy(TopicEntity::toString), + actual = topicDao.getTopicEntities() + .first() + .sortedBy(TopicEntity::toString), + ) + } @Test - fun offlineFirstNewsRepository_sync_saves_topic_cross_references() = - testScope.runTest { - subject.syncWith(synchronizer) - - assertEquals( - expected = network.getNewsResources() - .map(NetworkNewsResource::topicCrossReferences) - .flatten() - .distinct() - .sortedBy(NewsResourceTopicCrossRef::toString), - actual = newsResourceDao.topicCrossReferences - .sortedBy(NewsResourceTopicCrossRef::toString), - ) - } + fun offlineFirstNewsRepository_sync_saves_topic_cross_references() = testScope.runTest { + subject.syncWith(synchronizer) + + assertEquals( + expected = network.getNewsResources() + .map(NetworkNewsResource::topicCrossReferences) + .flatten() + .distinct() + .sortedBy(NewsResourceTopicCrossRef::toString), + actual = newsResourceDao.topicCrossReferences + .sortedBy(NewsResourceTopicCrossRef::toString), + ) + } @Test - fun offlineFirstNewsRepository_sync_marks_as_read_on_first_run() = - testScope.runTest { - subject.syncWith(synchronizer) + fun offlineFirstNewsRepository_sync_marks_as_read_on_first_run() = testScope.runTest { + subject.syncWith(synchronizer) - assertEquals( - network.getNewsResources().map { it.id }.toSet(), - niaPreferencesDataSource.userData.first().viewedNewsResources, - ) - } + assertEquals( + network.getNewsResources().map { it.id }.toSet(), + niaPreferencesDataSource.userData.first().viewedNewsResources, + ) + } @Test fun offlineFirstNewsRepository_sync_does_not_mark_as_read_on_subsequent_run() = 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 3bd314eae..a3316c943 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 @@ -28,6 +28,7 @@ import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSou import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import kotlin.test.assertEquals import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -36,7 +37,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import kotlin.test.assertEquals class OfflineFirstTopicsRepositoryTest { @@ -71,69 +71,66 @@ class OfflineFirstTopicsRepositoryTest { } @Test - fun offlineFirstTopicsRepository_topics_stream_is_backed_by_topics_dao() = - testScope.runTest { - assertEquals( - topicDao.getTopicEntities() - .first() - .map(TopicEntity::asExternalModel), - subject.getTopics() - .first(), - ) - } + fun offlineFirstTopicsRepository_topics_stream_is_backed_by_topics_dao() = testScope.runTest { + assertEquals( + topicDao.getTopicEntities() + .first() + .map(TopicEntity::asExternalModel), + subject.getTopics() + .first(), + ) + } @Test - fun offlineFirstTopicsRepository_sync_pulls_from_network() = - testScope.runTest { - subject.syncWith(synchronizer) + fun offlineFirstTopicsRepository_sync_pulls_from_network() = testScope.runTest { + subject.syncWith(synchronizer) - val networkTopics = network.getTopics() - .map(NetworkTopic::asEntity) + val networkTopics = network.getTopics() + .map(NetworkTopic::asEntity) - val dbTopics = topicDao.getTopicEntities() - .first() + val dbTopics = topicDao.getTopicEntities() + .first() - assertEquals( - networkTopics.map(TopicEntity::id), - dbTopics.map(TopicEntity::id), - ) + assertEquals( + networkTopics.map(TopicEntity::id), + dbTopics.map(TopicEntity::id), + ) - // After sync version should be updated - assertEquals( - network.latestChangeListVersion(CollectionType.Topics), - synchronizer.getChangeListVersions().topicVersion, - ) - } + // After sync version should be updated + assertEquals( + network.latestChangeListVersion(CollectionType.Topics), + synchronizer.getChangeListVersions().topicVersion, + ) + } @Test - fun offlineFirstTopicsRepository_incremental_sync_pulls_from_network() = - testScope.runTest { - // Set topics version to 10 - synchronizer.updateChangeListVersions { - copy(topicVersion = 10) - } + fun offlineFirstTopicsRepository_incremental_sync_pulls_from_network() = testScope.runTest { + // Set topics version to 10 + synchronizer.updateChangeListVersions { + copy(topicVersion = 10) + } - subject.syncWith(synchronizer) + subject.syncWith(synchronizer) - val networkTopics = network.getTopics() - .map(NetworkTopic::asEntity) - // Drop 10 to simulate the first 10 items being unchanged - .drop(10) + val networkTopics = network.getTopics() + .map(NetworkTopic::asEntity) + // Drop 10 to simulate the first 10 items being unchanged + .drop(10) - val dbTopics = topicDao.getTopicEntities() - .first() + val dbTopics = topicDao.getTopicEntities() + .first() - assertEquals( - networkTopics.map(TopicEntity::id), - dbTopics.map(TopicEntity::id), - ) + assertEquals( + networkTopics.map(TopicEntity::id), + dbTopics.map(TopicEntity::id), + ) - // After sync version should be updated - assertEquals( - network.latestChangeListVersion(CollectionType.Topics), - synchronizer.getChangeListVersions().topicVersion, - ) - } + // After sync version should be updated + assertEquals( + network.latestChangeListVersion(CollectionType.Topics), + synchronizer.getChangeListVersions().topicVersion, + ) + } @Test fun offlineFirstTopicsRepository_sync_deletes_items_marked_deleted_on_network() = 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 422e2cfb7..789d8c52b 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 @@ -22,6 +22,9 @@ import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferen 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 +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.test.TestScope @@ -31,9 +34,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue class OfflineFirstUserDataRepositoryTest { @@ -61,21 +61,20 @@ class OfflineFirstUserDataRepositoryTest { } @Test - fun offlineFirstUserDataRepository_default_user_data_is_correct() = - testScope.runTest { - assertEquals( - UserData( - bookmarkedNewsResources = emptySet(), - viewedNewsResources = emptySet(), - followedTopics = emptySet(), - themeBrand = ThemeBrand.DEFAULT, - darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, - useDynamicColor = false, - shouldHideOnboarding = false, - ), - subject.userData.first(), - ) - } + fun offlineFirstUserDataRepository_default_user_data_is_correct() = testScope.runTest { + assertEquals( + UserData( + bookmarkedNewsResources = emptySet(), + viewedNewsResources = emptySet(), + followedTopics = emptySet(), + themeBrand = ThemeBrand.DEFAULT, + darkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + useDynamicColor = false, + shouldHideOnboarding = false, + ), + subject.userData.first(), + ) + } @Test fun offlineFirstUserDataRepository_toggle_followed_topics_logic_delegates_to_nia_preferences() = diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt index a3e373918..b388555c5 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNewsResourceDao.kt @@ -43,54 +43,52 @@ class TestNewsResourceDao : NewsResourceDao { filterTopicIds: Set, useFilterNewsIds: Boolean, filterNewsIds: Set, - ): Flow> = - entitiesStateFlow - .map { newsResourceEntities -> - newsResourceEntities.map { entity -> - entity.asPopulatedNewsResource(topicCrossReferences) - } + ): Flow> = entitiesStateFlow + .map { newsResourceEntities -> + newsResourceEntities.map { entity -> + entity.asPopulatedNewsResource(topicCrossReferences) } - .map { resources -> - var result = resources - if (useFilterTopicIds) { - result = result.filter { resource -> - resource.topics.any { it.id in filterTopicIds } - } + } + .map { resources -> + var result = resources + if (useFilterTopicIds) { + result = result.filter { resource -> + resource.topics.any { it.id in filterTopicIds } } - if (useFilterNewsIds) { - result = result.filter { resource -> - resource.entity.id in filterNewsIds - } + } + if (useFilterNewsIds) { + result = result.filter { resource -> + resource.entity.id in filterNewsIds } - result } + result + } override fun getNewsResourceIds( useFilterTopicIds: Boolean, filterTopicIds: Set, useFilterNewsIds: Boolean, filterNewsIds: Set, - ): Flow> = - entitiesStateFlow - .map { newsResourceEntities -> - newsResourceEntities.map { entity -> - entity.asPopulatedNewsResource(topicCrossReferences) - } + ): Flow> = entitiesStateFlow + .map { newsResourceEntities -> + newsResourceEntities.map { entity -> + entity.asPopulatedNewsResource(topicCrossReferences) } - .map { resources -> - var result = resources - if (useFilterTopicIds) { - result = result.filter { resource -> - resource.topics.any { it.id in filterTopicIds } - } + } + .map { resources -> + var result = resources + if (useFilterTopicIds) { + result = result.filter { resource -> + resource.topics.any { it.id in filterTopicIds } } - if (useFilterNewsIds) { - result = result.filter { resource -> - resource.entity.id in filterNewsIds - } + } + if (useFilterNewsIds) { + result = result.filter { resource -> + resource.entity.id in filterNewsIds } - result.map { it.entity.id } } + result.map { it.entity.id } + } override suspend fun upsertNewsResources(newsResourceEntities: List) { entitiesStateFlow.update { oldValues -> diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt index 7675af7e9..c92777a26 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/testdoubles/TestNiaNetworkDataSource.kt @@ -51,11 +51,10 @@ class TestNiaNetworkDataSource : NiaNetworkDataSource { .mapToChangeList(idGetter = NetworkNewsResource::id), ) - override suspend fun getTopics(ids: List?): List = - allTopics.matchIds( - ids = ids, - idGetter = NetworkTopic::id, - ) + override suspend fun getTopics(ids: List?): List = allTopics.matchIds( + ids = ids, + idGetter = NetworkTopic::id, + ) override suspend fun getNewsResources(ids: List?): List = allNewsResources.matchIds( @@ -99,10 +98,7 @@ fun List.after(version: Int?): List = when /** * Return items from [this] whose id defined by [idGetter] is in [ids] if [ids] is not null */ -private fun List.matchIds( - ids: List?, - idGetter: (T) -> String, -) = when (ids) { +private fun List.matchIds(ids: List?, idGetter: (T) -> String) = when (ids) { null -> this else -> ids.toSet().let { idSet -> filter { idGetter(it) in idSet } } } @@ -111,9 +107,7 @@ private fun List.matchIds( * Maps items to a change list where the change list version is denoted by the index of each item. * [after] simulates which models have changed by excluding items before it */ -private fun List.mapToChangeList( - idGetter: (T) -> String, -) = mapIndexed { index, item -> +private fun List.mapToChangeList(idGetter: (T) -> String) = mapIndexed { index, item -> NetworkChangeList( id = idGetter(item), changeListVersion = index + 1, diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt index a1b72c52f..aa25b55ab 100644 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResourceKtTest.kt @@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.core.database.model import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.Topic +import kotlin.test.assertEquals import kotlinx.datetime.Instant import org.junit.Test -import kotlin.test.assertEquals class PopulatedNewsResourceKtTest { @Test diff --git a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt b/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt index 535ab61a7..b57da3166 100644 --- a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt +++ b/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt @@ -24,13 +24,13 @@ import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEnti import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel +import kotlin.test.assertEquals import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import org.junit.After import org.junit.Before import org.junit.Test -import kotlin.test.assertEquals class NewsResourceDaoTest { @@ -248,48 +248,44 @@ class NewsResourceDaoTest { } @Test - fun newsResourceDao_deletes_items_by_ids() = - runTest { - val newsResourceEntities = listOf( - testNewsResource( - id = "0", - millisSinceEpoch = 0, - ), - testNewsResource( - id = "1", - millisSinceEpoch = 3, - ), - testNewsResource( - id = "2", - millisSinceEpoch = 1, - ), - testNewsResource( - id = "3", - millisSinceEpoch = 2, - ), - ) - newsResourceDao.upsertNewsResources(newsResourceEntities) + fun newsResourceDao_deletes_items_by_ids() = runTest { + val newsResourceEntities = listOf( + testNewsResource( + id = "0", + millisSinceEpoch = 0, + ), + testNewsResource( + id = "1", + millisSinceEpoch = 3, + ), + testNewsResource( + id = "2", + millisSinceEpoch = 1, + ), + testNewsResource( + id = "3", + millisSinceEpoch = 2, + ), + ) + newsResourceDao.upsertNewsResources(newsResourceEntities) - val (toDelete, toKeep) = newsResourceEntities.partition { it.id.toInt() % 2 == 0 } + val (toDelete, toKeep) = newsResourceEntities.partition { it.id.toInt() % 2 == 0 } - newsResourceDao.deleteNewsResources( - toDelete.map(NewsResourceEntity::id), - ) + newsResourceDao.deleteNewsResources( + toDelete.map(NewsResourceEntity::id), + ) - assertEquals( - toKeep.map(NewsResourceEntity::id) - .toSet(), - newsResourceDao.getNewsResources().first() - .map { it.entity.id } - .toSet(), - ) - } + assertEquals( + toKeep.map(NewsResourceEntity::id) + .toSet(), + newsResourceDao.getNewsResources().first() + .map { it.entity.id } + .toSet(), + ) + } } -private fun testTopicEntity( - id: String = "0", - name: String, -) = TopicEntity( +private fun testTopicEntity(id: String = "0", name: String) = TopicEntity( id = id, name = name, shortDescription = "", @@ -298,10 +294,7 @@ private fun testTopicEntity( imageUrl = "", ) -private fun testNewsResource( - id: String = "0", - millisSinceEpoch: Long = 0, -) = NewsResourceEntity( +private fun testNewsResource(id: String = "0", millisSinceEpoch: Long = 0) = NewsResourceEntity( id = id, title = "", content = "", diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DaosModule.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DaosModule.kt index e7456054e..3aa2abe4b 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DaosModule.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DaosModule.kt @@ -31,27 +31,19 @@ import dagger.hilt.components.SingletonComponent @InstallIn(SingletonComponent::class) internal object DaosModule { @Provides - fun providesTopicsDao( - database: NiaDatabase, - ): TopicDao = database.topicDao() + fun providesTopicsDao(database: NiaDatabase): TopicDao = database.topicDao() @Provides - fun providesNewsResourceDao( - database: NiaDatabase, - ): NewsResourceDao = database.newsResourceDao() + fun providesNewsResourceDao(database: NiaDatabase): NewsResourceDao = database.newsResourceDao() @Provides - fun providesTopicFtsDao( - database: NiaDatabase, - ): TopicFtsDao = database.topicFtsDao() + fun providesTopicFtsDao(database: NiaDatabase): TopicFtsDao = database.topicFtsDao() @Provides - fun providesNewsResourceFtsDao( - database: NiaDatabase, - ): NewsResourceFtsDao = database.newsResourceFtsDao() + fun providesNewsResourceFtsDao(database: NiaDatabase): NewsResourceFtsDao = + database.newsResourceFtsDao() @Provides - fun providesRecentSearchQueryDao( - database: NiaDatabase, - ): RecentSearchQueryDao = database.recentSearchQueryDao() + fun providesRecentSearchQueryDao(database: NiaDatabase): RecentSearchQueryDao = + database.recentSearchQueryDao() } diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt index d79d35948..5fc7ec7c0 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/di/DatabaseModule.kt @@ -31,11 +31,10 @@ import javax.inject.Singleton internal object DatabaseModule { @Provides @Singleton - fun providesNiaDatabase( - @ApplicationContext context: Context, - ): NiaDatabase = Room.databaseBuilder( - context, - NiaDatabase::class.java, - "nia-database", - ).build() + fun providesNiaDatabase(@ApplicationContext context: Context): NiaDatabase = + Room.databaseBuilder( + context, + NiaDatabase::class.java, + "nia-database", + ).build() } diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt index 0b79c2099..226313622 100644 --- a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt +++ b/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt @@ -21,10 +21,8 @@ import kotlinx.datetime.Instant internal class InstantConverter { @TypeConverter - fun longToInstant(value: Long?): Instant? = - value?.let(Instant::fromEpochMilliseconds) + fun longToInstant(value: Long?): Instant? = value?.let(Instant::fromEpochMilliseconds) @TypeConverter - fun instantToLong(instant: Instant?): Long? = - instant?.toEpochMilliseconds() + fun instantToLong(instant: Instant?): Long? = instant?.toEpochMilliseconds() } diff --git a/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt b/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt index 295b2978a..1f8b23334 100644 --- a/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt +++ b/core/datastore-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/test/TestDataStoreModule.kt @@ -26,9 +26,9 @@ import dagger.Module import dagger.Provides import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn +import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope import org.junit.rules.TemporaryFolder -import javax.inject.Singleton @Module @TestInstallIn( @@ -43,11 +43,10 @@ internal object TestDataStoreModule { @ApplicationScope scope: CoroutineScope, userPreferencesSerializer: UserPreferencesSerializer, tmpFolder: TemporaryFolder, - ): DataStore = - tmpFolder.testUserPreferencesDataStore( - coroutineScope = scope, - userPreferencesSerializer = userPreferencesSerializer, - ) + ): DataStore = tmpFolder.testUserPreferencesDataStore( + coroutineScope = scope, + userPreferencesSerializer = userPreferencesSerializer, + ) } fun TemporaryFolder.testUserPreferencesDataStore( diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt index ef9c1dd03..e32e25dd6 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigration.kt @@ -25,25 +25,24 @@ internal object IntToStringIdsMigration : DataMigration { override suspend fun cleanUp() = Unit - override suspend fun migrate(currentData: UserPreferences): UserPreferences = - currentData.copy { - // Migrate topic ids - deprecatedFollowedTopicIds.clear() - deprecatedFollowedTopicIds.addAll( - currentData.deprecatedIntFollowedTopicIdsList.map(Int::toString), - ) - deprecatedIntFollowedTopicIds.clear() - - // Migrate author ids - deprecatedFollowedAuthorIds.clear() - deprecatedFollowedAuthorIds.addAll( - currentData.deprecatedIntFollowedAuthorIdsList.map(Int::toString), - ) - deprecatedIntFollowedAuthorIds.clear() - - // Mark migration as complete - hasDoneIntToStringIdMigration = true - } + override suspend fun migrate(currentData: UserPreferences): UserPreferences = currentData.copy { + // Migrate topic ids + deprecatedFollowedTopicIds.clear() + deprecatedFollowedTopicIds.addAll( + currentData.deprecatedIntFollowedTopicIdsList.map(Int::toString), + ) + deprecatedIntFollowedTopicIds.clear() + + // Migrate author ids + deprecatedFollowedAuthorIds.clear() + deprecatedFollowedAuthorIds.addAll( + currentData.deprecatedIntFollowedAuthorIdsList.map(Int::toString), + ) + deprecatedIntFollowedAuthorIds.clear() + + // Mark migration as complete + hasDoneIntToStringIdMigration = true + } override suspend fun shouldMigrate(currentData: UserPreferences): Boolean = !currentData.hasDoneIntToStringIdMigration diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt index 5675aee05..d510f29cd 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigration.kt @@ -25,32 +25,31 @@ internal object ListToMapMigration : DataMigration { override suspend fun cleanUp() = Unit - override suspend fun migrate(currentData: UserPreferences): UserPreferences = - currentData.copy { - // Migrate topic id lists - followedTopicIds.clear() - followedTopicIds.putAll( - currentData.deprecatedFollowedTopicIdsList.associateWith { true }, - ) - deprecatedFollowedTopicIds.clear() - - // Migrate author ids - followedAuthorIds.clear() - followedAuthorIds.putAll( - currentData.deprecatedFollowedAuthorIdsList.associateWith { true }, - ) - deprecatedFollowedAuthorIds.clear() - - // Migrate bookmarks - bookmarkedNewsResourceIds.clear() - bookmarkedNewsResourceIds.putAll( - currentData.deprecatedBookmarkedNewsResourceIdsList.associateWith { true }, - ) - deprecatedBookmarkedNewsResourceIds.clear() - - // Mark migration as complete - hasDoneListToMapMigration = true - } + override suspend fun migrate(currentData: UserPreferences): UserPreferences = currentData.copy { + // Migrate topic id lists + followedTopicIds.clear() + followedTopicIds.putAll( + currentData.deprecatedFollowedTopicIdsList.associateWith { true }, + ) + deprecatedFollowedTopicIds.clear() + + // Migrate author ids + followedAuthorIds.clear() + followedAuthorIds.putAll( + currentData.deprecatedFollowedAuthorIdsList.associateWith { true }, + ) + deprecatedFollowedAuthorIds.clear() + + // Migrate bookmarks + bookmarkedNewsResourceIds.clear() + bookmarkedNewsResourceIds.putAll( + currentData.deprecatedBookmarkedNewsResourceIdsList.associateWith { true }, + ) + deprecatedBookmarkedNewsResourceIds.clear() + + // Mark migration as complete + hasDoneListToMapMigration = true + } override suspend fun shouldMigrate(currentData: UserPreferences): Boolean = !currentData.hasDoneListToMapMigration 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 9a76a75a1..a8ecdcedd 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 @@ -21,10 +21,10 @@ import androidx.datastore.core.DataStore 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 -import kotlinx.coroutines.flow.firstOrNull -import kotlinx.coroutines.flow.map import java.io.IOException import javax.inject.Inject +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map class NiaPreferencesDataSource @Inject constructor( private val userPreferences: DataStore, diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt index 40c1e210f..e96bc9045 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializer.kt @@ -29,13 +29,12 @@ import javax.inject.Inject class UserPreferencesSerializer @Inject constructor() : Serializer { override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance() - override suspend fun readFrom(input: InputStream): UserPreferences = - try { - // readFrom is already called on the data store background thread - UserPreferences.parseFrom(input) - } catch (exception: InvalidProtocolBufferException) { - throw CorruptionException("Cannot read proto.", exception) - } + override suspend fun readFrom(input: InputStream): UserPreferences = try { + // readFrom is already called on the data store background thread + UserPreferences.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } override suspend fun writeTo(t: UserPreferences, output: OutputStream) { // writeTo is already called on the data store background thread diff --git a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt index 8e0d7d4d8..9a48f0632 100644 --- a/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/kotlin/com/google/samples/apps/nowinandroid/core/datastore/di/DataStoreModule.kt @@ -31,9 +31,9 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) @@ -46,14 +46,13 @@ object DataStoreModule { @Dispatcher(IO) ioDispatcher: CoroutineDispatcher, @ApplicationScope scope: CoroutineScope, userPreferencesSerializer: UserPreferencesSerializer, - ): DataStore = - DataStoreFactory.create( - serializer = userPreferencesSerializer, - scope = CoroutineScope(scope.coroutineContext + ioDispatcher), - migrations = listOf( - IntToStringIdsMigration, - ), - ) { - context.dataStoreFile("user_preferences.pb") - } + ): DataStore = DataStoreFactory.create( + serializer = userPreferencesSerializer, + scope = CoroutineScope(scope.coroutineContext + ioDispatcher), + migrations = listOf( + IntToStringIdsMigration, + ), + ) { + context.dataStoreFile("user_preferences.pb") + } } diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt index 8b97cff34..4d6120ac1 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/IntToStringIdsMigrationTest.kt @@ -16,10 +16,10 @@ package com.google.samples.apps.nowinandroid.core.datastore -import kotlinx.coroutines.test.runTest -import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlinx.coroutines.test.runTest +import org.junit.Test /** * Unit test for [IntToStringIdsMigration] diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt index f7e083b45..8e788e4ee 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/ListToMapMigrationTest.kt @@ -16,10 +16,10 @@ package com.google.samples.apps.nowinandroid.core.datastore -import kotlinx.coroutines.test.runTest -import org.junit.Test import kotlin.test.assertEquals import kotlin.test.assertTrue +import kotlinx.coroutines.test.runTest +import org.junit.Test class ListToMapMigrationTest { diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt index 433812808..bf47ea40b 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/NiaPreferencesDataSourceTest.kt @@ -17,6 +17,8 @@ package com.google.samples.apps.nowinandroid.core.datastore import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore +import kotlin.test.assertFalse +import kotlin.test.assertTrue import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -25,8 +27,6 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.TemporaryFolder -import kotlin.test.assertFalse -import kotlin.test.assertTrue class NiaPreferencesDataSourceTest { diff --git a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt index ad7664fe5..52dbacb8b 100644 --- a/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt +++ b/core/datastore/src/test/kotlin/com/google/samples/apps/nowinandroid/core/datastore/UserPreferencesSerializerTest.kt @@ -17,11 +17,11 @@ package com.google.samples.apps.nowinandroid.core.datastore import androidx.datastore.core.CorruptionException -import kotlinx.coroutines.test.runTest -import org.junit.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import org.junit.Test class UserPreferencesSerializerTest { private val userPreferencesSerializer = UserPreferencesSerializer() 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/GetFollowableTopicsUseCase.kt index 0167a3192..0d4387cfe 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/GetFollowableTopicsUseCase.kt @@ -21,9 +21,9 @@ import com.google.samples.apps.nowinandroid.core.data.repository.UserDataReposit 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.model.data.FollowableTopic +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import javax.inject.Inject /** * A use case which obtains a list of topics with their followed state. 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/GetRecentSearchQueriesUseCase.kt index 51f87d6fd..f7c27a5f0 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/GetRecentSearchQueriesUseCase.kt @@ -18,8 +18,8 @@ package com.google.samples.apps.nowinandroid.core.domain import com.google.samples.apps.nowinandroid.core.data.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.data.repository.RecentSearchRepository -import kotlinx.coroutines.flow.Flow import javax.inject.Inject +import kotlinx.coroutines.flow.Flow /** * A use case which returns the recent search queries. 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/GetSearchContentsUseCase.kt index d1065e87c..f69b11984 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/GetSearchContentsUseCase.kt @@ -23,9 +23,9 @@ import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.model.data.UserData import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserSearchResult +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import javax.inject.Inject /** * A use case which returns the searched contents matched with the search query. @@ -35,27 +35,26 @@ class GetSearchContentsUseCase @Inject constructor( private val userDataRepository: UserDataRepository, ) { - operator fun invoke( - searchQuery: String, - ): Flow = + operator fun invoke(searchQuery: String): Flow = searchContentsRepository.searchContents(searchQuery) .mapToUserSearchResult(userDataRepository.userData) } -private fun Flow.mapToUserSearchResult(userDataStream: Flow): Flow = - combine(userDataStream) { searchResult, userData -> - UserSearchResult( - topics = searchResult.topics.map { topic -> - FollowableTopic( - topic = topic, - isFollowed = topic.id in userData.followedTopics, - ) - }, - newsResources = searchResult.newsResources.map { news -> - UserNewsResource( - newsResource = news, - userData = userData, - ) - }, - ) - } +private fun Flow.mapToUserSearchResult( + userDataStream: Flow, +): Flow = combine(userDataStream) { searchResult, userData -> + UserSearchResult( + topics = searchResult.topics.map { topic -> + FollowableTopic( + topic = topic, + isFollowed = topic.id in userData.followedTopics, + ) + }, + newsResources = searchResult.newsResources.map { news -> + UserNewsResource( + newsResource = news, + userData = userData, + ) + }, + ) +} 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 42a31f858..b0a41648f 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 @@ -22,11 +22,11 @@ import com.google.samples.apps.nowinandroid.core.model.data.Topic import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule +import kotlin.test.assertEquals import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals class GetFollowableTopicsUseCaseTest { diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt index b7c912c00..d99c429ee 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSource.kt @@ -23,12 +23,12 @@ import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream -import javax.inject.Inject /** * [NiaNetworkDataSource] implementation that provides static news resources to aid development @@ -67,9 +67,7 @@ class DemoNiaNetworkDataSource @Inject constructor( * Converts a list of [T] to change list of all the items in it where [idGetter] defines the * [NetworkChangeList.id] */ -private fun List.mapToChangeList( - idGetter: (T) -> String, -) = mapIndexed { index, item -> +private fun List.mapToChangeList(idGetter: (T) -> String) = mapIndexed { index, item -> NetworkChangeList( id = idGetter(item), changeListVersion = index, diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt index a97540f2b..a5a6c9a87 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/di/NetworkModule.kt @@ -28,11 +28,11 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton import kotlinx.serialization.json.Json import okhttp3.Call import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) @@ -46,9 +46,8 @@ internal object NetworkModule { @Provides @Singleton - fun providesDemoAssetManager( - @ApplicationContext context: Context, - ): DemoAssetManager = DemoAssetManager(context.assets::open) + fun providesDemoAssetManager(@ApplicationContext context: Context): DemoAssetManager = + DemoAssetManager(context.assets::open) @Provides @Singleton diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt index e9fe99d9e..181672bfa 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/retrofit/RetrofitNiaNetwork.kt @@ -23,6 +23,8 @@ import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import javax.inject.Inject +import javax.inject.Singleton import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import okhttp3.Call @@ -30,17 +32,13 @@ import okhttp3.MediaType.Companion.toMediaType import retrofit2.Retrofit import retrofit2.http.GET import retrofit2.http.Query -import javax.inject.Inject -import javax.inject.Singleton /** * Retrofit API declaration for NIA Network API */ private interface RetrofitNiaNetworkApi { @GET(value = "topics") - suspend fun getTopics( - @Query("id") ids: List?, - ): NetworkResponse> + suspend fun getTopics(@Query("id") ids: List?): NetworkResponse> @GET(value = "newsresources") suspend fun getNewsResources( @@ -48,14 +46,10 @@ private interface RetrofitNiaNetworkApi { ): NetworkResponse> @GET(value = "changelists/topics") - suspend fun getTopicChangeList( - @Query("after") after: Int?, - ): List + suspend fun getTopicChangeList(@Query("after") after: Int?): List @GET(value = "changelists/newsresources") - suspend fun getNewsResourcesChangeList( - @Query("after") after: Int?, - ): List + suspend fun getNewsResourcesChangeList(@Query("after") after: Int?): List } private const val NIA_BASE_URL = BuildConfig.BACKEND_URL diff --git a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSourceTest.kt b/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSourceTest.kt index e60cfeb3e..d1035e3a1 100644 --- a/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSourceTest.kt +++ b/core/network/src/test/kotlin/com/google/samples/apps/nowinandroid/core/network/demo/DemoNiaNetworkDataSourceTest.kt @@ -19,6 +19,7 @@ package com.google.samples.apps.nowinandroid.core.network.demo import JvmUnitTestDemoAssetManager import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import kotlin.test.assertEquals import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.runTest import kotlinx.datetime.LocalDateTime @@ -27,7 +28,6 @@ import kotlinx.datetime.toInstant import kotlinx.serialization.json.Json import org.junit.Before import org.junit.Test -import kotlin.test.assertEquals class DemoNiaNetworkDataSourceTest { diff --git a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt index 1c9e7ab63..a821d8bd2 100644 --- a/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt +++ b/core/notifications/src/main/kotlin/com/google/samples/apps/nowinandroid/core/notifications/SystemTrayNotifier.kt @@ -65,6 +65,7 @@ internal class SystemTrayNotifier @Inject constructor( val newsNotifications = truncatedNewsResources.map { newsResource -> createNewsNotification { + setSmallIcon( com.google.samples.apps.nowinandroid.core.common.R.drawable.core_common_ic_nia_notification, ) diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/JankStatsExtensions.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/JankStatsExtensions.kt index ef3de1059..8c04f2774 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/JankStatsExtensions.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/JankStatsExtensions.kt @@ -49,10 +49,7 @@ fun rememberMetricsStateHolder(): Holder { * @see TrackDisposableJank if you need to work with DisposableEffect to cleanup added state. */ @Composable -fun TrackJank( - vararg keys: Any, - reportMetric: suspend CoroutineScope.(state: Holder) -> Unit, -) { +fun TrackJank(vararg keys: Any, reportMetric: suspend CoroutineScope.(state: Holder) -> Unit) { val metrics = rememberMetricsStateHolder() LaunchedEffect(metrics, *keys) { reportMetric(metrics) diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt index afdb584a2..4ba4e31ea 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsFeed.kt @@ -73,7 +73,11 @@ fun LazyStaggeredGridScope.newsFeed( analyticsHelper.logNewsResourceOpened( newsResourceId = userNewsResource.id, ) - launchCustomChromeTab(context, Uri.parse(userNewsResource.url), backgroundColor) + launchCustomChromeTab( + context, + Uri.parse(userNewsResource.url), + backgroundColor, + ) onNewsResourceViewed(userNewsResource.id) }, diff --git a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt index e60c498eb..0f59a58f9 100644 --- a/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt +++ b/core/ui/src/main/kotlin/com/google/samples/apps/nowinandroid/core/ui/NewsResourceCard.kt @@ -66,12 +66,12 @@ import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource -import kotlinx.datetime.Instant -import kotlinx.datetime.toJavaInstant -import kotlinx.datetime.toJavaZoneId import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.util.Locale +import kotlinx.datetime.Instant +import kotlinx.datetime.toJavaInstant +import kotlinx.datetime.toJavaZoneId /** * [NewsResource] card used on the following screens: For You, Saved @@ -142,9 +142,7 @@ fun NewsResourceCardExpanded( } @Composable -fun NewsResourceHeaderImage( - headerImageUrl: String?, -) { +fun NewsResourceHeaderImage(headerImageUrl: String?) { var isLoading by remember { mutableStateOf(true) } var isError by remember { mutableStateOf(false) } val imageLoader = rememberAsyncImagePainter( @@ -189,19 +187,12 @@ fun NewsResourceHeaderImage( } @Composable -fun NewsResourceTitle( - newsResourceTitle: String, - modifier: Modifier = Modifier, -) { +fun NewsResourceTitle(newsResourceTitle: String, modifier: Modifier = Modifier) { Text(newsResourceTitle, style = MaterialTheme.typography.headlineSmall, modifier = modifier) } @Composable -fun BookmarkButton( - isBookmarked: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier, -) { +fun BookmarkButton(isBookmarked: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) { NiaIconToggleButton( checked = isBookmarked, onCheckedChange = { onClick() }, @@ -222,10 +213,7 @@ fun BookmarkButton( } @Composable -fun NotificationDot( - color: Color, - modifier: Modifier = Modifier, -) { +fun NotificationDot(color: Color, modifier: Modifier = Modifier) { val description = stringResource(R.string.core_ui_unread_resource_dot_content_description) Canvas( modifier = modifier @@ -247,10 +235,7 @@ fun dateFormatted(publishDate: Instant): String = DateTimeFormatter .format(publishDate.toJavaInstant()) @Composable -fun NewsResourceMetaData( - publishDate: Instant, - resourceType: String, -) { +fun NewsResourceMetaData(publishDate: Instant, resourceType: String) { val formattedDate = dateFormatted(publishDate) Text( if (resourceType.isNotBlank()) { @@ -263,9 +248,7 @@ fun NewsResourceMetaData( } @Composable -fun NewsResourceShortDescription( - newsResourceShortDescription: String, -) { +fun NewsResourceShortDescription(newsResourceShortDescription: String) { Text(newsResourceShortDescription, style = MaterialTheme.typography.bodyLarge) } diff --git a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt index 40f54e4a7..18d0a8be4 100644 --- a/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt +++ b/feature/bookmarks/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksScreenTest.kt @@ -36,11 +36,11 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.testing.TestLifecycleOwner import com.google.samples.apps.nowinandroid.core.testing.data.userNewsResourcesTestData import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState +import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue /** * UI tests for [BookmarksScreen] composable. diff --git a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt index f93602485..05e87691e 100644 --- a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt +++ b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModel.kt @@ -27,13 +27,13 @@ import com.google.samples.apps.nowinandroid.core.model.data.UserNewsResource import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class BookmarksViewModel @Inject constructor( diff --git a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt index 13d0baef0..3a36ebf97 100644 --- a/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt +++ b/feature/bookmarks/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/navigation/BookmarksNavigation.kt @@ -24,7 +24,10 @@ import com.google.samples.apps.nowinandroid.feature.bookmarks.BookmarksRoute const val BOOKMARKS_ROUTE = "bookmarks_route" -fun NavController.navigateToBookmarks(navOptions: NavOptions) = navigate(BOOKMARKS_ROUTE, navOptions) +fun NavController.navigateToBookmarks(navOptions: NavOptions) = navigate( + BOOKMARKS_ROUTE, + navOptions, +) fun NavGraphBuilder.bookmarksScreen( onTopicClick: (String) -> Unit, diff --git a/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt b/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt index 037e9db64..bfab59c5d 100644 --- a/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt +++ b/feature/bookmarks/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/bookmarks/BookmarksViewModelTest.kt @@ -23,6 +23,8 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserData import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success +import kotlin.test.assertEquals +import kotlin.test.assertIs import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -30,8 +32,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs /** * To learn more about how this test handles Flows created with stateIn, see diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt index 885020636..beafee7eb 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreen.kt @@ -429,10 +429,7 @@ private fun SingleTopicButton( } @Composable -fun TopicIcon( - imageUrl: String, - modifier: Modifier = Modifier, -) { +fun TopicIcon(imageUrl: String, modifier: Modifier = Modifier) { DynamicAsyncImage( placeholder = painterResource(R.drawable.feature_foryou_ic_icon_placeholder), imageUrl = imageUrl, @@ -482,10 +479,7 @@ private fun DeepLinkEffect( } } -private fun feedItemsSize( - feedState: NewsFeedUiState, - onboardingUiState: OnboardingUiState, -): Int { +private fun feedItemsSize(feedState: NewsFeedUiState, onboardingUiState: OnboardingUiState): Int { val feedSize = when (feedState) { NewsFeedUiState.Loading -> 0 is NewsFeedUiState.Success -> feedState.feed.size diff --git a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt index 85035a77a..3484d0294 100644 --- a/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt +++ b/feature/foryou/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModel.kt @@ -30,6 +30,7 @@ import com.google.samples.apps.nowinandroid.core.domain.GetFollowableTopicsUseCa import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -39,7 +40,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class ForYouViewModel @Inject constructor( @@ -147,15 +147,14 @@ class ForYouViewModel @Inject constructor( } } -private fun AnalyticsHelper.logNewsDeepLinkOpen(newsResourceId: String) = - logEvent( - AnalyticsEvent( - type = "news_deep_link_opened", - extras = listOf( - Param( - key = LINKED_NEWS_RESOURCE_ID, - value = newsResourceId, - ), +private fun AnalyticsHelper.logNewsDeepLinkOpen(newsResourceId: String) = logEvent( + AnalyticsEvent( + type = "news_deep_link_opened", + extras = listOf( + Param( + key = LINKED_NEWS_RESOURCE_ID, + value = newsResourceId, ), ), - ) + ), +) diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt index 14b67c64e..d34dfe3b4 100644 --- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt +++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouScreenScreenshotTests.kt @@ -31,6 +31,7 @@ import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.Loa import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.NotShown import com.google.samples.apps.nowinandroid.feature.foryou.OnboardingUiState.Shown import dagger.hilt.android.testing.HiltTestApplication +import java.util.TimeZone import org.junit.Before import org.junit.Rule import org.junit.Test @@ -39,7 +40,6 @@ import org.robolectric.RobolectricTestRunner import org.robolectric.annotation.Config import org.robolectric.annotation.GraphicsMode import org.robolectric.annotation.LooperMode -import java.util.TimeZone /** * Screenshot tests for the [ForYouScreen]. diff --git a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt index 2fbdf0a79..e9ddabc1c 100644 --- a/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt +++ b/feature/foryou/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/foryou/ForYouViewModelTest.kt @@ -35,6 +35,9 @@ import com.google.samples.apps.nowinandroid.core.testing.util.TestAnalyticsHelpe import com.google.samples.apps.nowinandroid.core.testing.util.TestSyncManager import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.feature.foryou.navigation.LINKED_NEWS_RESOURCE_ID +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -45,9 +48,6 @@ import kotlinx.datetime.Instant import org.junit.Before import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull -import kotlin.test.assertTrue /** * To learn more about how this test handles Flows created with stateIn, see diff --git a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt index a441f5a9d..052de3edc 100644 --- a/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt +++ b/feature/interests/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsScreenTest.kt @@ -25,13 +25,13 @@ import androidx.compose.ui.test.onAllNodesWithContentDescription import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData +import com.google.samples.apps.nowinandroid.core.ui.R as CoreUiR import com.google.samples.apps.nowinandroid.feature.interests.InterestsScreen import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState +import com.google.samples.apps.nowinandroid.feature.interests.R as InterestsR import org.junit.Before import org.junit.Rule import org.junit.Test -import com.google.samples.apps.nowinandroid.core.ui.R as CoreUiR -import com.google.samples.apps.nowinandroid.feature.interests.R as InterestsR /** * UI test for checking the correct behaviour of the Interests screen; diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt index b369ac5ab..6adf4be0a 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/InterestsViewModel.kt @@ -25,12 +25,12 @@ import com.google.samples.apps.nowinandroid.core.domain.TopicSortField import com.google.samples.apps.nowinandroid.core.model.data.FollowableTopic import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class InterestsViewModel @Inject constructor( diff --git a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt index 8a0f2d130..a05a46220 100644 --- a/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt +++ b/feature/interests/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/interests/navigation/InterestsNavigation.kt @@ -37,9 +37,7 @@ fun NavController.navigateToInterests(topicId: String? = null, navOptions: NavOp navigate(route, navOptions) } -fun NavGraphBuilder.interestsScreen( - onTopicClick: (String) -> Unit, -) { +fun NavGraphBuilder.interestsScreen(onTopicClick: (String) -> Unit) { composable( route = INTERESTS_ROUTE, arguments = listOf( diff --git a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt index 63d3c49b7..b66ecdec2 100644 --- a/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt +++ b/feature/interests/src/test/kotlin/com/google/samples/apps/nowinandroid/interests/InterestsViewModelTest.kt @@ -26,6 +26,7 @@ import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.feature.interests.InterestsUiState import com.google.samples.apps.nowinandroid.feature.interests.InterestsViewModel import com.google.samples.apps.nowinandroid.feature.interests.navigation.TOPIC_ID_ARG +import kotlin.test.assertEquals import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -33,7 +34,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals /** * To learn more about how this test handles Flows created with stateIn, see diff --git a/feature/settings/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt b/feature/settings/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt index 790b5964d..e0bb39c1b 100644 --- a/feature/settings/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt +++ b/feature/settings/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialogTest.kt @@ -71,17 +71,29 @@ class SettingsDialogTest { } // Check that all the possible settings are displayed. - composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_default)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_android)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_brand_default), + ).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_brand_android), + ).assertExists() composeTestRule.onNodeWithText( getString(R.string.feature_settings_dark_mode_config_system_default), ).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_light)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_dark)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dark_mode_config_light), + ).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dark_mode_config_dark), + ).assertExists() // Check that the correct settings are selected. - composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_android)).assertIsSelected() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dark_mode_config_dark)).assertIsSelected() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_brand_android), + ).assertIsSelected() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dark_mode_config_dark), + ).assertIsSelected() } @Test @@ -103,12 +115,20 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_preference), + ).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_yes), + ).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_no), + ).assertExists() // Check that the correct default dynamic color setting is selected. - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertIsSelected() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_no), + ).assertIsSelected() } @Test @@ -129,10 +149,16 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)) + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_preference), + ) .assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertDoesNotExist() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_yes), + ).assertDoesNotExist() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_no), + ).assertDoesNotExist() } @Test @@ -153,10 +179,16 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_preference)) + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_preference), + ) .assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_yes)).assertDoesNotExist() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_dynamic_color_no)).assertDoesNotExist() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_yes), + ).assertDoesNotExist() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_dynamic_color_no), + ).assertDoesNotExist() } @Test @@ -177,9 +209,13 @@ class SettingsDialogTest { ) } - composeTestRule.onNodeWithText(getString(R.string.feature_settings_privacy_policy)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_privacy_policy), + ).assertExists() composeTestRule.onNodeWithText(getString(R.string.feature_settings_licenses)).assertExists() - composeTestRule.onNodeWithText(getString(R.string.feature_settings_brand_guidelines)).assertExists() + composeTestRule.onNodeWithText( + getString(R.string.feature_settings_brand_guidelines), + ).assertExists() composeTestRule.onNodeWithText(getString(R.string.feature_settings_feedback)).assertExists() } } diff --git a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt index db60a6447..e73cc17b3 100644 --- a/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt +++ b/feature/settings/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsDialog.kt @@ -72,10 +72,7 @@ import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loa import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success @Composable -fun SettingsDialog( - onDismiss: () -> Unit, - viewModel: SettingsViewModel = hiltViewModel(), -) { +fun SettingsDialog(onDismiss: () -> Unit, viewModel: SettingsViewModel = hiltViewModel()) { val settingsUiState by viewModel.settingsUiState.collectAsStateWithLifecycle() SettingsDialog( onDismiss = onDismiss, @@ -177,7 +174,9 @@ private fun ColumnScope.SettingsPanel( } AnimatedVisibility(visible = settings.brand == DEFAULT && supportDynamicColor) { Column { - SettingsDialogSectionTitle(text = stringResource(string.feature_settings_dynamic_color_preference)) + SettingsDialogSectionTitle( + text = stringResource(string.feature_settings_dynamic_color_preference), + ) Column(Modifier.selectableGroup()) { SettingsDialogThemeChooserRow( text = stringResource(string.feature_settings_dynamic_color_yes), @@ -222,11 +221,7 @@ private fun SettingsDialogSectionTitle(text: String) { } @Composable -fun SettingsDialogThemeChooserRow( - text: String, - selected: Boolean, - onClick: () -> Unit, -) { +fun SettingsDialogThemeChooserRow(text: String, selected: Boolean, onClick: () -> Unit) { Row( Modifier .fillMaxWidth() 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 123c84d1c..e6974fac3 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 @@ -24,13 +24,13 @@ import com.google.samples.apps.nowinandroid.core.model.data.ThemeBrand import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject -import kotlin.time.Duration.Companion.seconds @HiltViewModel class SettingsViewModel @Inject constructor( diff --git a/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt b/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt index 9062abee6..482bc605e 100644 --- a/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt +++ b/feature/settings/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/settings/SettingsViewModelTest.kt @@ -22,6 +22,7 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserData import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success +import kotlin.test.assertEquals import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -29,7 +30,6 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals class SettingsViewModelTest { diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt index 5ac766675..9b9ad1c70 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicScreen.kt @@ -172,18 +172,16 @@ internal fun TopicScreen( } } -private fun topicItemsSize( - topicUiState: TopicUiState, - newsUiState: NewsUiState, -) = when (topicUiState) { - TopicUiState.Error -> 0 // Nothing - TopicUiState.Loading -> 1 // Loading bar - is TopicUiState.Success -> when (newsUiState) { - NewsUiState.Error -> 0 // Nothing - NewsUiState.Loading -> 1 // Loading bar - is NewsUiState.Success -> 2 + newsUiState.news.size // Toolbar, header +private fun topicItemsSize(topicUiState: TopicUiState, newsUiState: NewsUiState) = + when (topicUiState) { + TopicUiState.Error -> 0 // Nothing + TopicUiState.Loading -> 1 // Loading bar + is TopicUiState.Success -> when (newsUiState) { + NewsUiState.Error -> 0 // Nothing + NewsUiState.Loading -> 1 // Loading bar + is NewsUiState.Success -> 2 + newsUiState.news.size // Toolbar, header + } } -} private fun LazyListScope.topicBody( name: String, 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 255e40f8b..867dc68d9 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 @@ -30,6 +30,7 @@ import com.google.samples.apps.nowinandroid.core.result.Result import com.google.samples.apps.nowinandroid.core.result.asResult import com.google.samples.apps.nowinandroid.feature.topic.navigation.TopicArgs import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -37,7 +38,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import javax.inject.Inject @HiltViewModel class TopicViewModel @Inject constructor( diff --git a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt index 41804b634..ea63d15ad 100644 --- a/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt +++ b/feature/topic/src/main/kotlin/com/google/samples/apps/nowinandroid/feature/topic/navigation/TopicNavigation.kt @@ -37,7 +37,9 @@ const val TOPIC_ROUTE = "topic_route" internal class TopicArgs(val topicId: String) { constructor(savedStateHandle: SavedStateHandle) : - this(URLDecoder.decode(checkNotNull(savedStateHandle[TOPIC_ID_ARG]), URL_CHARACTER_ENCODING)) + this( + URLDecoder.decode(checkNotNull(savedStateHandle[TOPIC_ID_ARG]), URL_CHARACTER_ENCODING), + ) } fun NavController.navigateToTopic(topicId: String, navOptions: NavOptionsBuilder.() -> Unit = {}) { diff --git a/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt b/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt index 565732f59..07043de25 100644 --- a/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt +++ b/feature/topic/src/test/kotlin/com/google/samples/apps/nowinandroid/feature/topic/TopicViewModelTest.kt @@ -26,6 +26,8 @@ import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRe import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule import com.google.samples.apps.nowinandroid.feature.topic.navigation.TOPIC_ID_ARG +import kotlin.test.assertEquals +import kotlin.test.assertIs import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first @@ -36,8 +38,6 @@ import kotlinx.datetime.Instant import org.junit.Before import org.junit.Rule import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertIs /** * To learn more about how this test handles Flows created with stateIn, see diff --git a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt index c13b409e6..5e0e839cf 100644 --- a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt +++ b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/NeverSyncingSyncManager.kt @@ -17,9 +17,9 @@ package com.google.samples.apps.nowinandroid.core.sync.test import com.google.samples.apps.nowinandroid.core.data.util.SyncManager +import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -import javax.inject.Inject internal class NeverSyncingSyncManager @Inject constructor() : SyncManager { override val isSyncing: Flow = flowOf(false) diff --git a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt index d1c0f562f..075ab8ee2 100644 --- a/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt +++ b/sync/sync-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/sync/test/TestSyncModule.kt @@ -30,7 +30,5 @@ import dagger.hilt.testing.TestInstallIn ) internal interface TestSyncModule { @Binds - fun bindsSyncStatusMonitor( - syncStatusMonitor: NeverSyncingSyncManager, - ): SyncManager + fun bindsSyncStatusMonitor(syncStatusMonitor: NeverSyncingSyncManager): SyncManager }