Skip to content

Commit f392908

Browse files
committedAug 24, 2020
Merges with 'github/develop' and applies spotless
Change-Id: I734022b261df56d546749c2c87051c97096a6970
2 parents 8a63e3d + 19b5e1c commit f392908

19 files changed

+935
-561
lines changed
 

‎JetNews/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ dependencies {
7474
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
7575

7676
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-alpha06"
77+
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-alpha06"
7778

7879
androidTestImplementation 'junit:junit:4.13'
7980
androidTestImplementation 'androidx.test:rules:1.2.0'

‎JetNews/app/src/androidTest/java/com/example/jetnews/TestHelper.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,16 @@ import androidx.compose.runtime.remember
2121
import androidx.lifecycle.SavedStateHandle
2222
import androidx.ui.test.ComposeTestRule
2323
import com.example.jetnews.ui.JetnewsApp
24-
import com.example.jetnews.ui.JetnewsStatus
2524
import com.example.jetnews.ui.NavigationViewModel
2625

2726
/**
2827
* Launches the app from a test context
2928
*/
3029
fun ComposeTestRule.launchJetNewsApp(context: Context) {
3130
setContent {
32-
JetnewsStatus.resetState()
3331
JetnewsApp(
3432
TestAppContainer(context),
3533
remember { NavigationViewModel(SavedStateHandle()) }
3634
)
3735
}
3836
}
39-
40-
/**
41-
* Resets the state of the app. Needs to be executed in Compose code (within a frame)
42-
*/
43-
fun JetnewsStatus.resetState() {
44-
favorites.clear()
45-
selectedTopics.clear()
46-
}

‎JetNews/app/src/main/java/com/example/jetnews/data/AppContainerImpl.kt

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@
1717
package com.example.jetnews.data
1818

1919
import android.content.Context
20-
import android.os.Handler
21-
import android.os.Looper
2220
import com.example.jetnews.data.interests.InterestsRepository
2321
import com.example.jetnews.data.interests.impl.FakeInterestsRepository
2422
import com.example.jetnews.data.posts.PostsRepository
2523
import com.example.jetnews.data.posts.impl.FakePostsRepository
26-
import java.util.concurrent.ExecutorService
27-
import java.util.concurrent.Executors
2824

2925
/**
3026
* Dependency Injection container at the application level.
@@ -41,18 +37,8 @@ interface AppContainer {
4137
*/
4238
class AppContainerImpl(private val applicationContext: Context) : AppContainer {
4339

44-
private val executorService: ExecutorService by lazy {
45-
Executors.newFixedThreadPool(4)
46-
}
47-
48-
private val mainThreadHandler: Handler by lazy {
49-
Handler(Looper.getMainLooper())
50-
}
51-
5240
override val postsRepository: PostsRepository by lazy {
5341
FakePostsRepository(
54-
executorService = executorService,
55-
resultThreadHandler = mainThreadHandler,
5642
resources = applicationContext.resources
5743
)
5844
}

‎JetNews/app/src/main/java/com/example/jetnews/data/interests/InterestsRepository.kt

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
package com.example.jetnews.data.interests
1818

1919
import com.example.jetnews.data.Result
20+
import kotlinx.coroutines.flow.Flow
21+
22+
typealias TopicsMap = Map<String, List<String>>
2023

2124
/**
2225
* Interface to the Interests data layer.
@@ -26,15 +29,47 @@ interface InterestsRepository {
2629
/**
2730
* Get relevant topics to the user.
2831
*/
29-
fun getTopics(callback: (Result<Map<String, List<String>>>) -> Unit)
32+
suspend fun getTopics(): Result<TopicsMap>
3033

3134
/**
3235
* Get list of people.
3336
*/
34-
fun getPeople(callback: (Result<List<String>>) -> Unit)
37+
suspend fun getPeople(): Result<List<String>>
3538

3639
/**
3740
* Get list of publications.
3841
*/
39-
fun getPublications(callback: (Result<List<String>>) -> Unit)
42+
suspend fun getPublications(): Result<List<String>>
43+
44+
/**
45+
* Toggle between selected and unselected
46+
*/
47+
suspend fun toggleTopicSelection(topic: TopicSelection)
48+
49+
/**
50+
* Toggle between selected and unselected
51+
*/
52+
suspend fun togglePersonSelected(person: String)
53+
54+
/**
55+
* Toggle between selected and unselected
56+
*/
57+
suspend fun togglePublicationSelected(publication: String)
58+
59+
/**
60+
* Currently selected topics
61+
*/
62+
fun observeTopicsSelected(): Flow<Set<TopicSelection>>
63+
64+
/**
65+
* Currently selected people
66+
*/
67+
fun observePeopleSelected(): Flow<Set<String>>
68+
69+
/**
70+
* Currently selected publications
71+
*/
72+
fun observePublicationSelected(): Flow<Set<String>>
4073
}
74+
75+
data class TopicSelection(val section: String, val topic: String)

‎JetNews/app/src/main/java/com/example/jetnews/data/interests/impl/FakeInterestsRepository.kt

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,20 @@ package com.example.jetnews.data.interests.impl
1818

1919
import com.example.jetnews.data.Result
2020
import com.example.jetnews.data.interests.InterestsRepository
21+
import com.example.jetnews.data.interests.TopicSelection
22+
import com.example.jetnews.data.interests.TopicsMap
23+
import com.example.jetnews.utils.addOrRemove
24+
import kotlinx.coroutines.ExperimentalCoroutinesApi
25+
import kotlinx.coroutines.flow.Flow
26+
import kotlinx.coroutines.flow.MutableStateFlow
27+
import kotlinx.coroutines.sync.Mutex
28+
import kotlinx.coroutines.sync.withLock
2129

2230
/**
2331
* Implementation of InterestRepository that returns a hardcoded list of
2432
* topics, people and publications synchronously.
2533
*/
34+
@OptIn(ExperimentalCoroutinesApi::class)
2635
class FakeInterestsRepository : InterestsRepository {
2736

2837
private val topics by lazy {
@@ -61,15 +70,53 @@ class FakeInterestsRepository : InterestsRepository {
6170
)
6271
}
6372

64-
override fun getTopics(callback: (Result<Map<String, List<String>>>) -> Unit) {
65-
callback(Result.Success(topics))
73+
// for now, keep the selections in memory
74+
private val selectedTopics = MutableStateFlow(setOf<TopicSelection>())
75+
private val selectedPeople = MutableStateFlow(setOf<String>())
76+
private val selectedPublications = MutableStateFlow(setOf<String>())
77+
78+
// Used to make suspend functions that read and update state safe to call from any thread
79+
private val mutex = Mutex()
80+
81+
override suspend fun getTopics(): Result<TopicsMap> {
82+
return Result.Success(topics)
83+
}
84+
85+
override suspend fun getPeople(): Result<List<String>> {
86+
return Result.Success(people)
87+
}
88+
89+
override suspend fun getPublications(): Result<List<String>> {
90+
return Result.Success(publications)
6691
}
6792

68-
override fun getPeople(callback: (Result<List<String>>) -> Unit) {
69-
callback(Result.Success(people))
93+
override suspend fun toggleTopicSelection(topic: TopicSelection) {
94+
mutex.withLock {
95+
val set = selectedTopics.value.toMutableSet()
96+
set.addOrRemove(topic)
97+
selectedTopics.value = set
98+
}
7099
}
71100

72-
override fun getPublications(callback: (Result<List<String>>) -> Unit) {
73-
callback(Result.Success(publications))
101+
override suspend fun togglePersonSelected(person: String) {
102+
mutex.withLock {
103+
val set = selectedPeople.value.toMutableSet()
104+
set.addOrRemove(person)
105+
selectedPeople.value = set
106+
}
74107
}
108+
109+
override suspend fun togglePublicationSelected(publication: String) {
110+
mutex.withLock {
111+
val set = selectedPublications.value.toMutableSet()
112+
set.addOrRemove(publication)
113+
selectedPublications.value = set
114+
}
115+
}
116+
117+
override fun observeTopicsSelected(): Flow<Set<TopicSelection>> = selectedTopics
118+
119+
override fun observePeopleSelected(): Flow<Set<String>> = selectedPeople
120+
121+
override fun observePublicationSelected(): Flow<Set<String>> = selectedPublications
75122
}

‎JetNews/app/src/main/java/com/example/jetnews/data/posts/PostsRepository.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package com.example.jetnews.data.posts
1818

1919
import com.example.jetnews.data.Result
2020
import com.example.jetnews.model.Post
21+
import kotlinx.coroutines.flow.Flow
2122

2223
/**
2324
* Interface to the Posts data layer.
@@ -27,10 +28,20 @@ interface PostsRepository {
2728
/**
2829
* Get a specific JetNews post.
2930
*/
30-
fun getPost(postId: String, callback: (Result<Post?>) -> Unit)
31+
suspend fun getPost(postId: String): Result<Post>
3132

3233
/**
3334
* Get JetNews posts.
3435
*/
35-
fun getPosts(callback: (Result<List<Post>>) -> Unit)
36+
suspend fun getPosts(): Result<List<Post>>
37+
38+
/**
39+
* Observe the current favorites
40+
*/
41+
fun observeFavorites(): Flow<Set<String>>
42+
43+
/**
44+
* Toggle a postId to be a favorite or not.
45+
*/
46+
suspend fun toggleFavorite(postId: String)
3647
}

‎JetNews/app/src/main/java/com/example/jetnews/data/posts/impl/BlockingFakePostsRepository.kt

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,18 @@ import androidx.compose.ui.graphics.imageFromResource
2121
import com.example.jetnews.data.Result
2222
import com.example.jetnews.data.posts.PostsRepository
2323
import com.example.jetnews.model.Post
24+
import com.example.jetnews.utils.addOrRemove
25+
import kotlinx.coroutines.Dispatchers
26+
import kotlinx.coroutines.ExperimentalCoroutinesApi
27+
import kotlinx.coroutines.flow.Flow
28+
import kotlinx.coroutines.flow.MutableStateFlow
29+
import kotlinx.coroutines.withContext
2430

2531
/**
2632
* Implementation of PostsRepository that returns a hardcoded list of
2733
* posts with resources synchronously.
2834
*/
35+
@OptIn(ExperimentalCoroutinesApi::class)
2936
class BlockingFakePostsRepository(private val context: Context) : PostsRepository {
3037

3138
private val postsWithResources: List<Post> by lazy {
@@ -37,11 +44,29 @@ class BlockingFakePostsRepository(private val context: Context) : PostsRepositor
3744
}
3845
}
3946

40-
override fun getPost(postId: String, callback: (Result<Post?>) -> Unit) {
41-
callback(Result.Success(postsWithResources.find { it.id == postId }))
47+
// for now, keep the favorites in memory
48+
private val favorites = MutableStateFlow<Set<String>>(setOf())
49+
50+
override suspend fun getPost(postId: String): Result<Post> {
51+
return withContext(Dispatchers.IO) {
52+
val post = postsWithResources.find { it.id == postId }
53+
if (post == null) {
54+
Result.Error(IllegalArgumentException("Unable to find post"))
55+
} else {
56+
Result.Success(post)
57+
}
58+
}
4259
}
4360

44-
override fun getPosts(callback: (Result<List<Post>>) -> Unit) {
45-
callback(Result.Success(postsWithResources))
61+
override suspend fun getPosts(): Result<List<Post>> {
62+
return Result.Success(postsWithResources)
63+
}
64+
65+
override fun observeFavorites(): Flow<Set<String>> = favorites
66+
67+
override suspend fun toggleFavorite(postId: String) {
68+
val set = favorites.value.toMutableSet()
69+
set.addOrRemove(postId)
70+
favorites.value = set
4671
}
4772
}

0 commit comments

Comments
 (0)
Please sign in to comment.