Skip to content

Commit de5ceb4

Browse files
Merge pull request #508 from android/mm/chipcolor
Update topic chips to show a different color based on whether the user is following that topic
2 parents a601f01 + bad3ea6 commit de5ceb4

File tree

24 files changed

+507
-340
lines changed

24 files changed

+507
-340
lines changed

core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetSaveableNewsResourcesUseCase.kt renamed to core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/GetUserNewsResourcesUseCase.kt

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,43 @@ package com.google.samples.apps.nowinandroid.core.domain
1818

1919
import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository
2020
import com.google.samples.apps.nowinandroid.core.data.repository.UserDataRepository
21-
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
21+
import com.google.samples.apps.nowinandroid.core.domain.model.UserNewsResource
22+
import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources
2223
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
24+
import com.google.samples.apps.nowinandroid.core.model.data.UserData
2325
import javax.inject.Inject
2426
import kotlinx.coroutines.flow.Flow
2527
import kotlinx.coroutines.flow.combine
2628
import kotlinx.coroutines.flow.filterNot
27-
import kotlinx.coroutines.flow.map
2829

2930
/**
3031
* A use case responsible for obtaining news resources with their associated bookmarked (also known
3132
* as "saved") state.
3233
*/
33-
class GetSaveableNewsResourcesUseCase @Inject constructor(
34+
class GetUserNewsResourcesUseCase @Inject constructor(
3435
private val newsRepository: NewsRepository,
35-
userDataRepository: UserDataRepository
36+
private val userDataRepository: UserDataRepository
3637
) {
37-
38-
private val bookmarkedNewsResources = userDataRepository.userData.map {
39-
it.bookmarkedNewsResources
40-
}
41-
4238
/**
43-
* Returns a list of SaveableNewsResources which match the supplied set of topic ids.
39+
* Returns a list of UserNewsResources which match the supplied set of topic ids.
4440
*
4541
* @param filterTopicIds - A set of topic ids used to filter the list of news resources. If
4642
* this is empty the list of news resources will not be filtered.
4743
*/
4844
operator fun invoke(
4945
filterTopicIds: Set<String> = emptySet()
50-
): Flow<List<SaveableNewsResource>> =
46+
): Flow<List<UserNewsResource>> =
5147
if (filterTopicIds.isEmpty()) {
5248
newsRepository.getNewsResources()
5349
} else {
5450
newsRepository.getNewsResources(filterTopicIds = filterTopicIds)
55-
}.mapToSaveableNewsResources(bookmarkedNewsResources)
51+
}.mapToUserNewsResources(userDataRepository.userData)
5652
}
5753

58-
private fun Flow<List<NewsResource>>.mapToSaveableNewsResources(
59-
savedNewsResourceIdsStream: Flow<Set<String>>
60-
): Flow<List<SaveableNewsResource>> =
54+
private fun Flow<List<NewsResource>>.mapToUserNewsResources(
55+
userDataStream: Flow<UserData>
56+
): Flow<List<UserNewsResource>> =
6157
filterNot { it.isEmpty() }
62-
.combine(savedNewsResourceIdsStream) { newsResources, savedNewsResourceIds ->
63-
newsResources.map { newsResource ->
64-
SaveableNewsResource(
65-
newsResource = newsResource,
66-
isSaved = savedNewsResourceIds.contains(newsResource.id)
67-
)
68-
}
58+
.combine(userDataStream) { newsResources, userData ->
59+
newsResources.mapToUserNewsResources(userData)
6960
}

core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/FollowableTopic.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,27 @@
1717
package com.google.samples.apps.nowinandroid.core.domain.model
1818

1919
import com.google.samples.apps.nowinandroid.core.model.data.Topic
20+
import com.google.samples.apps.nowinandroid.core.model.data.previewTopics
2021

2122
/**
2223
* A [topic] with the additional information for whether or not it is followed.
2324
*/
24-
data class FollowableTopic(
25+
data class FollowableTopic( // TODO consider changing to UserTopic and flattening
2526
val topic: Topic,
2627
val isFollowed: Boolean
2728
)
29+
30+
val previewFollowableTopics = listOf(
31+
FollowableTopic(
32+
previewTopics[0],
33+
isFollowed = false
34+
),
35+
FollowableTopic(
36+
previewTopics[1],
37+
isFollowed = true
38+
),
39+
FollowableTopic(
40+
previewTopics[2],
41+
isFollowed = false
42+
)
43+
)

core/domain/src/main/java/com/google/samples/apps/nowinandroid/core/domain/model/SaveableNewsResource.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright 2022 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.samples.apps.nowinandroid.core.domain.model
18+
19+
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
20+
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType
21+
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Codelab
22+
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Unknown
23+
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
24+
import com.google.samples.apps.nowinandroid.core.model.data.UserData
25+
import kotlinx.datetime.Instant
26+
import kotlinx.datetime.LocalDateTime
27+
import kotlinx.datetime.TimeZone
28+
import kotlinx.datetime.toInstant
29+
30+
/* ktlint-disable max-line-length */
31+
32+
/**
33+
* A [NewsResource] with additional user information such as whether the user is following the
34+
* news resource's topics and whether they have saved (bookmarked) this news resource.
35+
*/
36+
data class UserNewsResource internal constructor(
37+
val id: String,
38+
val title: String,
39+
val content: String,
40+
val url: String,
41+
val headerImageUrl: String?,
42+
val publishDate: Instant,
43+
val type: NewsResourceType,
44+
val followableTopics: List<FollowableTopic>,
45+
val isSaved: Boolean
46+
) {
47+
constructor(newsResource: NewsResource, userData: UserData) : this(
48+
id = newsResource.id,
49+
title = newsResource.title,
50+
content = newsResource.content,
51+
url = newsResource.url,
52+
headerImageUrl = newsResource.headerImageUrl,
53+
publishDate = newsResource.publishDate,
54+
type = newsResource.type,
55+
followableTopics = newsResource.topics.map { topic ->
56+
FollowableTopic(
57+
topic = topic,
58+
isFollowed = userData.followedTopics.contains(topic.id)
59+
)
60+
},
61+
isSaved = userData.bookmarkedNewsResources.contains(newsResource.id)
62+
)
63+
}
64+
65+
fun List<NewsResource>.mapToUserNewsResources(userData: UserData): List<UserNewsResource> {
66+
return map { UserNewsResource(it, userData) }
67+
}
68+
69+
val previewUserNewsResources = listOf(
70+
UserNewsResource(
71+
id = "1",
72+
title = "Android Basics with Compose",
73+
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. You’ll learn the fundamentals of programming in Kotlin while building Android apps using Jetpack Compose, Android’s 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",
74+
url = "https://android-developers.googleblog.com/2022/05/new-android-basics-with-compose-course.html",
75+
headerImageUrl = "https://developer.android.com/images/hero-assets/android-basics-compose.svg",
76+
publishDate = LocalDateTime(
77+
year = 2022,
78+
monthNumber = 5,
79+
dayOfMonth = 4,
80+
hour = 23,
81+
minute = 0,
82+
second = 0,
83+
nanosecond = 0
84+
).toInstant(TimeZone.UTC),
85+
type = Codelab,
86+
followableTopics = listOf(previewFollowableTopics[1]),
87+
isSaved = true
88+
),
89+
UserNewsResource(
90+
id = "2",
91+
title = "Thanks for helping us reach 1M YouTube Subscribers",
92+
content = "Thank you everyone for following the Now in Android series and everything the " +
93+
"Android Developers YouTube channel has to offer. During the Android Developer " +
94+
"Summit, our YouTube channel reached 1 million subscribers! Here’s a small video to " +
95+
"thank you all.",
96+
url = "https://youtu.be/-fJ6poHQrjM",
97+
headerImageUrl = "https://i.ytimg.com/vi/-fJ6poHQrjM/maxresdefault.jpg",
98+
publishDate = Instant.parse("2021-11-09T00:00:00.000Z"),
99+
type = Video,
100+
followableTopics = listOf(previewFollowableTopics[0], previewFollowableTopics[1]),
101+
isSaved = false
102+
),
103+
UserNewsResource(
104+
id = "3",
105+
title = "Transformations and customisations in the Paging Library",
106+
content = "A demonstration of different operations that can be performed " +
107+
"with Paging. Transformations like inserting separators, when to " +
108+
"create a new pager, and customisation options for consuming " +
109+
"PagingData.",
110+
url = "https://youtu.be/ZARz0pjm5YM",
111+
headerImageUrl = "https://i.ytimg.com/vi/ZARz0pjm5YM/maxresdefault.jpg",
112+
publishDate = Instant.parse("2021-11-01T00:00:00.000Z"),
113+
type = Video,
114+
followableTopics = listOf(previewFollowableTopics[2]),
115+
isSaved = false
116+
),
117+
UserNewsResource(
118+
id = "4",
119+
title = "New Jetpack Release",
120+
content = "New Jetpack release includes updates to libraries such as CameraX, Benchmark, and" +
121+
"more!",
122+
url = "https://developer.android.com/jetpack/androidx/versions/all-channel",
123+
headerImageUrl = "",
124+
publishDate = Instant.parse("2022-10-01T00:00:00.000Z"),
125+
type = Unknown,
126+
followableTopics = listOf(previewFollowableTopics[2]),
127+
isSaved = true
128+
)
129+
)

core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetSaveableNewsResourcesUseCaseTest.kt renamed to core/domain/src/test/java/com/google/samples/apps/nowinandroid/core/domain/GetUserNewsResourcesUseCaseTest.kt

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616

1717
package com.google.samples.apps.nowinandroid.core.domain
1818

19-
import com.google.samples.apps.nowinandroid.core.domain.model.SaveableNewsResource
19+
import com.google.samples.apps.nowinandroid.core.domain.model.mapToUserNewsResources
2020
import com.google.samples.apps.nowinandroid.core.model.data.NewsResource
2121
import com.google.samples.apps.nowinandroid.core.model.data.NewsResourceType.Video
2222
import com.google.samples.apps.nowinandroid.core.model.data.Topic
2323
import com.google.samples.apps.nowinandroid.core.testing.repository.TestNewsRepository
2424
import com.google.samples.apps.nowinandroid.core.testing.repository.TestUserDataRepository
25+
import com.google.samples.apps.nowinandroid.core.testing.repository.emptyUserData
2526
import com.google.samples.apps.nowinandroid.core.testing.util.MainDispatcherRule
2627
import kotlin.test.assertEquals
2728
import kotlinx.coroutines.flow.first
@@ -30,55 +31,56 @@ import kotlinx.datetime.Instant
3031
import org.junit.Rule
3132
import org.junit.Test
3233

33-
class GetSaveableNewsResourcesUseCaseTest {
34+
class GetUserNewsResourcesUseCaseTest {
3435

3536
@get:Rule
3637
val mainDispatcherRule = MainDispatcherRule()
3738

3839
private val newsRepository = TestNewsRepository()
3940
private val userDataRepository = TestUserDataRepository()
4041

41-
val useCase = GetSaveableNewsResourcesUseCase(newsRepository, userDataRepository)
42+
val useCase = GetUserNewsResourcesUseCase(newsRepository, userDataRepository)
4243

4344
@Test
4445
fun whenNoFilters_allNewsResourcesAreReturned() = runTest {
4546

46-
// Obtain the saveable news resources stream.
47-
val saveableNewsResources = useCase()
47+
// Obtain the user news resources stream.
48+
val userNewsResources = useCase()
4849

49-
// Send some news resources and bookmarks.
50+
// Send some news resources and user data into the data repositories.
5051
newsRepository.sendNewsResources(sampleNewsResources)
51-
userDataRepository.setNewsResourceBookmarks(
52-
setOf(sampleNewsResources[0].id, sampleNewsResources[2].id)
52+
53+
// Construct the test user data with bookmarks and followed topics.
54+
val userData = emptyUserData.copy(
55+
bookmarkedNewsResources = setOf(sampleNewsResources[0].id, sampleNewsResources[2].id),
56+
followedTopics = setOf(sampleTopic1.id)
5357
)
5458

59+
userDataRepository.setUserData(userData)
60+
5561
// Check that the correct news resources are returned with their bookmarked state.
5662
assertEquals(
57-
listOf(
58-
SaveableNewsResource(sampleNewsResources[0], true),
59-
SaveableNewsResource(sampleNewsResources[1], false),
60-
SaveableNewsResource(sampleNewsResources[2], true)
61-
),
62-
saveableNewsResources.first()
63+
sampleNewsResources.mapToUserNewsResources(userData),
64+
userNewsResources.first()
6365
)
6466
}
6567

6668
@Test
6769
fun whenFilteredByTopicId_matchingNewsResourcesAreReturned() = runTest {
6870

69-
// Obtain a stream of saveable news resources for the given topic id.
70-
val saveableNewsResources = useCase(filterTopicIds = setOf(sampleTopic1.id))
71+
// Obtain a stream of user news resources for the given topic id.
72+
val userNewsResources = useCase(filterTopicIds = setOf(sampleTopic1.id))
7173

72-
// Send some news resources and bookmarks.
74+
// Send test data into the repositories.
7375
newsRepository.sendNewsResources(sampleNewsResources)
74-
userDataRepository.setNewsResourceBookmarks(setOf())
76+
userDataRepository.setUserData(emptyUserData)
7577

7678
// Check that only news resources with the given topic id are returned.
7779
assertEquals(
7880
sampleNewsResources
7981
.filter { it.topics.contains(sampleTopic1) }
80-
.map { SaveableNewsResource(it, false) },
81-
saveableNewsResources.first()
82+
.mapToUserNewsResources(emptyUserData),
83+
userNewsResources.first()
8284
)
8385
}
8486
}

0 commit comments

Comments
 (0)