Move models back into separate module

Change-Id: Ibe3e115e7618505a51fe34c8ebbf80691d344ad5
dt/clean-arch
Don Turner 11 months ago
parent 80be5c8c59
commit 86e322886a

@ -35,7 +35,7 @@ import androidx.test.espresso.Espresso
import androidx.test.espresso.NoActivityResumedException import androidx.test.espresso.NoActivityResumedException
import com.google.samples.apps.nowinandroid.MainActivity import com.google.samples.apps.nowinandroid.MainActivity
import com.google.samples.apps.nowinandroid.R import com.google.samples.apps.nowinandroid.R
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule import com.google.samples.apps.nowinandroid.core.rules.GrantPostNotificationsPermissionRule
import dagger.hilt.android.testing.BindValue import dagger.hilt.android.testing.BindValue

@ -44,8 +44,8 @@ import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor import com.google.samples.apps.nowinandroid.core.data.util.NetworkMonitor
import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor import com.google.samples.apps.nowinandroid.core.data.util.TimeZoneMonitor
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone import com.google.samples.apps.nowinandroid.core.ui.LocalTimeZone
import com.google.samples.apps.nowinandroid.ui.NiaApp import com.google.samples.apps.nowinandroid.ui.NiaApp

@ -20,7 +20,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading import com.google.samples.apps.nowinandroid.MainActivityUiState.Loading
import com.google.samples.apps.nowinandroid.MainActivityUiState.Success import com.google.samples.apps.nowinandroid.MainActivityUiState.Success
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.data.model.asEntity import com.google.samples.apps.nowinandroid.core.data.model.asEntity
import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
@ -46,7 +46,7 @@ internal class FakeNewsRepository @Inject constructor(
override fun getNewsResources( override fun getNewsResources(
query: NewsResourceQuery, query: NewsResourceQuery,
): Flow<List<NewsResource>> = ): Flow<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>> =
flow { flow {
emit( emit(
datasource datasource

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -28,7 +28,7 @@ import javax.inject.Inject
internal class FakeRecentSearchRepository @Inject constructor() : RecentSearchRepository { internal class FakeRecentSearchRepository @Inject constructor() : RecentSearchRepository {
override suspend fun insertOrReplaceRecentSearch(searchQuery: String) = Unit override suspend fun insertOrReplaceRecentSearch(searchQuery: String) = Unit
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> = override fun getRecentSearchQueries(limit: Int): Flow<List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery>> =
flowOf(emptyList()) flowOf(emptyList())
override suspend fun clearRecentSearches() = Unit override suspend fun clearRecentSearches() = Unit

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.domain.model.SearchResult import com.google.samples.apps.nowinandroid.core.model.SearchResult
import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
@ -28,6 +28,6 @@ import javax.inject.Inject
internal class FakeSearchContentsRepository @Inject constructor() : SearchContentsRepository { internal class FakeSearchContentsRepository @Inject constructor() : SearchContentsRepository {
override suspend fun populateFtsData() = Unit override suspend fun populateFtsData() = Unit
override fun searchContents(searchQuery: String): Flow<SearchResult> = flowOf() override fun searchContents(searchQuery: String): Flow<com.google.samples.apps.nowinandroid.core.model.SearchResult> = flowOf()
override fun getSearchContentsCount(): Flow<Int> = flowOf(1) override fun getSearchContentsCount(): Flow<Int> = flowOf(1)
} }

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
@ -40,10 +40,10 @@ internal class FakeTopicsRepository @Inject constructor(
@Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher, @Dispatcher(IO) private val ioDispatcher: CoroutineDispatcher,
private val datasource: FakeNiaNetworkDataSource, private val datasource: FakeNiaNetworkDataSource,
) : TopicsRepository { ) : TopicsRepository {
override fun getTopics(): Flow<List<Topic>> = flow { override fun getTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.Topic>> = flow {
emit( emit(
datasource.getTopics().map { datasource.getTopics().map {
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = it.id, id = it.id,
name = it.name, name = it.name,
shortDescription = it.shortDescription, shortDescription = it.shortDescription,
@ -55,7 +55,7 @@ internal class FakeTopicsRepository @Inject constructor(
) )
}.flowOn(ioDispatcher) }.flowOn(ioDispatcher)
override fun getTopic(id: String): Flow<Topic> = getTopics() override fun getTopic(id: String): Flow<com.google.samples.apps.nowinandroid.core.model.Topic> = getTopics()
.map { it.first { topic -> topic.id == id } } .map { it.first { topic -> topic.id == id } }
override suspend fun syncWith(synchronizer: Synchronizer) = true override suspend fun syncWith(synchronizer: Synchronizer) = true

@ -17,9 +17,9 @@
package com.google.samples.apps.nowinandroid.core.data.test.repository package com.google.samples.apps.nowinandroid.core.data.test.repository
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
@ -34,7 +34,7 @@ internal class FakeUserDataRepository @Inject constructor(
private val niaPreferencesDataSource: NiaPreferencesDataSource, private val niaPreferencesDataSource: NiaPreferencesDataSource,
) : UserDataRepository { ) : UserDataRepository {
override val userData: Flow<UserData> = override val userData: Flow<com.google.samples.apps.nowinandroid.core.model.UserData> =
niaPreferencesDataSource.userData niaPreferencesDataSource.userData
override suspend fun setFollowedTopicIds(followedTopicIds: Set<String>) = override suspend fun setFollowedTopicIds(followedTopicIds: Set<String>) =
@ -50,11 +50,11 @@ internal class FakeUserDataRepository @Inject constructor(
override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) = override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) =
niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed) niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed)
override suspend fun setThemeBrand(themeBrand: ThemeBrand) { override suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) {
niaPreferencesDataSource.setThemeBrand(themeBrand) niaPreferencesDataSource.setThemeBrand(themeBrand)
} }
override suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { override suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) {
niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig) niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig)
} }

@ -17,9 +17,10 @@
package com.google.samples.apps.nowinandroid.core.data.model package com.google.samples.apps.nowinandroid.core.data.model
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
fun RecentSearchQueryEntity.asExternalModel() = RecentSearchQuery( fun RecentSearchQueryEntity.asExternalModel() =
com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery(
query = query, query = query,
queriedDate = queriedDate, queriedDate = queriedDate,
) )

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.data.repository package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources import com.google.samples.apps.nowinandroid.core.model.mapToUserNewsResources
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
@ -44,7 +44,7 @@ class CompositeUserNewsResourceRepository @Inject constructor(
*/ */
override fun observeAll( override fun observeAll(
query: NewsResourceQuery, query: NewsResourceQuery,
): Flow<List<UserNewsResource>> = ): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> =
newsRepository.getNewsResources(query) newsRepository.getNewsResources(query)
.combine(userDataRepository.userData) { newsResources, userData -> .combine(userDataRepository.userData) { newsResources, userData ->
newsResources.mapToUserNewsResources(userData) newsResources.mapToUserNewsResources(userData)
@ -53,7 +53,7 @@ class CompositeUserNewsResourceRepository @Inject constructor(
/** /**
* Returns available news resources (joined with user data) for the followed topics. * Returns available news resources (joined with user data) for the followed topics.
*/ */
override fun observeAllForFollowedTopics(): Flow<List<UserNewsResource>> = override fun observeAllForFollowedTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> =
userDataRepository.userData.map { it.followedTopics }.distinctUntilChanged() userDataRepository.userData.map { it.followedTopics }.distinctUntilChanged()
.flatMapLatest { followedTopics -> .flatMapLatest { followedTopics ->
when { when {
@ -62,7 +62,7 @@ class CompositeUserNewsResourceRepository @Inject constructor(
} }
} }
override fun observeAllBookmarked(): Flow<List<UserNewsResource>> = override fun observeAllBookmarked(): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> =
userDataRepository.userData.map { it.bookmarkedNewsResources }.distinctUntilChanged() userDataRepository.userData.map { it.bookmarkedNewsResources }.distinctUntilChanged()
.flatMapLatest { bookmarkedNewsResources -> .flatMapLatest { bookmarkedNewsResources ->
when { when {

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel 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.dao.RecentSearchQueryDao
import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
@ -38,7 +38,7 @@ internal class DefaultRecentSearchRepository @Inject constructor(
) )
} }
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> = override fun getRecentSearchQueries(limit: Int): Flow<List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery>> =
recentSearchQueryDao.getRecentSearchQueryEntities(limit).map { searchQueries -> recentSearchQueryDao.getRecentSearchQueryEntities(limit).map { searchQueries ->
searchQueries.map { it.asExternalModel() } searchQueries.map { it.asExternalModel() }
} }

@ -23,7 +23,7 @@ import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity
import com.google.samples.apps.nowinandroid.core.domain.model.SearchResult import com.google.samples.apps.nowinandroid.core.model.SearchResult
import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository
import com.google.samples.apps.nowinandroid.core.network.Dispatcher 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.NiaDispatchers.IO
@ -59,7 +59,7 @@ internal class DefaultSearchContentsRepository @Inject constructor(
} }
} }
override fun searchContents(searchQuery: String): Flow<SearchResult> { override fun searchContents(searchQuery: String): Flow<com.google.samples.apps.nowinandroid.core.model.SearchResult> {
// Surround the query by asterisks to match the query when it's in the middle of // Surround the query by asterisks to match the query when it's in the middle of
// a word // a word
val newsResourceIds = newsResourceFtsDao.searchAllNewsResources("*$searchQuery*") val newsResourceIds = newsResourceFtsDao.searchAllNewsResources("*$searchQuery*")
@ -76,7 +76,7 @@ internal class DefaultSearchContentsRepository @Inject constructor(
.distinctUntilChanged() .distinctUntilChanged()
.flatMapLatest(topicDao::getTopicEntities) .flatMapLatest(topicDao::getTopicEntities)
return combine(newsResourcesFlow, topicsFlow) { newsResources, topics -> return combine(newsResourcesFlow, topicsFlow) { newsResources, topics ->
SearchResult( com.google.samples.apps.nowinandroid.core.model.SearchResult(
topics = topics.map { it.asExternalModel() }, topics = topics.map { it.asExternalModel() },
newsResources = newsResources.map { it.asExternalModel() }, newsResources = newsResources.map { it.asExternalModel() },
) )

@ -26,8 +26,8 @@ import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsRes
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
@ -57,7 +57,7 @@ internal class OfflineFirstNewsRepository @Inject constructor(
override fun getNewsResources( override fun getNewsResources(
query: NewsResourceQuery, query: NewsResourceQuery,
): Flow<List<NewsResource>> = newsResourceDao.getNewsResources( ): Flow<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>> = newsResourceDao.getNewsResources(
useFilterTopicIds = query.filterTopicIds != null, useFilterTopicIds = query.filterTopicIds != null,
filterTopicIds = query.filterTopicIds ?: emptySet(), filterTopicIds = query.filterTopicIds ?: emptySet(),
useFilterNewsIds = query.filterNewsIds != null, useFilterNewsIds = query.filterNewsIds != null,
@ -68,7 +68,7 @@ internal class OfflineFirstNewsRepository @Inject constructor(
override suspend fun syncWith(synchronizer: Synchronizer): Boolean { override suspend fun syncWith(synchronizer: Synchronizer): Boolean {
var isFirstSync = false var isFirstSync = false
return synchronizer.changeListSync( return synchronizer.changeListSync(
versionReader = ChangeListVersions::newsResourceVersion, versionReader = com.google.samples.apps.nowinandroid.core.model.ChangeListVersions::newsResourceVersion,
changeListFetcher = { currentVersion -> changeListFetcher = { currentVersion ->
isFirstSync = currentVersion <= 0 isFirstSync = currentVersion <= 0
network.getNewsResourceChangeList(after = currentVersion) network.getNewsResourceChangeList(after = currentVersion)

@ -21,8 +21,8 @@ import com.google.samples.apps.nowinandroid.core.data.util.changeListSync
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource import com.google.samples.apps.nowinandroid.core.network.NiaNetworkDataSource
@ -40,16 +40,16 @@ internal class OfflineFirstTopicsRepository @Inject constructor(
private val network: NiaNetworkDataSource, private val network: NiaNetworkDataSource,
) : TopicsRepository { ) : TopicsRepository {
override fun getTopics(): Flow<List<Topic>> = override fun getTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.Topic>> =
topicDao.getTopicEntities() topicDao.getTopicEntities()
.map { it.map(TopicEntity::asExternalModel) } .map { it.map(TopicEntity::asExternalModel) }
override fun getTopic(id: String): Flow<Topic> = override fun getTopic(id: String): Flow<com.google.samples.apps.nowinandroid.core.model.Topic> =
topicDao.getTopicEntity(id).map { it.asExternalModel() } topicDao.getTopicEntity(id).map { it.asExternalModel() }
override suspend fun syncWith(synchronizer: Synchronizer): Boolean = override suspend fun syncWith(synchronizer: Synchronizer): Boolean =
synchronizer.changeListSync( synchronizer.changeListSync(
versionReader = ChangeListVersions::topicVersion, versionReader = com.google.samples.apps.nowinandroid.core.model.ChangeListVersions::topicVersion,
changeListFetcher = { currentVersion -> changeListFetcher = { currentVersion ->
network.getTopicChangeList(after = currentVersion) network.getTopicChangeList(after = currentVersion)
}, },

@ -19,9 +19,9 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
@ -31,7 +31,7 @@ internal class OfflineFirstUserDataRepository @Inject constructor(
private val analyticsHelper: AnalyticsHelper, private val analyticsHelper: AnalyticsHelper,
) : UserDataRepository { ) : UserDataRepository {
override val userData: Flow<UserData> = override val userData: Flow<com.google.samples.apps.nowinandroid.core.model.UserData> =
niaPreferencesDataSource.userData niaPreferencesDataSource.userData
@VisibleForTesting @VisibleForTesting
@ -54,12 +54,12 @@ internal class OfflineFirstUserDataRepository @Inject constructor(
override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) = override suspend fun setNewsResourceViewed(newsResourceId: String, viewed: Boolean) =
niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed) niaPreferencesDataSource.setNewsResourceViewed(newsResourceId, viewed)
override suspend fun setThemeBrand(themeBrand: ThemeBrand) { override suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) {
niaPreferencesDataSource.setThemeBrand(themeBrand) niaPreferencesDataSource.setThemeBrand(themeBrand)
analyticsHelper.logThemeChanged(themeBrand.name) analyticsHelper.logThemeChanged(themeBrand.name)
} }
override suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { override suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) {
niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig) niaPreferencesDataSource.setDarkThemeConfig(darkThemeConfig)
analyticsHelper.logDarkThemeConfigChanged(darkThemeConfig.name) analyticsHelper.logDarkThemeConfigChanged(darkThemeConfig.name)
} }

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.data.util package com.google.samples.apps.nowinandroid.core.data.util
import android.util.Log import android.util.Log
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -60,9 +60,9 @@ private suspend fun <T> suspendRunCatching(block: suspend () -> T): Result<T> =
* implementation must guarantee this. * implementation must guarantee this.
*/ */
suspend fun Synchronizer.changeListSync( suspend fun Synchronizer.changeListSync(
versionReader: (ChangeListVersions) -> Int, versionReader: (com.google.samples.apps.nowinandroid.core.model.ChangeListVersions) -> Int,
changeListFetcher: suspend (Int) -> List<NetworkChangeList>, changeListFetcher: suspend (Int) -> List<NetworkChangeList>,
versionUpdater: ChangeListVersions.(Int) -> ChangeListVersions, versionUpdater: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions.(Int) -> com.google.samples.apps.nowinandroid.core.model.ChangeListVersions,
modelDeleter: suspend (List<String>) -> Unit, modelDeleter: suspend (List<String>) -> Unit,
modelUpdater: suspend (List<String>) -> Unit, modelUpdater: suspend (List<String>) -> Unit,
) = suspendRunCatching { ) = suspendRunCatching {

@ -17,9 +17,9 @@
package com.google.samples.apps.nowinandroid.core.data package com.google.samples.apps.nowinandroid.core.data
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources import com.google.samples.apps.nowinandroid.core.model.mapToUserNewsResources
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository 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.TestUserDataRepository
@ -134,7 +134,7 @@ class CompositeUserNewsResourceRepositoryTest {
} }
} }
private val sampleTopic1 = Topic( private val sampleTopic1 = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "Topic1", id = "Topic1",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -143,7 +143,7 @@ private val sampleTopic1 = Topic(
imageUrl = "image URL", imageUrl = "image URL",
) )
private val sampleTopic2 = Topic( private val sampleTopic2 = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "Topic2", id = "Topic2",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -153,7 +153,7 @@ private val sampleTopic2 = Topic(
) )
private val sampleNewsResources = listOf( private val sampleNewsResources = listOf(
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -166,7 +166,7 @@ private val sampleNewsResources = listOf(
type = "Video 📺", type = "Video 📺",
topics = listOf(sampleTopic1), topics = listOf(sampleTopic1),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "2", id = "2",
title = "Transformations and customisations in the Paging Library", title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed with Paging. " + content = "A demonstration of different operations that can be performed with Paging. " +
@ -178,7 +178,7 @@ private val sampleNewsResources = listOf(
type = "Video 📺", type = "Video 📺",
topics = listOf(sampleTopic1, sampleTopic2), topics = listOf(sampleTopic1, sampleTopic2),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "3", id = "3",
title = "Community tip on Paging", title = "Community tip on Paging",
content = "Tips for using the Paging library from the developer community", content = "Tips for using the Paging library from the developer community",

@ -16,13 +16,13 @@
package com.google.samples.apps.nowinandroid.core.data package com.google.samples.apps.nowinandroid.core.data
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import kotlinx.datetime.Clock.System import kotlinx.datetime.Clock.System
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue import org.junit.Assert.assertTrue
@ -37,7 +37,7 @@ class UserNewsResourceTest {
*/ */
@Test @Test
fun userNewsResourcesAreConstructedFromNewsResourcesAndUserData() { fun userNewsResourcesAreConstructedFromNewsResourcesAndUserData() {
val newsResource1 = NewsResource( val newsResource1 = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "N1", id = "N1",
title = "Test news title", title = "Test news title",
content = "Test news content", content = "Test news content",
@ -46,7 +46,7 @@ class UserNewsResourceTest {
publishDate = System.now(), publishDate = System.now(),
type = "Article 📚", type = "Article 📚",
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "T1", id = "T1",
name = "Topic 1", name = "Topic 1",
shortDescription = "Topic 1 short description", shortDescription = "Topic 1 short description",
@ -54,7 +54,7 @@ class UserNewsResourceTest {
url = "Topic 1 URL", url = "Topic 1 URL",
imageUrl = "Topic 1 image URL", imageUrl = "Topic 1 image URL",
), ),
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "T2", id = "T2",
name = "Topic 2", name = "Topic 2",
shortDescription = "Topic 2 short description", shortDescription = "Topic 2 short description",
@ -65,7 +65,7 @@ class UserNewsResourceTest {
), ),
) )
val userData = UserData( val userData = com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = setOf("N1"), bookmarkedNewsResources = setOf("N1"),
viewedNewsResources = setOf("N1"), viewedNewsResources = setOf("N1"),
followedTopics = setOf("T1"), followedTopics = setOf("T1"),
@ -75,7 +75,10 @@ class UserNewsResourceTest {
shouldHideOnboarding = true, shouldHideOnboarding = true,
) )
val userNewsResource = UserNewsResource(newsResource1, userData) val userNewsResource = com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource1,
userData
)
// Check that the simple field mappings have been done correctly. // Check that the simple field mappings have been done correctly.
assertEquals(newsResource1.id, userNewsResource.id) assertEquals(newsResource1.id, userNewsResource.id)
@ -89,7 +92,7 @@ class UserNewsResourceTest {
assertEquals(newsResource1.topics.size, userNewsResource.followableTopics.size) assertEquals(newsResource1.topics.size, userNewsResource.followableTopics.size)
for (topic in newsResource1.topics) { for (topic in newsResource1.topics) {
// Construct the expected FollowableTopic. // Construct the expected FollowableTopic.
val followableTopic = FollowableTopic( val followableTopic = com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = topic, topic = topic,
isFollowed = topic.id in userData.followedTopics, isFollowed = topic.id in userData.followedTopics,
) )

@ -32,8 +32,8 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList import com.google.samples.apps.nowinandroid.core.network.model.NetworkChangeList
@ -150,8 +150,8 @@ class OfflineFirstNewsRepositoryTest {
.map(PopulatedNewsResource::asExternalModel) .map(PopulatedNewsResource::asExternalModel)
assertEquals( assertEquals(
newsResourcesFromNetwork.map(NewsResource::id).sorted(), newsResourcesFromNetwork.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
newsResourcesFromDb.map(NewsResource::id).sorted(), newsResourcesFromDb.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
) )
// After sync version should be updated // After sync version should be updated
@ -176,7 +176,7 @@ class OfflineFirstNewsRepositoryTest {
// Delete half of the items on the network // Delete half of the items on the network
val deletedItems = newsResourcesFromNetwork val deletedItems = newsResourcesFromNetwork
.map(NewsResource::id) .map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id)
.partition { it.chars().sum() % 2 == 0 } .partition { it.chars().sum() % 2 == 0 }
.first .first
.toSet() .toSet()
@ -197,8 +197,8 @@ class OfflineFirstNewsRepositoryTest {
// Assert that items marked deleted on the network have been deleted locally // Assert that items marked deleted on the network have been deleted locally
assertEquals( assertEquals(
expected = (newsResourcesFromNetwork.map(NewsResource::id) - deletedItems).sorted(), expected = (newsResourcesFromNetwork.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id) - deletedItems).sorted(),
actual = newsResourcesFromDb.map(NewsResource::id).sorted(), actual = newsResourcesFromDb.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
) )
// After sync version should be updated // After sync version should be updated
@ -242,8 +242,8 @@ class OfflineFirstNewsRepositoryTest {
.map(PopulatedNewsResource::asExternalModel) .map(PopulatedNewsResource::asExternalModel)
assertEquals( assertEquals(
expected = newsResourcesFromNetwork.map(NewsResource::id).sorted(), expected = newsResourcesFromNetwork.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
actual = newsResourcesFromDb.map(NewsResource::id).sorted(), actual = newsResourcesFromDb.map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
) )
// After sync version should be updated // After sync version should be updated
@ -349,7 +349,7 @@ class OfflineFirstNewsRepositoryTest {
// that the user follows // that the user follows
assertEquals( assertEquals(
expected = followedNewsResourceIdsFromNetwork, expected = followedNewsResourceIdsFromNetwork,
actual = notifier.addedNewsResources.first().map(NewsResource::id).sorted(), actual = notifier.addedNewsResources.first().map(com.google.samples.apps.nowinandroid.core.model.NewsResource::id).sorted(),
) )
} }
@ -369,8 +369,8 @@ class OfflineFirstNewsRepositoryTest {
newsResourceDao.upsertNewsResources(networkNewsResources) newsResourceDao.upsertNewsResources(networkNewsResources)
val followedTopicIds = newsResources val followedTopicIds = newsResources
.flatMap(NewsResource::topics) .flatMap(com.google.samples.apps.nowinandroid.core.model.NewsResource::topics)
.map(Topic::id) .map(com.google.samples.apps.nowinandroid.core.model.Topic::id)
.toSet() .toSet()
// Follow all topics // Follow all topics

@ -25,7 +25,7 @@ import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
@ -144,7 +144,7 @@ class OfflineFirstTopicsRepositoryTest {
// Delete half of the items on the network // Delete half of the items on the network
val deletedItems = networkTopics val deletedItems = networkTopics
.map(Topic::id) .map(com.google.samples.apps.nowinandroid.core.model.Topic::id)
.partition { it.chars().sum() % 2 == 0 } .partition { it.chars().sum() % 2 == 0 }
.first .first
.toSet() .toSet()
@ -165,8 +165,8 @@ class OfflineFirstTopicsRepositoryTest {
// Assert that items marked deleted on the network have been deleted locally // Assert that items marked deleted on the network have been deleted locally
assertEquals( assertEquals(
networkTopics.map(Topic::id) - deletedItems, networkTopics.map(com.google.samples.apps.nowinandroid.core.model.Topic::id) - deletedItems,
dbTopics.map(Topic::id), dbTopics.map(com.google.samples.apps.nowinandroid.core.model.Topic::id),
) )
// After sync version should be updated // After sync version should be updated

@ -19,11 +19,11 @@ package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.NoOpAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore import com.google.samples.apps.nowinandroid.core.datastore.test.testUserPreferencesDataStore
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.TestScope
@ -66,7 +66,7 @@ class OfflineFirstUserDataRepositoryTest {
fun offlineFirstUserDataRepository_default_user_data_is_correct() = fun offlineFirstUserDataRepository_default_user_data_is_correct() =
testScope.runTest { testScope.runTest {
assertEquals( assertEquals(
UserData( com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = emptySet(), bookmarkedNewsResources = emptySet(),
viewedNewsResources = emptySet(), viewedNewsResources = emptySet(),
followedTopics = emptySet(), followedTopics = emptySet(),
@ -197,16 +197,16 @@ class OfflineFirstUserDataRepositoryTest {
@Test @Test
fun offlineFirstUserDataRepository_set_theme_brand_delegates_to_nia_preferences() = fun offlineFirstUserDataRepository_set_theme_brand_delegates_to_nia_preferences() =
testScope.runTest { testScope.runTest {
subject.setThemeBrand(ThemeBrand.ANDROID) subject.setThemeBrand(com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID)
assertEquals( assertEquals(
ThemeBrand.ANDROID, com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID,
subject.userData subject.userData
.map { it.themeBrand } .map { it.themeBrand }
.first(), .first(),
) )
assertEquals( assertEquals(
ThemeBrand.ANDROID, com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID,
niaPreferencesDataSource niaPreferencesDataSource
.userData .userData
.map { it.themeBrand } .map { it.themeBrand }
@ -237,16 +237,16 @@ class OfflineFirstUserDataRepositoryTest {
@Test @Test
fun offlineFirstUserDataRepository_set_dark_theme_config_delegates_to_nia_preferences() = fun offlineFirstUserDataRepository_set_dark_theme_config_delegates_to_nia_preferences() =
testScope.runTest { testScope.runTest {
subject.setDarkThemeConfig(DarkThemeConfig.DARK) subject.setDarkThemeConfig(com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK)
assertEquals( assertEquals(
DarkThemeConfig.DARK, com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK,
subject.userData subject.userData
.map { it.darkThemeConfig } .map { it.darkThemeConfig }
.first(), .first(),
) )
assertEquals( assertEquals(
DarkThemeConfig.DARK, com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK,
niaPreferencesDataSource niaPreferencesDataSource
.userData .userData
.map { it.darkThemeConfig } .map { it.darkThemeConfig }

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.data.repository package com.google.samples.apps.nowinandroid.core.data.repository
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
/** /**
@ -26,10 +26,10 @@ import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
class TestSynchronizer( class TestSynchronizer(
private val niaPreferences: NiaPreferencesDataSource, private val niaPreferences: NiaPreferencesDataSource,
) : Synchronizer { ) : Synchronizer {
override suspend fun getChangeListVersions(): ChangeListVersions = override suspend fun getChangeListVersions(): com.google.samples.apps.nowinandroid.core.model.ChangeListVersions =
niaPreferences.getChangeListVersions() niaPreferences.getChangeListVersions()
override suspend fun updateChangeListVersions( override suspend fun updateChangeListVersions(
update: ChangeListVersions.() -> ChangeListVersions, update: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions.() -> com.google.samples.apps.nowinandroid.core.model.ChangeListVersions,
) = niaPreferences.updateChangeListVersion(update) ) = niaPreferences.updateChangeListVersion(update)
} }

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.database.model package com.google.samples.apps.nowinandroid.core.database.model
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -49,7 +49,7 @@ class PopulatedNewsResourceKtTest {
val newsResource = populatedNewsResource.asExternalModel() val newsResource = populatedNewsResource.asExternalModel()
assertEquals( assertEquals(
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "news", title = "news",
content = "Hilt", content = "Hilt",
@ -58,7 +58,7 @@ class PopulatedNewsResourceKtTest {
type = "Video 📺", type = "Video 📺",
publishDate = Instant.fromEpochMilliseconds(1), publishDate = Instant.fromEpochMilliseconds(1),
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "3", id = "3",
name = "name", name = "name",
shortDescription = "short description", shortDescription = "short description",

@ -30,7 +30,7 @@ android {
} }
dependencies { dependencies {
api(projects.core.domain) api(projects.core.model)
implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.datetime)

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
/** /**
@ -42,7 +42,7 @@ data class NewsResourceEntity(
) )
fun NewsResourceEntity.asExternalModel() = fun NewsResourceEntity.asExternalModel() =
com.google.samples.apps.nowinandroid.core.domain.model.NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = id, id = id,
title = title, title = title,
content = content, content = content,

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.Embedded import androidx.room.Embedded
import androidx.room.Junction import androidx.room.Junction
import androidx.room.Relation import androidx.room.Relation
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
/** /**
* External data layer representation of a fully populated NiA news resource * External data layer representation of a fully populated NiA news resource
@ -40,7 +40,7 @@ data class PopulatedNewsResource(
) )
fun PopulatedNewsResource.asExternalModel() = fun PopulatedNewsResource.asExternalModel() =
com.google.samples.apps.nowinandroid.core.domain.model.NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = entity.id, id = entity.id,
title = entity.title, title = entity.title,
content = entity.content, content = entity.content,

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.core.database.model
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
/** /**
* Defines a topic a user may follow. * Defines a topic a user may follow.
@ -41,7 +41,7 @@ data class TopicEntity(
val imageUrl: String, val imageUrl: String,
) )
fun TopicEntity.asExternalModel() = com.google.samples.apps.nowinandroid.core.domain.model.Topic( fun TopicEntity.asExternalModel() = com.google.samples.apps.nowinandroid.core.model.Topic(
id = id, id = id,
name = name, name = name,
shortDescription = shortDescription, shortDescription = shortDescription,

@ -35,7 +35,7 @@ android {
dependencies { dependencies {
api(libs.androidx.dataStore.core) api(libs.androidx.dataStore.core)
api(projects.core.datastoreProto) api(projects.core.datastoreProto)
api(projects.core.domain) api(projects.core.model)
implementation(projects.core.common) implementation(projects.core.common)

@ -26,10 +26,10 @@ import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME
import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_DEFAULT import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_DEFAULT
import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_UNSPECIFIED import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.THEME_BRAND_UNSPECIFIED
import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.UNRECOGNIZED import com.google.samples.apps.nowinandroid.core.datastore.ThemeBrandProto.UNRECOGNIZED
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import java.io.IOException import java.io.IOException
@ -40,7 +40,7 @@ class NiaPreferencesDataSource @Inject constructor(
) { ) {
val userData = userPreferences.data val userData = userPreferences.data
.map { .map {
com.google.samples.apps.nowinandroid.core.domain.model.UserData( com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = it.bookmarkedNewsResourceIdsMap.keys, bookmarkedNewsResources = it.bookmarkedNewsResourceIdsMap.keys,
viewedNewsResources = it.viewedNewsResourceIdsMap.keys, viewedNewsResources = it.viewedNewsResourceIdsMap.keys,
followedTopics = it.followedTopicIdsMap.keys, followedTopics = it.followedTopicIdsMap.keys,
@ -49,9 +49,9 @@ class NiaPreferencesDataSource @Inject constructor(
THEME_BRAND_UNSPECIFIED, THEME_BRAND_UNSPECIFIED,
UNRECOGNIZED, UNRECOGNIZED,
THEME_BRAND_DEFAULT, THEME_BRAND_DEFAULT,
-> com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT -> com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
THEME_BRAND_ANDROID -> com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID THEME_BRAND_ANDROID -> com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
}, },
darkThemeConfig = when (it.darkThemeConfig) { darkThemeConfig = when (it.darkThemeConfig) {
null, null,
@ -59,12 +59,12 @@ class NiaPreferencesDataSource @Inject constructor(
DarkThemeConfigProto.UNRECOGNIZED, DarkThemeConfigProto.UNRECOGNIZED,
DARK_THEME_CONFIG_FOLLOW_SYSTEM, DARK_THEME_CONFIG_FOLLOW_SYSTEM,
-> ->
com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM
DARK_THEME_CONFIG_LIGHT -> DARK_THEME_CONFIG_LIGHT ->
com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.LIGHT com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.LIGHT
DARK_THEME_CONFIG_DARK -> com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK DARK_THEME_CONFIG_DARK -> com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
}, },
useDynamicColor = it.useDynamicColor, useDynamicColor = it.useDynamicColor,
shouldHideOnboarding = it.shouldHideOnboarding, shouldHideOnboarding = it.shouldHideOnboarding,
@ -102,12 +102,12 @@ class NiaPreferencesDataSource @Inject constructor(
} }
} }
suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand) { suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) {
userPreferences.updateData { userPreferences.updateData {
it.copy { it.copy {
this.themeBrand = when (themeBrand) { this.themeBrand = when (themeBrand) {
com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT -> ThemeBrandProto.THEME_BRAND_DEFAULT com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT -> ThemeBrandProto.THEME_BRAND_DEFAULT
com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID -> ThemeBrandProto.THEME_BRAND_ANDROID com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID -> ThemeBrandProto.THEME_BRAND_ANDROID
} }
} }
} }
@ -119,14 +119,14 @@ class NiaPreferencesDataSource @Inject constructor(
} }
} }
suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig) { suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) {
userPreferences.updateData { userPreferences.updateData {
it.copy { it.copy {
this.darkThemeConfig = when (darkThemeConfig) { this.darkThemeConfig = when (darkThemeConfig) {
com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM -> com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM ->
DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM DarkThemeConfigProto.DARK_THEME_CONFIG_FOLLOW_SYSTEM
com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.LIGHT -> DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.LIGHT -> DarkThemeConfigProto.DARK_THEME_CONFIG_LIGHT
com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK -> DarkThemeConfigProto.DARK_THEME_CONFIG_DARK com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK -> DarkThemeConfigProto.DARK_THEME_CONFIG_DARK
} }
} }
} }
@ -168,21 +168,21 @@ class NiaPreferencesDataSource @Inject constructor(
suspend fun getChangeListVersions() = userPreferences.data suspend fun getChangeListVersions() = userPreferences.data
.map { .map {
ChangeListVersions( com.google.samples.apps.nowinandroid.core.model.ChangeListVersions(
topicVersion = it.topicChangeListVersion, topicVersion = it.topicChangeListVersion,
newsResourceVersion = it.newsResourceChangeListVersion, newsResourceVersion = it.newsResourceChangeListVersion,
) )
} }
.firstOrNull() ?: ChangeListVersions() .firstOrNull() ?: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions()
/** /**
* Update the [ChangeListVersions] using [update]. * Update the [ChangeListVersions] using [update].
*/ */
suspend fun updateChangeListVersion(update: ChangeListVersions.() -> ChangeListVersions) { suspend fun updateChangeListVersion(update: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions.() -> com.google.samples.apps.nowinandroid.core.model.ChangeListVersions) {
try { try {
userPreferences.updateData { currentPreferences -> userPreferences.updateData { currentPreferences ->
val updatedChangeListVersions = update( val updatedChangeListVersions = update(
ChangeListVersions( com.google.samples.apps.nowinandroid.core.model.ChangeListVersions(
topicVersion = currentPreferences.topicChangeListVersion, topicVersion = currentPreferences.topicChangeListVersion,
newsResourceVersion = currentPreferences.newsResourceChangeListVersion, newsResourceVersion = currentPreferences.newsResourceChangeListVersion,
), ),

@ -25,6 +25,7 @@ android {
dependencies { dependencies {
api(libs.kotlinx.datetime) api(libs.kotlinx.datetime)
implementation(projects.core.model)
implementation(libs.javax.inject) implementation(libs.javax.inject)

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -46,5 +46,5 @@ interface NewsRepository : Syncable {
filterTopicIds = null, filterTopicIds = null,
filterNewsIds = null, filterNewsIds = null,
), ),
): Flow<List<NewsResource>> ): Flow<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>>
} }

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@ -27,7 +27,7 @@ interface RecentSearchRepository {
/** /**
* Get the recent search queries up to the number of queries specified as [limit]. * Get the recent search queries up to the number of queries specified as [limit].
*/ */
fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> fun getRecentSearchQueries(limit: Int): Flow<List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery>>
/** /**
* Insert or replace the [searchQuery] as part of the recent searches. * Insert or replace the [searchQuery] as part of the recent searches.

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.SearchResult import com.google.samples.apps.nowinandroid.core.model.SearchResult
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@ -32,7 +32,7 @@ interface SearchContentsRepository {
/** /**
* Query the contents matched with the [searchQuery] and returns it as a [Flow] of [SearchResult] * Query the contents matched with the [searchQuery] and returns it as a [Flow] of [SearchResult]
*/ */
fun searchContents(searchQuery: String): Flow<SearchResult> fun searchContents(searchQuery: String): Flow<com.google.samples.apps.nowinandroid.core.model.SearchResult>
fun getSearchContentsCount(): Flow<Int> fun getSearchContentsCount(): Flow<Int>
} }

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable import com.google.samples.apps.nowinandroid.core.domain.utils.Syncable
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -24,10 +24,10 @@ interface TopicsRepository : Syncable {
/** /**
* Gets the available topics as a stream * Gets the available topics as a stream
*/ */
fun getTopics(): Flow<List<Topic>> fun getTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.Topic>>
/** /**
* Gets data for a specific topic * Gets data for a specific topic
*/ */
fun getTopic(id: String): Flow<Topic> fun getTopic(id: String): Flow<com.google.samples.apps.nowinandroid.core.model.Topic>
} }

@ -16,9 +16,9 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface UserDataRepository { interface UserDataRepository {
@ -26,7 +26,7 @@ interface UserDataRepository {
/** /**
* Stream of [UserData] * Stream of [UserData]
*/ */
val userData: Flow<UserData> val userData: Flow<com.google.samples.apps.nowinandroid.core.model.UserData>
/** /**
* Sets the user's currently followed topics * Sets the user's currently followed topics
@ -51,12 +51,12 @@ interface UserDataRepository {
/** /**
* Sets the desired theme brand. * Sets the desired theme brand.
*/ */
suspend fun setThemeBrand(themeBrand: ThemeBrand) suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand)
/** /**
* Sets the desired dark theme config. * Sets the desired dark theme config.
*/ */
suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig)
/** /**
* Sets the preferred dynamic color config. * Sets the preferred dynamic color config.

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.repository package com.google.samples.apps.nowinandroid.core.domain.repository
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
/** /**
@ -31,15 +31,15 @@ interface UserNewsResourceRepository {
filterTopicIds = null, filterTopicIds = null,
filterNewsIds = null, filterNewsIds = null,
), ),
): Flow<List<UserNewsResource>> ): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>>
/** /**
* Returns available news resources for the user's followed topics as a stream. * Returns available news resources for the user's followed topics as a stream.
*/ */
fun observeAllForFollowedTopics(): Flow<List<UserNewsResource>> fun observeAllForFollowedTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>>
/** /**
* Returns the user's bookmarked news resources as a stream. * Returns the user's bookmarked news resources as a stream.
*/ */
fun observeAllBookmarked(): Flow<List<UserNewsResource>> fun observeAllBookmarked(): Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>>
} }

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.usecase package com.google.samples.apps.nowinandroid.core.domain.usecase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME
@ -37,13 +37,13 @@ class GetFollowableTopicsUseCase @Inject constructor(
* *
* @param sortBy - the field used to sort the topics. Default NONE = no sorting. * @param sortBy - the field used to sort the topics. Default NONE = no sorting.
*/ */
operator fun invoke(sortBy: TopicSortField = NONE): Flow<List<FollowableTopic>> = combine( operator fun invoke(sortBy: TopicSortField = NONE): Flow<List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>> = combine(
userDataRepository.userData, userDataRepository.userData,
topicsRepository.getTopics(), topicsRepository.getTopics(),
) { userData, topics -> ) { userData, topics ->
val followedTopics = topics val followedTopics = topics
.map { topic -> .map { topic ->
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = topic, topic = topic,
isFollowed = topic.id in userData.followedTopics, isFollowed = topic.id in userData.followedTopics,
) )

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.usecase package com.google.samples.apps.nowinandroid.core.domain.usecase
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import javax.inject.Inject import javax.inject.Inject
@ -27,6 +27,6 @@ import javax.inject.Inject
class GetRecentSearchQueriesUseCase @Inject constructor( class GetRecentSearchQueriesUseCase @Inject constructor(
private val recentSearchRepository: RecentSearchRepository, private val recentSearchRepository: RecentSearchRepository,
) { ) {
operator fun invoke(limit: Int = 10): Flow<List<RecentSearchQuery>> = operator fun invoke(limit: Int = 10): Flow<List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery>> =
recentSearchRepository.getRecentSearchQueries(limit) recentSearchRepository.getRecentSearchQueries(limit)
} }

@ -16,11 +16,11 @@
package com.google.samples.apps.nowinandroid.core.domain.usecase package com.google.samples.apps.nowinandroid.core.domain.usecase
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.SearchResult import com.google.samples.apps.nowinandroid.core.model.SearchResult
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.UserSearchResult import com.google.samples.apps.nowinandroid.core.model.UserSearchResult
import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -37,22 +37,22 @@ class GetSearchContentsUseCase @Inject constructor(
operator fun invoke( operator fun invoke(
searchQuery: String, searchQuery: String,
): Flow<UserSearchResult> = ): Flow<com.google.samples.apps.nowinandroid.core.model.UserSearchResult> =
searchContentsRepository.searchContents(searchQuery) searchContentsRepository.searchContents(searchQuery)
.mapToUserSearchResult(userDataRepository.userData) .mapToUserSearchResult(userDataRepository.userData)
} }
private fun Flow<SearchResult>.mapToUserSearchResult(userDataStream: Flow<UserData>): Flow<UserSearchResult> = private fun Flow<com.google.samples.apps.nowinandroid.core.model.SearchResult>.mapToUserSearchResult(userDataStream: Flow<com.google.samples.apps.nowinandroid.core.model.UserData>): Flow<com.google.samples.apps.nowinandroid.core.model.UserSearchResult> =
combine(userDataStream) { searchResult, userData -> combine(userDataStream) { searchResult, userData ->
UserSearchResult( com.google.samples.apps.nowinandroid.core.model.UserSearchResult(
topics = searchResult.topics.map { topic -> topics = searchResult.topics.map { topic ->
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = topic, topic = topic,
isFollowed = topic.id in userData.followedTopics, isFollowed = topic.id in userData.followedTopics,
) )
}, },
newsResources = searchResult.newsResources.map { news -> newsResources = searchResult.newsResources.map { news ->
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = news, newsResource = news,
userData = userData, userData = userData,
) )

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.domain.utils package com.google.samples.apps.nowinandroid.core.domain.utils
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
@ -25,9 +25,9 @@ import kotlinx.coroutines.flow.combine
* source for a [Syncable]. * source for a [Syncable].
*/ */
interface Synchronizer { interface Synchronizer {
suspend fun getChangeListVersions(): ChangeListVersions suspend fun getChangeListVersions(): com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
suspend fun updateChangeListVersions(update: ChangeListVersions.() -> ChangeListVersions) suspend fun updateChangeListVersions(update: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions.() -> com.google.samples.apps.nowinandroid.core.model.ChangeListVersions)
/** /**
* Syntactic sugar to call [Syncable.syncWith] while omitting the synchronizer argument * Syntactic sugar to call [Syncable.syncWith] while omitting the synchronizer argument

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.domain package com.google.samples.apps.nowinandroid.core.domain
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField.NAME
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
@ -54,9 +54,18 @@ class GetFollowableTopicsUseCaseTest {
// Check that the order hasn't changed and that the correct topics are marked as followed. // Check that the order hasn't changed and that the correct topics are marked as followed.
assertEquals( assertEquals(
listOf( listOf(
FollowableTopic(testTopics[0], true), com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
FollowableTopic(testTopics[1], false), testTopics[0],
FollowableTopic(testTopics[2], true), true
),
com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
testTopics[1],
false
),
com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
testTopics[2],
true
),
), ),
followableTopics.first(), followableTopics.first(),
) )
@ -79,14 +88,14 @@ class GetFollowableTopicsUseCaseTest {
testTopics testTopics
.sortedBy { it.name } .sortedBy { it.name }
.map { .map {
FollowableTopic(it, false) com.google.samples.apps.nowinandroid.core.model.FollowableTopic(it, false)
}, },
) )
} }
} }
private val testTopics = listOf( private val testTopics = listOf(
Topic("1", "Headlines", "", "", "", ""), com.google.samples.apps.nowinandroid.core.model.Topic("1", "Headlines", "", "", "", ""),
Topic("2", "Android Studio", "", "", "", ""), com.google.samples.apps.nowinandroid.core.model.Topic("2", "Android Studio", "", "", "", ""),
Topic("3", "Compose", "", "", "", ""), com.google.samples.apps.nowinandroid.core.model.Topic("3", "Compose", "", "", "", ""),
) )

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** /**
* Class summarizing the local version of each model for sync * Class summarizing the local version of each model for sync

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
enum class DarkThemeConfig { enum class DarkThemeConfig {
FOLLOW_SYSTEM, FOLLOW_SYSTEM,

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** /**
* A [topic] with the additional information for whether or not it is followed. * A [topic] with the additional information for whether or not it is followed.

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
import kotlinx.datetime.Instant import kotlinx.datetime.Instant

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** An entity that holds the search result */ /** An entity that holds the search result */
data class SearchResult( data class SearchResult(

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
enum class ThemeBrand { enum class ThemeBrand {
DEFAULT, DEFAULT,

@ -1,5 +1,5 @@
/* /*
* Copyright 2022 The Android Open Source Project * Copyright 2024 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** /**
* External data layer representation of a NiA Topic * External data layer representation of a NiA Topic

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** /**
* Class summarizing user interest data * Class summarizing user interest data

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
import kotlinx.datetime.Instant import kotlinx.datetime.Instant

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package com.google.samples.apps.nowinandroid.core.domain.model package com.google.samples.apps.nowinandroid.core.model
/** /**
* An entity of [SearchResult] with additional user information such as whether the user is * An entity of [SearchResult] with additional user information such as whether the user is

@ -41,7 +41,7 @@ secrets {
dependencies { dependencies {
api(libs.kotlinx.datetime) api(libs.kotlinx.datetime)
api(projects.core.common) api(projects.core.common)
api(projects.core.domain) api(projects.core.model)
implementation(libs.coil.kt) implementation(libs.coil.kt)
implementation(libs.coil.kt.svg) implementation(libs.coil.kt.svg)

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.network.model package com.google.samples.apps.nowinandroid.core.network.model
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**

@ -23,8 +23,7 @@ android {
} }
dependencies { dependencies {
api(projects.core.domain) implementation(projects.core.model)
implementation(projects.core.common) implementation(projects.core.common)
compileOnly(platform(libs.androidx.compose.bom)) compileOnly(platform(libs.androidx.compose.bom))

@ -16,12 +16,12 @@
package com.google.samples.apps.nowinandroid.core.notifications package com.google.samples.apps.nowinandroid.core.notifications
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import javax.inject.Inject import javax.inject.Inject
/** /**
* Implementation of [Notifier] which does nothing. Useful for tests and previews. * Implementation of [Notifier] which does nothing. Useful for tests and previews.
*/ */
internal class NoOpNotifier @Inject constructor() : Notifier { internal class NoOpNotifier @Inject constructor() : Notifier {
override fun postNewsNotifications(newsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.NewsResource>) = Unit override fun postNewsNotifications(newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>) = Unit
} }

@ -16,11 +16,11 @@
package com.google.samples.apps.nowinandroid.core.notifications package com.google.samples.apps.nowinandroid.core.notifications
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
/** /**
* Interface for creating notifications in the app * Interface for creating notifications in the app
*/ */
interface Notifier { interface Notifier {
fun postNewsNotifications(newsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.NewsResource>) fun postNewsNotifications(newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>)
} }

@ -32,7 +32,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.InboxStyle import androidx.core.app.NotificationCompat.InboxStyle
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -55,7 +55,7 @@ internal class SystemTrayNotifier @Inject constructor(
) : Notifier { ) : Notifier {
override fun postNewsNotifications( override fun postNewsNotifications(
newsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.NewsResource>, newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>,
) = with(context) { ) = with(context) {
if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) { if (checkSelfPermission(this, permission.POST_NOTIFICATIONS) != PERMISSION_GRANTED) {
return return
@ -108,7 +108,7 @@ internal class SystemTrayNotifier @Inject constructor(
* Creates an inbox style summary notification for news updates * Creates an inbox style summary notification for news updates
*/ */
private fun newsNotificationStyle( private fun newsNotificationStyle(
newsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.NewsResource>, newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>,
title: String, title: String,
): InboxStyle = newsResources ): InboxStyle = newsResources
.fold(InboxStyle()) { inboxStyle, newsResource -> inboxStyle.addLine(newsResource.title) } .fold(InboxStyle()) { inboxStyle, newsResource -> inboxStyle.addLine(newsResource.title) }
@ -150,7 +150,7 @@ private fun Context.ensureNotificationChannelExists() {
} }
private fun Context.newsPendingIntent( private fun Context.newsPendingIntent(
newsResource: com.google.samples.apps.nowinandroid.core.domain.model.NewsResource, newsResource: com.google.samples.apps.nowinandroid.core.model.NewsResource,
): PendingIntent? = PendingIntent.getActivity( ): PendingIntent? = PendingIntent.getActivity(
this, this,
NEWS_NOTIFICATION_REQUEST_CODE, NEWS_NOTIFICATION_REQUEST_CODE,
@ -165,4 +165,4 @@ private fun Context.newsPendingIntent(
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
) )
private fun com.google.samples.apps.nowinandroid.core.domain.model.NewsResource.newsDeepLinkUri() = "$DEEP_LINK_SCHEME_AND_HOST/$FOR_YOU_PATH/$id".toUri() private fun com.google.samples.apps.nowinandroid.core.model.NewsResource.newsDeepLinkUri() = "$DEEP_LINK_SCHEME_AND_HOST/$FOR_YOU_PATH/$id".toUri()

@ -18,12 +18,12 @@
package com.google.samples.apps.nowinandroid.core.testing.data package com.google.samples.apps.nowinandroid.core.testing.data
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
val followableTopicTestData: List<FollowableTopic> = listOf( val followableTopicTestData: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic> = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Headlines", name = "Headlines",
shortDescription = "News we want everyone to see", shortDescription = "News we want everyone to see",
@ -33,8 +33,8 @@ val followableTopicTestData: List<FollowableTopic> = listOf(
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "3", id = "3",
name = "UI", name = "UI",
shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets", shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets",
@ -44,8 +44,8 @@ val followableTopicTestData: List<FollowableTopic> = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "4", id = "4",
name = "Testing", name = "Testing",
shortDescription = "CI, Espresso, TestLab, etc", shortDescription = "CI, Espresso, TestLab, etc",

@ -18,11 +18,11 @@
package com.google.samples.apps.nowinandroid.core.testing.data package com.google.samples.apps.nowinandroid.core.testing.data
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
val newsResourcesTestData: List<NewsResource> = listOf( val newsResourcesTestData: List<com.google.samples.apps.nowinandroid.core.model.NewsResource> = listOf(
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Android Basics with Compose", title = "Android Basics with Compose",
content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey",
@ -32,7 +32,7 @@ val newsResourcesTestData: List<NewsResource> = listOf(
type = "Codelab", type = "Codelab",
topics = listOf(topicsTestData[1]), topics = listOf(topicsTestData[1]),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "2", id = "2",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -45,7 +45,7 @@ val newsResourcesTestData: List<NewsResource> = listOf(
type = "Video 📺", type = "Video 📺",
topics = listOf(topicsTestData[0], topicsTestData[1]), topics = listOf(topicsTestData[0], topicsTestData[1]),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "3", id = "3",
title = "Transformations and customisations in the Paging Library", title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed " + content = "A demonstration of different operations that can be performed " +
@ -58,7 +58,7 @@ val newsResourcesTestData: List<NewsResource> = listOf(
type = "Video 📺", type = "Video 📺",
topics = listOf(topicsTestData[2]), topics = listOf(topicsTestData[2]),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "4", id = "4",
title = "New Jetpack Release", title = "New Jetpack Release",
content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" + content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" +

@ -18,10 +18,10 @@
package com.google.samples.apps.nowinandroid.core.testing.data package com.google.samples.apps.nowinandroid.core.testing.data
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
val topicsTestData: List<Topic> = listOf( val topicsTestData: List<com.google.samples.apps.nowinandroid.core.model.Topic> = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Headlines", name = "Headlines",
shortDescription = "News we want everyone to see", shortDescription = "News we want everyone to see",
@ -29,7 +29,7 @@ val topicsTestData: List<Topic> = listOf(
imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f", imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f",
url = "", url = "",
), ),
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "3", id = "3",
name = "UI", name = "UI",
shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets", shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets",
@ -37,7 +37,7 @@ val topicsTestData: List<Topic> = listOf(
imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594", imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594",
url = "", url = "",
), ),
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "4", id = "4",
name = "Testing", name = "Testing",
shortDescription = "CI, Espresso, TestLab, etc", shortDescription = "CI, Espresso, TestLab, etc",

@ -18,17 +18,17 @@
package com.google.samples.apps.nowinandroid.core.testing.data package com.google.samples.apps.nowinandroid.core.testing.data
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant
val userNewsResourcesTestData: List<UserNewsResource> = UserData( val userNewsResourcesTestData: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource> = com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = setOf("1", "4"), bookmarkedNewsResources = setOf("1", "4"),
viewedNewsResources = setOf("1", "2", "4"), viewedNewsResources = setOf("1", "2", "4"),
followedTopics = emptySet(), followedTopics = emptySet(),
@ -38,8 +38,8 @@ val userNewsResourcesTestData: List<UserNewsResource> = UserData(
useDynamicColor = false, useDynamicColor = false,
).let { userData -> ).let { userData ->
listOf( listOf(
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Android Basics with Compose", title = "Android Basics with Compose",
content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey",
@ -59,8 +59,8 @@ val userNewsResourcesTestData: List<UserNewsResource> = UserData(
), ),
userData = userData, userData = userData,
), ),
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "2", id = "2",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -75,8 +75,8 @@ val userNewsResourcesTestData: List<UserNewsResource> = UserData(
), ),
userData = userData, userData = userData,
), ),
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "3", id = "3",
title = "Transformations and customisations in the Paging Library", title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed " + content = "A demonstration of different operations that can be performed " +
@ -91,8 +91,8 @@ val userNewsResourcesTestData: List<UserNewsResource> = UserData(
), ),
userData = userData, userData = userData,
), ),
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "4", id = "4",
title = "New Jetpack Release", title = "New Jetpack Release",
content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" + content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" +

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.testing.notifications package com.google.samples.apps.nowinandroid.core.testing.notifications
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.notifications.Notifier import com.google.samples.apps.nowinandroid.core.notifications.Notifier
/** /**
@ -24,11 +24,11 @@ import com.google.samples.apps.nowinandroid.core.notifications.Notifier
*/ */
class TestNotifier : Notifier { class TestNotifier : Notifier {
private val mutableAddedNewResources = mutableListOf<List<NewsResource>>() private val mutableAddedNewResources = mutableListOf<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>>()
val addedNewsResources: List<List<NewsResource>> = mutableAddedNewResources val addedNewsResources: List<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>> = mutableAddedNewResources
override fun postNewsNotifications(newsResources: List<NewsResource>) { override fun postNewsNotifications(newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>) {
mutableAddedNewResources.add(newsResources) mutableAddedNewResources.add(newsResources)
} }
} }

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.core.testing.repository package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
@ -31,15 +31,15 @@ class TestNewsRepository : NewsRepository {
/** /**
* The backing hot flow for the list of topics ids for testing. * The backing hot flow for the list of topics ids for testing.
*/ */
private val newsResourcesFlow: MutableSharedFlow<List<NewsResource>> = private val newsResourcesFlow: MutableSharedFlow<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>> =
MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override fun getNewsResources(query: NewsResourceQuery): Flow<List<NewsResource>> = override fun getNewsResources(query: NewsResourceQuery): Flow<List<com.google.samples.apps.nowinandroid.core.model.NewsResource>> =
newsResourcesFlow.map { newsResources -> newsResourcesFlow.map { newsResources ->
var result = newsResources var result = newsResources
query.filterTopicIds?.let { filterTopicIds -> query.filterTopicIds?.let { filterTopicIds ->
result = newsResources.filter { result = newsResources.filter {
it.topics.map(Topic::id).intersect(filterTopicIds).isNotEmpty() it.topics.map(com.google.samples.apps.nowinandroid.core.model.Topic::id).intersect(filterTopicIds).isNotEmpty()
} }
} }
query.filterNewsIds?.let { filterNewsIds -> query.filterNewsIds?.let { filterNewsIds ->
@ -51,7 +51,7 @@ class TestNewsRepository : NewsRepository {
/** /**
* A test-only API to allow controlling the list of news resources from tests. * A test-only API to allow controlling the list of news resources from tests.
*/ */
fun sendNewsResources(newsResources: List<NewsResource>) { fun sendNewsResources(newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>) {
newsResourcesFlow.tryEmit(newsResources) newsResourcesFlow.tryEmit(newsResources)
} }

@ -16,21 +16,21 @@
package com.google.samples.apps.nowinandroid.core.testing.repository package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
class TestRecentSearchRepository : RecentSearchRepository { class TestRecentSearchRepository : RecentSearchRepository {
private val cachedRecentSearches: MutableList<RecentSearchQuery> = mutableListOf() private val cachedRecentSearches: MutableList<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery> = mutableListOf()
override fun getRecentSearchQueries(limit: Int): Flow<List<RecentSearchQuery>> = override fun getRecentSearchQueries(limit: Int): Flow<List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery>> =
flowOf(cachedRecentSearches.sortedByDescending { it.queriedDate }.take(limit)) flowOf(cachedRecentSearches.sortedByDescending { it.queriedDate }.take(limit))
override suspend fun insertOrReplaceRecentSearch(searchQuery: String) { override suspend fun insertOrReplaceRecentSearch(searchQuery: String) {
cachedRecentSearches.add( cachedRecentSearches.add(
RecentSearchQuery( com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery(
searchQuery, searchQuery,
), ),
) )

@ -16,9 +16,9 @@
package com.google.samples.apps.nowinandroid.core.testing.repository package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.SearchResult import com.google.samples.apps.nowinandroid.core.model.SearchResult
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -28,14 +28,14 @@ import org.jetbrains.annotations.TestOnly
class TestSearchContentsRepository : SearchContentsRepository { class TestSearchContentsRepository : SearchContentsRepository {
private val cachedTopics = MutableStateFlow(emptyList<Topic>()) private val cachedTopics = MutableStateFlow(emptyList<com.google.samples.apps.nowinandroid.core.model.Topic>())
private val cachedNewsResources = MutableStateFlow(emptyList<NewsResource>()) private val cachedNewsResources = MutableStateFlow(emptyList<com.google.samples.apps.nowinandroid.core.model.NewsResource>())
override suspend fun populateFtsData() = Unit override suspend fun populateFtsData() = Unit
override fun searchContents(searchQuery: String): Flow<SearchResult> = override fun searchContents(searchQuery: String): Flow<com.google.samples.apps.nowinandroid.core.model.SearchResult> =
combine(cachedTopics, cachedNewsResources) { topics, news -> combine(cachedTopics, cachedNewsResources) { topics, news ->
SearchResult( com.google.samples.apps.nowinandroid.core.model.SearchResult(
topics = topics.filter { topics = topics.filter {
searchQuery in it.name || searchQuery in it.shortDescription || searchQuery in it.longDescription searchQuery in it.name || searchQuery in it.shortDescription || searchQuery in it.longDescription
}, },
@ -48,9 +48,9 @@ class TestSearchContentsRepository : SearchContentsRepository {
override fun getSearchContentsCount(): Flow<Int> = combine(cachedTopics, cachedNewsResources) { topics, news -> topics.size + news.size } override fun getSearchContentsCount(): Flow<Int> = combine(cachedTopics, cachedNewsResources) { topics, news -> topics.size + news.size }
@TestOnly @TestOnly
fun addTopics(topics: List<Topic>) = cachedTopics.update { it + topics } fun addTopics(topics: List<com.google.samples.apps.nowinandroid.core.model.Topic>) = cachedTopics.update { it + topics }
@TestOnly @TestOnly
fun addNewsResources(newsResources: List<NewsResource>) = fun addNewsResources(newsResources: List<com.google.samples.apps.nowinandroid.core.model.NewsResource>) =
cachedNewsResources.update { it + newsResources } cachedNewsResources.update { it + newsResources }
} }

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.core.testing.repository package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer import com.google.samples.apps.nowinandroid.core.domain.utils.Synchronizer
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
@ -28,18 +28,18 @@ class TestTopicsRepository : TopicsRepository {
/** /**
* The backing hot flow for the list of topics ids for testing. * The backing hot flow for the list of topics ids for testing.
*/ */
private val topicsFlow: MutableSharedFlow<List<Topic>> = private val topicsFlow: MutableSharedFlow<List<com.google.samples.apps.nowinandroid.core.model.Topic>> =
MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
override fun getTopics(): Flow<List<Topic>> = topicsFlow override fun getTopics(): Flow<List<com.google.samples.apps.nowinandroid.core.model.Topic>> = topicsFlow
override fun getTopic(id: String): Flow<Topic> = override fun getTopic(id: String): Flow<com.google.samples.apps.nowinandroid.core.model.Topic> =
topicsFlow.map { topics -> topics.find { it.id == id }!! } topicsFlow.map { topics -> topics.find { it.id == id }!! }
/** /**
* A test-only API to allow controlling the list of topics from tests. * A test-only API to allow controlling the list of topics from tests.
*/ */
fun sendTopics(topics: List<Topic>) { fun sendTopics(topics: List<com.google.samples.apps.nowinandroid.core.model.Topic>) {
topicsFlow.tryEmit(topics) topicsFlow.tryEmit(topics)
} }

@ -16,18 +16,18 @@
package com.google.samples.apps.nowinandroid.core.testing.repository package com.google.samples.apps.nowinandroid.core.testing.repository
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST import kotlinx.coroutines.channels.BufferOverflow.DROP_OLDEST
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
val emptyUserData = UserData( val emptyUserData = com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = emptySet(), bookmarkedNewsResources = emptySet(),
viewedNewsResources = emptySet(), viewedNewsResources = emptySet(),
followedTopics = emptySet(), followedTopics = emptySet(),
@ -41,11 +41,11 @@ class TestUserDataRepository : UserDataRepository {
/** /**
* The backing hot flow for the list of followed topic ids for testing. * The backing hot flow for the list of followed topic ids for testing.
*/ */
private val _userData = MutableSharedFlow<UserData>(replay = 1, onBufferOverflow = DROP_OLDEST) private val _userData = MutableSharedFlow<com.google.samples.apps.nowinandroid.core.model.UserData>(replay = 1, onBufferOverflow = DROP_OLDEST)
private val currentUserData get() = _userData.replayCache.firstOrNull() ?: emptyUserData private val currentUserData get() = _userData.replayCache.firstOrNull() ?: emptyUserData
override val userData: Flow<UserData> = _userData.filterNotNull() override val userData: Flow<com.google.samples.apps.nowinandroid.core.model.UserData> = _userData.filterNotNull()
override suspend fun setFollowedTopicIds(followedTopicIds: Set<String>) { override suspend fun setFollowedTopicIds(followedTopicIds: Set<String>) {
_userData.tryEmit(currentUserData.copy(followedTopics = followedTopicIds)) _userData.tryEmit(currentUserData.copy(followedTopics = followedTopicIds))
@ -90,13 +90,13 @@ class TestUserDataRepository : UserDataRepository {
} }
} }
override suspend fun setThemeBrand(themeBrand: ThemeBrand) { override suspend fun setThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) {
currentUserData.let { current -> currentUserData.let { current ->
_userData.tryEmit(current.copy(themeBrand = themeBrand)) _userData.tryEmit(current.copy(themeBrand = themeBrand))
} }
} }
override suspend fun setDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { override suspend fun setDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) {
currentUserData.let { current -> currentUserData.let { current ->
_userData.tryEmit(current.copy(darkThemeConfig = darkThemeConfig)) _userData.tryEmit(current.copy(darkThemeConfig = darkThemeConfig))
} }
@ -117,7 +117,7 @@ class TestUserDataRepository : UserDataRepository {
/** /**
* A test-only API to allow setting of user data directly. * A test-only API to allow setting of user data directly.
*/ */
fun setUserData(userData: UserData) { fun setUserData(userData: com.google.samples.apps.nowinandroid.core.model.UserData) {
_userData.tryEmit(userData) _userData.tryEmit(userData)
} }
} }

@ -30,7 +30,7 @@ dependencies {
api(libs.androidx.metrics) api(libs.androidx.metrics)
api(projects.core.analytics) api(projects.core.analytics)
api(projects.core.designsystem) api(projects.core.designsystem)
api(projects.core.domain) api(projects.core.model)
implementation(libs.androidx.browser) implementation(libs.androidx.browser)
implementation(libs.coil.kt) implementation(libs.coil.kt)

@ -19,19 +19,19 @@
package com.google.samples.apps.nowinandroid.core.ui package com.google.samples.apps.nowinandroid.core.ui
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
/** /**
* This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider)
* provides list of [FollowableTopic] for Composable previews. * provides list of [FollowableTopic] for Composable previews.
*/ */
class FollowableTopicPreviewParameterProvider : PreviewParameterProvider<List<com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic>> { class FollowableTopicPreviewParameterProvider : PreviewParameterProvider<List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>> {
override val values: Sequence<List<com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic>> override val values: Sequence<List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>>
get() = sequenceOf( get() = sequenceOf(
listOf( listOf(
com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = com.google.samples.apps.nowinandroid.core.domain.model.Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Headlines", name = "Headlines",
shortDescription = "News we want everyone to see", shortDescription = "News we want everyone to see",
@ -41,8 +41,8 @@ class FollowableTopicPreviewParameterProvider : PreviewParameterProvider<List<co
), ),
isFollowed = false, isFollowed = false,
), ),
com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = com.google.samples.apps.nowinandroid.core.domain.model.Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "3", id = "3",
name = "UI", name = "UI",
shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets", shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets",
@ -52,8 +52,8 @@ class FollowableTopicPreviewParameterProvider : PreviewParameterProvider<List<co
), ),
isFollowed = true, isFollowed = true,
), ),
com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = com.google.samples.apps.nowinandroid.core.domain.model.Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "4", id = "4",
name = "Testing", name = "Testing",
shortDescription = "CI, Espresso, TestLab, etc", shortDescription = "CI, Espresso, TestLab, etc",

@ -39,7 +39,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
/** /**
* An extension on [LazyListScope] defining a feed with news resources. * An extension on [LazyListScope] defining a feed with news resources.
@ -120,7 +120,7 @@ sealed interface NewsFeedUiState {
/** /**
* The list of news resources contained in this feed. * The list of news resources contained in this feed.
*/ */
val feed: List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>, val feed: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) : NewsFeedUiState ) : NewsFeedUiState
} }
@ -144,7 +144,7 @@ private fun NewsFeedLoadingPreview() {
@Composable @Composable
private fun NewsFeedContentPreview( private fun NewsFeedContentPreview(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
LazyVerticalStaggeredGrid(columns = StaggeredGridCells.Adaptive(300.dp)) { LazyVerticalStaggeredGrid(columns = StaggeredGridCells.Adaptive(300.dp)) {

@ -64,9 +64,9 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaIconT
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTopicTag
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.toJavaInstant import kotlinx.datetime.toJavaInstant
import kotlinx.datetime.toJavaZoneId import kotlinx.datetime.toJavaZoneId
@ -81,7 +81,7 @@ import java.util.Locale
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun NewsResourceCardExpanded( fun NewsResourceCardExpanded(
userNewsResource: com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource, userNewsResource: com.google.samples.apps.nowinandroid.core.model.UserNewsResource,
isBookmarked: Boolean, isBookmarked: Boolean,
hasBeenViewed: Boolean, hasBeenViewed: Boolean,
onToggleBookmark: () -> Unit, onToggleBookmark: () -> Unit,
@ -273,7 +273,7 @@ fun NewsResourceShortDescription(
@Composable @Composable
fun NewsResourceTopics( fun NewsResourceTopics(
topics: List<com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic>, topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
@ -334,7 +334,7 @@ private fun BookmarkButtonBookmarkedPreview() {
@Composable @Composable
private fun ExpandedNewsResourcePreview( private fun ExpandedNewsResourcePreview(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
CompositionLocalProvider( CompositionLocalProvider(
LocalInspectionMode provides true, LocalInspectionMode provides true,

@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.LocalAnalyticsHelper
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
/** /**
* Extension function for displaying a [List] of [NewsResourceCardExpanded] backed by a list of * Extension function for displaying a [List] of [NewsResourceCardExpanded] backed by a list of
@ -34,8 +34,8 @@ import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
* When a news resource card is tapped it will open the news resource URL in a Chrome Custom Tab. * When a news resource card is tapped it will open the news resource URL in a Chrome Custom Tab.
*/ */
fun LazyListScope.userNewsResourceCardItems( fun LazyListScope.userNewsResourceCardItems(
items: List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>, items: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
onToggleBookmark: (item: com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource) -> Unit, onToggleBookmark: (item: com.google.samples.apps.nowinandroid.core.model.UserNewsResource) -> Unit,
onNewsResourceViewed: (String) -> Unit, onNewsResourceViewed: (String) -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
itemModifier: Modifier = Modifier, itemModifier: Modifier = Modifier,

@ -19,12 +19,12 @@
package com.google.samples.apps.nowinandroid.core.ui package com.google.samples.apps.nowinandroid.core.ui
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
@ -35,26 +35,26 @@ import kotlinx.datetime.toInstant
* This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider) * This [PreviewParameterProvider](https://developer.android.com/reference/kotlin/androidx/compose/ui/tooling/preview/PreviewParameterProvider)
* provides list of [UserNewsResource] for Composable previews. * provides list of [UserNewsResource] for Composable previews.
*/ */
class UserNewsResourcePreviewParameterProvider : PreviewParameterProvider<List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>> { class UserNewsResourcePreviewParameterProvider : PreviewParameterProvider<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> {
override val values: Sequence<List<com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource>> = sequenceOf(newsResources) override val values: Sequence<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> = sequenceOf(newsResources)
} }
object PreviewParameterData { object PreviewParameterData {
private val userData: com.google.samples.apps.nowinandroid.core.domain.model.UserData = private val userData: com.google.samples.apps.nowinandroid.core.model.UserData =
com.google.samples.apps.nowinandroid.core.domain.model.UserData( com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = setOf("1", "3"), bookmarkedNewsResources = setOf("1", "3"),
viewedNewsResources = setOf("1", "2", "4"), viewedNewsResources = setOf("1", "2", "4"),
followedTopics = emptySet(), followedTopics = emptySet(),
themeBrand = com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID, themeBrand = com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID,
darkThemeConfig = com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK, darkThemeConfig = com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK,
shouldHideOnboarding = true, shouldHideOnboarding = true,
useDynamicColor = false, useDynamicColor = false,
) )
val topics = listOf( val topics = listOf(
com.google.samples.apps.nowinandroid.core.domain.model.Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Headlines", name = "Headlines",
shortDescription = "News we want everyone to see", shortDescription = "News we want everyone to see",
@ -62,7 +62,7 @@ object PreviewParameterData {
imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f", imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_Headlines.svg?alt=media&token=506faab0-617a-4668-9e63-4a2fb996603f",
url = "", url = "",
), ),
com.google.samples.apps.nowinandroid.core.domain.model.Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "3", id = "3",
name = "UI", name = "UI",
shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets", shortDescription = "Material Design, Navigation, Text, Paging, Accessibility (a11y), Internationalization (i18n), Localization (l10n), Animations, Large Screens, Widgets",
@ -70,7 +70,7 @@ object PreviewParameterData {
imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594", imageUrl = "https://firebasestorage.googleapis.com/v0/b/now-in-android.appspot.com/o/img%2Fic_topic_UI.svg?alt=media&token=0ee1842b-12e8-435f-87ba-a5bb02c47594",
url = "", url = "",
), ),
com.google.samples.apps.nowinandroid.core.domain.model.Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "4", id = "4",
name = "Testing", name = "Testing",
shortDescription = "CI, Espresso, TestLab, etc", shortDescription = "CI, Espresso, TestLab, etc",
@ -81,8 +81,8 @@ object PreviewParameterData {
) )
val newsResources = listOf( val newsResources = listOf(
com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = com.google.samples.apps.nowinandroid.core.domain.model.NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Android Basics with Compose", title = "Android Basics with Compose",
content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey", content = "We released the first two units of Android Basics with Compose, our first free course that teaches Android Development with Jetpack Compose to anyone; you do not need any prior programming experience other than basic computer literacy to get started. Youll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Androids modern toolkit that simplifies and accelerates native UI development. These two units are just the beginning; more will be coming soon. Check out Android Basics with Compose to get started on your Android development journey",
@ -102,8 +102,8 @@ object PreviewParameterData {
), ),
userData = userData, userData = userData,
), ),
com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = com.google.samples.apps.nowinandroid.core.domain.model.NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "2", id = "2",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -118,8 +118,8 @@ object PreviewParameterData {
), ),
userData = userData, userData = userData,
), ),
com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = com.google.samples.apps.nowinandroid.core.domain.model.NewsResource( newsResource = com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "3", id = "3",
title = "Transformations and customisations in the Paging Library", title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed " + content = "A demonstration of different operations that can be performed " +

@ -66,7 +66,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.LocalTintTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Loading
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success
@ -266,7 +266,7 @@ private fun LoadingStatePreview() {
@Composable @Composable
private fun BookmarksGridPreview( private fun BookmarksGridPreview(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
BookmarksGrid( BookmarksGrid(

@ -21,7 +21,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
@ -46,7 +46,7 @@ class BookmarksViewModel @Inject constructor(
val feedUiState: StateFlow<NewsFeedUiState> = val feedUiState: StateFlow<NewsFeedUiState> =
userNewsResourceRepository.observeAllBookmarked() userNewsResourceRepository.observeAllBookmarked()
.map<List<UserNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success) .map<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>, NewsFeedUiState>(NewsFeedUiState::Success)
.onStart { emit(Loading) } .onStart { emit(Loading) }
.stateIn( .stateIn(
scope = viewModelScope, scope = viewModelScope,

@ -96,7 +96,7 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
@ -136,7 +136,7 @@ internal fun ForYouScreen(
isSyncing: Boolean, isSyncing: Boolean,
onboardingUiState: OnboardingUiState, onboardingUiState: OnboardingUiState,
feedState: NewsFeedUiState, feedState: NewsFeedUiState,
deepLinkedUserNewsResource: UserNewsResource?, deepLinkedUserNewsResource: com.google.samples.apps.nowinandroid.core.model.UserNewsResource?,
onTopicCheckedChanged: (String, Boolean) -> Unit, onTopicCheckedChanged: (String, Boolean) -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
onDeepLinkOpened: (String) -> Unit, onDeepLinkOpened: (String) -> Unit,
@ -464,7 +464,7 @@ private fun NotificationPermissionEffect() {
@Composable @Composable
private fun DeepLinkEffect( private fun DeepLinkEffect(
userNewsResource: UserNewsResource?, userNewsResource: com.google.samples.apps.nowinandroid.core.model.UserNewsResource?,
onDeepLinkOpened: (String) -> Unit, onDeepLinkOpened: (String) -> Unit,
) { ) {
val context = LocalContext.current val context = LocalContext.current
@ -505,7 +505,7 @@ private fun feedItemsSize(
@Composable @Composable
fun ForYouScreenPopulatedFeed( fun ForYouScreenPopulatedFeed(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
ForYouScreen( ForYouScreen(
@ -529,7 +529,7 @@ fun ForYouScreenPopulatedFeed(
@Composable @Composable
fun ForYouScreenOfflinePopulatedFeed( fun ForYouScreenOfflinePopulatedFeed(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
ForYouScreen( ForYouScreen(
@ -553,7 +553,7 @@ fun ForYouScreenOfflinePopulatedFeed(
@Composable @Composable
fun ForYouScreenTopicSelection( fun ForYouScreenTopicSelection(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
ForYouScreen( ForYouScreen(
@ -599,7 +599,7 @@ fun ForYouScreenLoading() {
@Composable @Composable
fun ForYouScreenPopulatedAndLoading( fun ForYouScreenPopulatedAndLoading(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
ForYouScreen( ForYouScreen(

@ -16,7 +16,7 @@
package com.google.samples.apps.nowinandroid.feature.foryou package com.google.samples.apps.nowinandroid.feature.foryou
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
/** /**
* A sealed hierarchy describing the onboarding state for the for you screen. * A sealed hierarchy describing the onboarding state for the for you screen.
@ -41,7 +41,7 @@ sealed interface OnboardingUiState {
* There is a onboarding state, with the given lists of topics. * There is a onboarding state, with the given lists of topics.
*/ */
data class Shown( data class Shown(
val topics: List<FollowableTopic>, val topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
) : OnboardingUiState { ) : OnboardingUiState {
/** /**
* True if the onboarding can be dismissed. * True if the onboarding can be dismissed.

@ -20,11 +20,11 @@ import androidx.lifecycle.SavedStateHandle
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent 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.AnalyticsEvent.Param
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources import com.google.samples.apps.nowinandroid.core.model.mapToUserNewsResources
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository
@ -157,8 +157,8 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = listOf( topics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -168,8 +168,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -179,8 +179,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Tools", name = "Tools",
shortDescription = "", shortDescription = "",
@ -218,8 +218,8 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = listOf( topics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -229,8 +229,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -240,8 +240,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Tools", name = "Tools",
shortDescription = "", shortDescription = "",
@ -315,7 +315,7 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = sampleTopics.map { topics = sampleTopics.map {
FollowableTopic(it, false) com.google.samples.apps.nowinandroid.core.model.FollowableTopic(it, false)
}, },
), ),
viewModel.onboardingUiState.value, viewModel.onboardingUiState.value,
@ -333,7 +333,10 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = sampleTopics.map { topics = sampleTopics.map {
FollowableTopic(it, it.id == followedTopicId) com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
it,
it.id == followedTopicId
)
}, },
), ),
viewModel.onboardingUiState.value, viewModel.onboardingUiState.value,
@ -344,8 +347,14 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
NewsFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
UserNewsResource(sampleNewsResources[1], userData), com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
UserNewsResource(sampleNewsResources[2], userData), sampleNewsResources[1],
userData
),
com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
sampleNewsResources[2],
userData
),
), ),
), ),
viewModel.feedState.value, viewModel.feedState.value,
@ -371,8 +380,8 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
OnboardingUiState.Shown( OnboardingUiState.Shown(
topics = listOf( topics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -382,8 +391,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -393,8 +402,8 @@ class ForYouViewModelTest {
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = Topic( topic = com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Tools", name = "Tools",
shortDescription = "", shortDescription = "",
@ -452,8 +461,14 @@ class ForYouViewModelTest {
assertEquals( assertEquals(
NewsFeedUiState.Success( NewsFeedUiState.Success(
feed = listOf( feed = listOf(
UserNewsResource(newsResource = sampleNewsResources[1], userDataExpected), com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
UserNewsResource(newsResource = sampleNewsResources[2], userDataExpected), newsResource = sampleNewsResources[1],
userDataExpected
),
com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = sampleNewsResources[2],
userDataExpected
),
), ),
), ),
viewModel.feedState.value, viewModel.feedState.value,
@ -473,7 +488,7 @@ class ForYouViewModelTest {
savedStateHandle[LINKED_NEWS_RESOURCE_ID] = sampleNewsResources.first().id savedStateHandle[LINKED_NEWS_RESOURCE_ID] = sampleNewsResources.first().id
assertEquals( assertEquals(
expected = UserNewsResource( expected = com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = sampleNewsResources.first(), newsResource = sampleNewsResources.first(),
userData = emptyUserData, userData = emptyUserData,
), ),
@ -507,7 +522,7 @@ class ForYouViewModelTest {
} }
private val sampleTopics = listOf( private val sampleTopics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -515,7 +530,7 @@ private val sampleTopics = listOf(
url = "URL", url = "URL",
imageUrl = "image URL", imageUrl = "image URL",
), ),
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -523,7 +538,7 @@ private val sampleTopics = listOf(
url = "URL", url = "URL",
imageUrl = "image URL", imageUrl = "image URL",
), ),
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = "Tools", name = "Tools",
shortDescription = "", shortDescription = "",
@ -534,7 +549,7 @@ private val sampleTopics = listOf(
) )
private val sampleNewsResources = listOf( private val sampleNewsResources = listOf(
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -546,7 +561,7 @@ private val sampleNewsResources = listOf(
publishDate = Instant.parse("2021-11-09T00:00:00.000Z"), publishDate = Instant.parse("2021-11-09T00:00:00.000Z"),
type = "Video 📺", type = "Video 📺",
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",
@ -556,7 +571,7 @@ private val sampleNewsResources = listOf(
), ),
), ),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "2", id = "2",
title = "Transformations and customisations in the Paging Library", title = "Transformations and customisations in the Paging Library",
content = "A demonstration of different operations that can be performed with Paging. " + content = "A demonstration of different operations that can be performed with Paging. " +
@ -567,7 +582,7 @@ private val sampleNewsResources = listOf(
publishDate = Instant.parse("2021-11-01T00:00:00.000Z"), publishDate = Instant.parse("2021-11-01T00:00:00.000Z"),
type = "Video 📺", type = "Video 📺",
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",
@ -577,7 +592,7 @@ private val sampleNewsResources = listOf(
), ),
), ),
), ),
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "3", id = "3",
title = "Community tip on Paging", title = "Community tip on Paging",
content = "Tips for using the Paging library from the developer community", content = "Tips for using the Paging library from the developer community",
@ -586,7 +601,7 @@ private val sampleNewsResources = listOf(
publishDate = Instant.parse("2021-11-08T00:00:00.000Z"), publishDate = Instant.parse("2021-11-08T00:00:00.000Z"),
type = "Video 📺", type = "Video 📺",
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = "UI", name = "UI",
shortDescription = "", shortDescription = "",

@ -29,7 +29,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaBackground
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaLoadingWheel
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider import com.google.samples.apps.nowinandroid.core.ui.FollowableTopicPreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
@ -99,7 +99,7 @@ private fun InterestsEmptyScreen() {
@Composable @Composable
fun InterestsScreenPopulated( fun InterestsScreenPopulated(
@PreviewParameter(FollowableTopicPreviewParameterProvider::class) @PreviewParameter(FollowableTopicPreviewParameterProvider::class)
followableTopics: List<FollowableTopic>, followableTopics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
) { ) {
NiaTheme { NiaTheme {
NiaBackground { NiaBackground {

@ -19,7 +19,7 @@ package com.google.samples.apps.nowinandroid.feature.interests
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField import com.google.samples.apps.nowinandroid.core.domain.usecase.TopicSortField
@ -67,7 +67,7 @@ sealed interface InterestsUiState {
data class Interests( data class Interests(
val selectedTopicId: String?, val selectedTopicId: String?,
val topics: List<FollowableTopic>, val topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
) : InterestsUiState ) : InterestsUiState
data object Empty : InterestsUiState data object Empty : InterestsUiState

@ -38,11 +38,11 @@ import androidx.compose.ui.unit.dp
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.DraggableScrollbar
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.rememberDraggableScroller
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
@Composable @Composable
fun TopicsTabContent( fun TopicsTabContent(
topics: List<FollowableTopic>, topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
onFollowButtonClick: (String, Boolean) -> Unit, onFollowButtonClick: (String, Boolean) -> Unit,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

@ -17,8 +17,8 @@
package com.google.samples.apps.nowinandroid.interests package com.google.samples.apps.nowinandroid.interests
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetFollowableTopicsUseCase
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository 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.repository.TestUserDataRepository
@ -149,8 +149,8 @@ private const val TOPIC_URL = "URL"
private const val TOPIC_IMAGE_URL = "Image URL" private const val TOPIC_IMAGE_URL = "Image URL"
private val testInputTopics = listOf( private val testInputTopics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = TOPIC_1_NAME, name = TOPIC_1_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -160,8 +160,8 @@ private val testInputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = TOPIC_2_NAME, name = TOPIC_2_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -171,8 +171,8 @@ private val testInputTopics = listOf(
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = TOPIC_3_NAME, name = TOPIC_3_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -185,8 +185,8 @@ private val testInputTopics = listOf(
) )
private val testOutputTopics = listOf( private val testOutputTopics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = TOPIC_1_NAME, name = TOPIC_1_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -196,8 +196,8 @@ private val testOutputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = TOPIC_2_NAME, name = TOPIC_2_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -207,8 +207,8 @@ private val testOutputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = TOPIC_3_NAME, name = TOPIC_3_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,

@ -28,11 +28,11 @@ import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performScrollToIndex import androidx.compose.ui.test.performScrollToIndex
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
import com.google.samples.apps.nowinandroid.core.domain.model.UserData import com.google.samples.apps.nowinandroid.core.model.UserData
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData import com.google.samples.apps.nowinandroid.core.testing.data.followableTopicTestData
import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData import com.google.samples.apps.nowinandroid.core.testing.data.newsResourcesTestData
import org.junit.Before import org.junit.Before
@ -57,7 +57,8 @@ class SearchScreenTest {
private lateinit var tryAnotherSearchString: String private lateinit var tryAnotherSearchString: String
private lateinit var searchNotReadyString: String private lateinit var searchNotReadyString: String
private val userData: UserData = UserData( private val userData: com.google.samples.apps.nowinandroid.core.model.UserData =
com.google.samples.apps.nowinandroid.core.model.UserData(
bookmarkedNewsResources = setOf("1", "3"), bookmarkedNewsResources = setOf("1", "3"),
viewedNewsResources = setOf("1", "2", "4"), viewedNewsResources = setOf("1", "2", "4"),
followedTopics = emptySet(), followedTopics = emptySet(),
@ -169,7 +170,7 @@ class SearchScreenTest {
SearchScreen( SearchScreen(
searchResultUiState = SearchResultUiState.Success( searchResultUiState = SearchResultUiState.Success(
newsResources = newsResourcesTestData.map { newsResources = newsResourcesTestData.map {
UserNewsResource( com.google.samples.apps.nowinandroid.core.model.UserNewsResource(
newsResource = it, newsResource = it,
userData = userData, userData = userData,
) )

@ -16,12 +16,12 @@
package com.google.samples.apps.nowinandroid.feature.search package com.google.samples.apps.nowinandroid.feature.search
import com.google.samples.apps.nowinandroid.core.domain.model.RecentSearchQuery import com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery
sealed interface RecentSearchQueriesUiState { sealed interface RecentSearchQueriesUiState {
data object Loading : RecentSearchQueriesUiState data object Loading : RecentSearchQueriesUiState
data class Success( data class Success(
val recentQueries: List<RecentSearchQuery> = emptyList(), val recentQueries: List<com.google.samples.apps.nowinandroid.core.model.RecentSearchQuery> = emptyList(),
) : RecentSearchQueriesUiState ) : RecentSearchQueriesUiState
} }

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.feature.search package com.google.samples.apps.nowinandroid.feature.search
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
sealed interface SearchResultUiState { sealed interface SearchResultUiState {
data object Loading : SearchResultUiState data object Loading : SearchResultUiState
@ -32,8 +32,8 @@ sealed interface SearchResultUiState {
data object LoadFailed : SearchResultUiState data object LoadFailed : SearchResultUiState
data class Success( data class Success(
val topics: List<FollowableTopic> = emptyList(), val topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic> = emptyList(),
val newsResources: List<UserNewsResource> = emptyList(), val newsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource> = emptyList(),
) : SearchResultUiState { ) : SearchResultUiState {
fun isEmpty(): Boolean = topics.isEmpty() && newsResources.isEmpty() fun isEmpty(): Boolean = topics.isEmpty() && newsResources.isEmpty()
} }

@ -85,8 +85,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success import com.google.samples.apps.nowinandroid.core.ui.NewsFeedUiState.Success
import com.google.samples.apps.nowinandroid.core.ui.R.string import com.google.samples.apps.nowinandroid.core.ui.R.string
@ -287,8 +287,8 @@ private fun SearchNotReadyBody() {
@Composable @Composable
private fun SearchResultBody( private fun SearchResultBody(
searchQuery: String, searchQuery: String,
topics: List<FollowableTopic>, topics: List<com.google.samples.apps.nowinandroid.core.model.FollowableTopic>,
newsResources: List<UserNewsResource>, newsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
onSearchTriggered: (String) -> Unit, onSearchTriggered: (String) -> Unit,
onTopicClick: (String) -> Unit, onTopicClick: (String) -> Unit,
onNewsResourcesCheckedChanged: (String, Boolean) -> Unit, onNewsResourcesCheckedChanged: (String, Boolean) -> Unit,

@ -19,7 +19,7 @@
package com.google.samples.apps.nowinandroid.feature.search package com.google.samples.apps.nowinandroid.feature.search
import androidx.compose.ui.tooling.preview.PreviewParameterProvider import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.newsResources
import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.topics import com.google.samples.apps.nowinandroid.core.ui.PreviewParameterData.topics
@ -31,7 +31,10 @@ class SearchUiStatePreviewParameterProvider : PreviewParameterProvider<SearchRes
override val values: Sequence<SearchResultUiState> = sequenceOf( override val values: Sequence<SearchResultUiState> = sequenceOf(
SearchResultUiState.Success( SearchResultUiState.Success(
topics = topics.mapIndexed { i, topic -> topics = topics.mapIndexed { i, topic ->
FollowableTopic(topic = topic, isFollowed = i % 2 == 0) com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = topic,
isFollowed = i % 2 == 0
)
}, },
newsResources = newsResources, newsResources = newsResources,
), ),

@ -22,7 +22,7 @@ import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsEvent 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.AnalyticsEvent.Param
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.domain.model.UserSearchResult import com.google.samples.apps.nowinandroid.core.model.UserSearchResult
import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository import com.google.samples.apps.nowinandroid.core.domain.repository.RecentSearchRepository
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetRecentSearchQueriesUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetRecentSearchQueriesUseCase
import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsCountUseCase import com.google.samples.apps.nowinandroid.core.domain.usecase.GetSearchContentsCountUseCase
@ -63,7 +63,7 @@ class SearchViewModel @Inject constructor(
getSearchContentsUseCase(query) getSearchContentsUseCase(query)
// Not using .asResult() here, because it emits Loading state every // Not using .asResult() here, because it emits Loading state every
// time the user types a letter in the search box, which flickers the screen. // time the user types a letter in the search box, which flickers the screen.
.map<UserSearchResult, SearchResultUiState> { data -> .map<com.google.samples.apps.nowinandroid.core.model.UserSearchResult, SearchResultUiState> { data ->
SearchResultUiState.Success( SearchResultUiState.Success(
topics = data.topics, topics = data.topics,
newsResources = data.newsResources, newsResources = data.newsResources,

@ -20,9 +20,9 @@ import androidx.activity.ComponentActivity
import androidx.compose.ui.test.assertIsSelected import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success
import org.junit.Rule import org.junit.Rule

@ -59,13 +59,13 @@ import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTextButton import com.google.samples.apps.nowinandroid.core.designsystem.component.NiaTextButton
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.designsystem.theme.supportsDynamicTheming import com.google.samples.apps.nowinandroid.core.designsystem.theme.supportsDynamicTheming
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.FOLLOW_SYSTEM import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.FOLLOW_SYSTEM
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.LIGHT import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.LIGHT
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.DEFAULT import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.DEFAULT
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.feature.settings.R.string import com.google.samples.apps.nowinandroid.feature.settings.R.string
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading
@ -91,9 +91,9 @@ fun SettingsDialog(
settingsUiState: SettingsUiState, settingsUiState: SettingsUiState,
supportDynamicColor: Boolean = supportsDynamicTheming(), supportDynamicColor: Boolean = supportsDynamicTheming(),
onDismiss: () -> Unit, onDismiss: () -> Unit,
onChangeThemeBrand: (themeBrand: ThemeBrand) -> Unit, onChangeThemeBrand: (themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) -> Unit,
onChangeDynamicColorPreference: (useDynamicColor: Boolean) -> Unit, onChangeDynamicColorPreference: (useDynamicColor: Boolean) -> Unit,
onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit, onChangeDarkThemeConfig: (darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) -> Unit,
) { ) {
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
@ -158,9 +158,9 @@ fun SettingsDialog(
private fun ColumnScope.SettingsPanel( private fun ColumnScope.SettingsPanel(
settings: UserEditableSettings, settings: UserEditableSettings,
supportDynamicColor: Boolean, supportDynamicColor: Boolean,
onChangeThemeBrand: (themeBrand: ThemeBrand) -> Unit, onChangeThemeBrand: (themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) -> Unit,
onChangeDynamicColorPreference: (useDynamicColor: Boolean) -> Unit, onChangeDynamicColorPreference: (useDynamicColor: Boolean) -> Unit,
onChangeDarkThemeConfig: (darkThemeConfig: DarkThemeConfig) -> Unit, onChangeDarkThemeConfig: (darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) -> Unit,
) { ) {
SettingsDialogSectionTitle(text = stringResource(string.feature_settings_theme)) SettingsDialogSectionTitle(text = stringResource(string.feature_settings_theme))
Column(Modifier.selectableGroup()) { Column(Modifier.selectableGroup()) {

@ -18,8 +18,8 @@ package com.google.samples.apps.nowinandroid.feature.settings
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand import com.google.samples.apps.nowinandroid.core.model.ThemeBrand
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Success
@ -53,13 +53,13 @@ class SettingsViewModel @Inject constructor(
initialValue = Loading, initialValue = Loading,
) )
fun updateThemeBrand(themeBrand: ThemeBrand) { fun updateThemeBrand(themeBrand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand) {
viewModelScope.launch { viewModelScope.launch {
userDataRepository.setThemeBrand(themeBrand) userDataRepository.setThemeBrand(themeBrand)
} }
} }
fun updateDarkThemeConfig(darkThemeConfig: DarkThemeConfig) { fun updateDarkThemeConfig(darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig) {
viewModelScope.launch { viewModelScope.launch {
userDataRepository.setDarkThemeConfig(darkThemeConfig) userDataRepository.setDarkThemeConfig(darkThemeConfig)
} }
@ -76,9 +76,9 @@ class SettingsViewModel @Inject constructor(
* Represents the settings which the user can edit within the app. * Represents the settings which the user can edit within the app.
*/ */
data class UserEditableSettings( data class UserEditableSettings(
val brand: ThemeBrand, val brand: com.google.samples.apps.nowinandroid.core.model.ThemeBrand,
val useDynamicColor: Boolean, val useDynamicColor: Boolean,
val darkThemeConfig: DarkThemeConfig, val darkThemeConfig: com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig,
) )
sealed interface SettingsUiState { sealed interface SettingsUiState {

@ -16,8 +16,8 @@
package com.google.samples.apps.nowinandroid.feature.settings package com.google.samples.apps.nowinandroid.feature.settings
import com.google.samples.apps.nowinandroid.core.domain.model.DarkThemeConfig.DARK import com.google.samples.apps.nowinandroid.core.model.DarkThemeConfig.DARK
import com.google.samples.apps.nowinandroid.core.domain.model.ThemeBrand.ANDROID import com.google.samples.apps.nowinandroid.core.model.ThemeBrand.ANDROID
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository 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.core.testing.util.MainDispatcherRule
import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading import com.google.samples.apps.nowinandroid.feature.settings.SettingsUiState.Loading

@ -61,8 +61,8 @@ import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollba
import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState import com.google.samples.apps.nowinandroid.core.designsystem.component.scrollbar.scrollbarState
import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons import com.google.samples.apps.nowinandroid.core.designsystem.icon.NiaIcons
import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme import com.google.samples.apps.nowinandroid.core.designsystem.theme.NiaTheme
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews import com.google.samples.apps.nowinandroid.core.ui.DevicePreviews
import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent import com.google.samples.apps.nowinandroid.core.ui.TrackScreenViewEvent
import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank import com.google.samples.apps.nowinandroid.core.ui.TrackScrollJank
@ -274,7 +274,7 @@ private fun TopicBodyPreview() {
@Composable @Composable
private fun TopicToolbar( private fun TopicToolbar(
uiState: FollowableTopic, uiState: com.google.samples.apps.nowinandroid.core.model.FollowableTopic,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
showBackButton: Boolean = true, showBackButton: Boolean = true,
onBackClick: () -> Unit = {}, onBackClick: () -> Unit = {},
@ -319,7 +319,7 @@ private fun TopicToolbar(
@Composable @Composable
fun TopicScreenPopulated( fun TopicScreenPopulated(
@PreviewParameter(UserNewsResourcePreviewParameterProvider::class) @PreviewParameter(UserNewsResourcePreviewParameterProvider::class)
userNewsResources: List<UserNewsResource>, userNewsResources: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>,
) { ) {
NiaTheme { NiaTheme {
NiaBackground { NiaBackground {

@ -19,9 +19,9 @@ package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource import com.google.samples.apps.nowinandroid.core.model.UserNewsResource
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.domain.repository.NewsResourceQuery
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository import com.google.samples.apps.nowinandroid.core.domain.repository.UserDataRepository
@ -103,7 +103,7 @@ private fun topicUiState(
.map { it.followedTopics } .map { it.followedTopics }
// Observe topic information // Observe topic information
val topicStream: Flow<Topic> = topicsRepository.getTopic( val topicStream: Flow<com.google.samples.apps.nowinandroid.core.model.Topic> = topicsRepository.getTopic(
id = topicId, id = topicId,
) )
@ -118,7 +118,7 @@ private fun topicUiState(
is Result.Success -> { is Result.Success -> {
val (followedTopics, topic) = followedTopicToTopicResult.data val (followedTopics, topic) = followedTopicToTopicResult.data
TopicUiState.Success( TopicUiState.Success(
followableTopic = FollowableTopic( followableTopic = com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
topic = topic, topic = topic,
isFollowed = topicId in followedTopics, isFollowed = topicId in followedTopics,
), ),
@ -137,7 +137,7 @@ private fun newsUiState(
userDataRepository: UserDataRepository, userDataRepository: UserDataRepository,
): Flow<NewsUiState> { ): Flow<NewsUiState> {
// Observe news // Observe news
val newsStream: Flow<List<UserNewsResource>> = userNewsResourceRepository.observeAll( val newsStream: Flow<List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>> = userNewsResourceRepository.observeAll(
NewsResourceQuery(filterTopicIds = setOf(element = topicId)), NewsResourceQuery(filterTopicIds = setOf(element = topicId)),
) )
@ -157,13 +157,13 @@ private fun newsUiState(
} }
sealed interface TopicUiState { sealed interface TopicUiState {
data class Success(val followableTopic: FollowableTopic) : TopicUiState data class Success(val followableTopic: com.google.samples.apps.nowinandroid.core.model.FollowableTopic) : TopicUiState
data object Error : TopicUiState data object Error : TopicUiState
data object Loading : TopicUiState data object Loading : TopicUiState
} }
sealed interface NewsUiState { sealed interface NewsUiState {
data class Success(val news: List<UserNewsResource>) : NewsUiState data class Success(val news: List<com.google.samples.apps.nowinandroid.core.model.UserNewsResource>) : NewsUiState
data object Error : NewsUiState data object Error : NewsUiState
data object Loading : NewsUiState data object Loading : NewsUiState
} }

@ -18,9 +18,9 @@ package com.google.samples.apps.nowinandroid.feature.topic
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.SavedStateHandle
import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository import com.google.samples.apps.nowinandroid.core.data.repository.CompositeUserNewsResourceRepository
import com.google.samples.apps.nowinandroid.core.domain.model.FollowableTopic import com.google.samples.apps.nowinandroid.core.model.FollowableTopic
import com.google.samples.apps.nowinandroid.core.domain.model.NewsResource import com.google.samples.apps.nowinandroid.core.model.NewsResource
import com.google.samples.apps.nowinandroid.core.domain.model.Topic import com.google.samples.apps.nowinandroid.core.model.Topic
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
import com.google.samples.apps.nowinandroid.core.testing.repository.TestTopicsRepository 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.repository.TestUserDataRepository
@ -75,7 +75,7 @@ class TopicViewModelTest {
fun uiStateTopic_whenSuccess_matchesTopicFromRepository() = runTest { fun uiStateTopic_whenSuccess_matchesTopicFromRepository() = runTest {
val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.topicUiState.collect() } val collectJob = launch(UnconfinedTestDispatcher()) { viewModel.topicUiState.collect() }
topicsRepository.sendTopics(testInputTopics.map(FollowableTopic::topic)) topicsRepository.sendTopics(testInputTopics.map(com.google.samples.apps.nowinandroid.core.model.FollowableTopic::topic))
userDataRepository.setFollowedTopicIds(setOf(testInputTopics[1].topic.id)) userDataRepository.setFollowedTopicIds(setOf(testInputTopics[1].topic.id))
val item = viewModel.topicUiState.value val item = viewModel.topicUiState.value
assertIs<TopicUiState.Success>(item) assertIs<TopicUiState.Success>(item)
@ -175,8 +175,8 @@ private const val TOPIC_URL = "URL"
private const val TOPIC_IMAGE_URL = "Image URL" private const val TOPIC_IMAGE_URL = "Image URL"
private val testInputTopics = listOf( private val testInputTopics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = TOPIC_1_NAME, name = TOPIC_1_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -186,8 +186,8 @@ private val testInputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = TOPIC_2_NAME, name = TOPIC_2_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -197,8 +197,8 @@ private val testInputTopics = listOf(
), ),
isFollowed = false, isFollowed = false,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = TOPIC_3_NAME, name = TOPIC_3_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -211,8 +211,8 @@ private val testInputTopics = listOf(
) )
private val testOutputTopics = listOf( private val testOutputTopics = listOf(
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = TOPIC_1_NAME, name = TOPIC_1_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -222,8 +222,8 @@ private val testOutputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "1", id = "1",
name = TOPIC_2_NAME, name = TOPIC_2_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -233,8 +233,8 @@ private val testOutputTopics = listOf(
), ),
isFollowed = true, isFollowed = true,
), ),
FollowableTopic( com.google.samples.apps.nowinandroid.core.model.FollowableTopic(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "2", id = "2",
name = TOPIC_3_NAME, name = TOPIC_3_NAME,
shortDescription = TOPIC_SHORT_DESC, shortDescription = TOPIC_SHORT_DESC,
@ -247,7 +247,7 @@ private val testOutputTopics = listOf(
) )
private val sampleNewsResources = listOf( private val sampleNewsResources = listOf(
NewsResource( com.google.samples.apps.nowinandroid.core.model.NewsResource(
id = "1", id = "1",
title = "Thanks for helping us reach 1M YouTube Subscribers", title = "Thanks for helping us reach 1M YouTube Subscribers",
content = "Thank you everyone for following the Now in Android series and everything the " + content = "Thank you everyone for following the Now in Android series and everything the " +
@ -259,7 +259,7 @@ private val sampleNewsResources = listOf(
publishDate = Instant.parse("2021-11-09T00:00:00.000Z"), publishDate = Instant.parse("2021-11-09T00:00:00.000Z"),
type = "Video 📺", type = "Video 📺",
topics = listOf( topics = listOf(
Topic( com.google.samples.apps.nowinandroid.core.model.Topic(
id = "0", id = "0",
name = "Headlines", name = "Headlines",
shortDescription = "", shortDescription = "",

@ -26,7 +26,7 @@ import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper import com.google.samples.apps.nowinandroid.core.analytics.AnalyticsHelper
import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource import com.google.samples.apps.nowinandroid.core.datastore.NiaPreferencesDataSource
import com.google.samples.apps.nowinandroid.core.domain.model.ChangeListVersions import com.google.samples.apps.nowinandroid.core.model.ChangeListVersions
import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.NewsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.SearchContentsRepository
import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository import com.google.samples.apps.nowinandroid.core.domain.repository.TopicsRepository
@ -86,11 +86,11 @@ internal class SyncWorker @AssistedInject constructor(
} }
} }
override suspend fun getChangeListVersions(): ChangeListVersions = override suspend fun getChangeListVersions(): com.google.samples.apps.nowinandroid.core.model.ChangeListVersions =
niaPreferences.getChangeListVersions() niaPreferences.getChangeListVersions()
override suspend fun updateChangeListVersions( override suspend fun updateChangeListVersions(
update: ChangeListVersions.() -> ChangeListVersions, update: com.google.samples.apps.nowinandroid.core.model.ChangeListVersions.() -> com.google.samples.apps.nowinandroid.core.model.ChangeListVersions,
) = niaPreferences.updateChangeListVersion(update) ) = niaPreferences.updateChangeListVersion(update)
companion object { companion object {

Loading…
Cancel
Save