Skip to content

Commit f63a1c5

Browse files
committed
Twelve: Better (Flow)Result
- Mark all subclasses as data classes - Mark sealed interface generics as `out` - ...this allows us to get rid of the unused generic for each subclass - `FlowResult.Loading` can now be a singleton Change-Id: I7ad1f97daee2b723135830f218dc0b37885599ef
1 parent 7aefbe3 commit f63a1c5

23 files changed

Lines changed: 104 additions & 99 deletions

app/src/main/java/org/lineageos/twelve/datasources/AmpacheDataSource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ class AmpacheDataSource(
682682
override suspend fun onAudioPlayed(
683683
audioUri: Uri,
684684
positionMs: Long,
685-
): MediaRequestStatus<Unit> = Result.Success<_, Error>(Unit)
685+
): MediaRequestStatus<Unit> = Result.Success(Unit)
686686

687687
override suspend fun setFavorite(
688688
audioUri: Uri,

app/src/main/java/org/lineageos/twelve/datasources/FileDataSource.kt

Lines changed: 25 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,10 @@ import org.lineageos.twelve.ext.Bundle
2323
import org.lineageos.twelve.ext.executeAsync
2424
import org.lineageos.twelve.ext.mapEachRow
2525
import org.lineageos.twelve.ext.queryFlow
26-
import org.lineageos.twelve.models.ActivityTab
27-
import org.lineageos.twelve.models.Album
28-
import org.lineageos.twelve.models.Artist
29-
import org.lineageos.twelve.models.ArtistWorks
3026
import org.lineageos.twelve.models.Audio
3127
import org.lineageos.twelve.models.DataSourceInformation
3228
import org.lineageos.twelve.models.Error
33-
import org.lineageos.twelve.models.Genre
34-
import org.lineageos.twelve.models.GenreContent
35-
import org.lineageos.twelve.models.Lyrics
36-
import org.lineageos.twelve.models.MediaItem
3729
import org.lineageos.twelve.models.MediaType
38-
import org.lineageos.twelve.models.Playlist
3930
import org.lineageos.twelve.models.ProviderIdentifier
4031
import org.lineageos.twelve.models.Result
4132
import org.lineageos.twelve.models.SortingRule
@@ -64,54 +55,54 @@ class FileDataSource(
6455

6556
override fun status(
6657
providerIdentifier: ProviderIdentifier,
67-
) = flowOf(Result.Success<_, Error>(listOf<DataSourceInformation>()))
58+
) = flowOf(Result.Success(listOf<DataSourceInformation>()))
6859

6960
override suspend fun mediaTypeOf(mediaItemUri: Uri) = getMimeType(mediaItemUri)?.let {
7061
MimeUtils.mimeTypeToMediaType(it)
7162
}
7263

7364
override fun providerOf(
7465
mediaItemUri: Uri
75-
) = flowOf(Result.Failure<ProviderIdentifier, _>(Error.NOT_FOUND))
66+
) = flowOf(Result.Failure(Error.NOT_FOUND))
7667

7768
override fun activity(
7869
providerIdentifier: ProviderIdentifier,
79-
) = flowOf(Result.Failure<List<ActivityTab>, _>(Error.NOT_IMPLEMENTED))
70+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
8071

8172
override fun albums(
8273
providerIdentifier: ProviderIdentifier,
8374
sortingRule: SortingRule,
84-
) = flowOf(Result.Failure<List<Album>, _>(Error.NOT_IMPLEMENTED))
75+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
8576

8677
override fun artists(
8778
providerIdentifier: ProviderIdentifier,
8879
sortingRule: SortingRule,
89-
) = flowOf(Result.Failure<List<Artist>, _>(Error.NOT_IMPLEMENTED))
80+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
9081

9182
override fun audios(
9283
providerIdentifier: ProviderIdentifier,
9384
sortingRule: SortingRule,
94-
) = flowOf(Result.Failure<List<Audio>, _>(Error.NOT_IMPLEMENTED))
85+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
9586

9687
override fun genres(
9788
providerIdentifier: ProviderIdentifier,
9889
sortingRule: SortingRule,
99-
) = flowOf(Result.Failure<List<Genre>, _>(Error.NOT_IMPLEMENTED))
90+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
10091

10192
override fun playlists(
10293
providerIdentifier: ProviderIdentifier,
10394
sortingRule: SortingRule,
104-
) = flowOf(Result.Failure<List<Playlist>, _>(Error.NOT_IMPLEMENTED))
95+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
10596

10697
override fun search(
10798
providerIdentifier: ProviderIdentifier,
10899
query: String,
109-
) = flowOf(Result.Failure<List<MediaItem<*>>, _>(Error.NOT_IMPLEMENTED))
100+
) = flowOf(Result.Failure(Error.NOT_IMPLEMENTED))
110101

111102
@OptIn(ExperimentalCoroutinesApi::class)
112103
override fun audio(audioUri: Uri) = when (audioUri.scheme) {
113104
SCHEME_FILE -> suspend {
114-
Result.Success<_, Error>(
105+
Result.Success(
115106
Audio.Builder(audioUri)
116107
.setPlaybackUri(audioUri)
117108
.setMimeType(audioUri.determineFileMimeType())
@@ -147,7 +138,7 @@ class FileDataSource(
147138

148139
okHttpClient.newCall(request).executeAsync().use { response ->
149140
when (response.isSuccessful) {
150-
true -> Result.Success<_, Error>(
141+
true -> Result.Success(
151142
Audio.Builder(audioUri)
152143
.setPlaybackUri(audioUri)
153144
.setMimeType(response.getContentType())
@@ -161,7 +152,7 @@ class FileDataSource(
161152
}.asFlow()
162153

163154
SCHEME_RTSP -> suspend {
164-
Result.Success<_, Error>(
155+
Result.Success(
165156
Audio.Builder(audioUri)
166157
.setPlaybackUri(audioUri)
167158
.build()
@@ -172,60 +163,60 @@ class FileDataSource(
172163
}
173164

174165
override fun album(albumUri: Uri) = flowOf(
175-
Result.Failure<Pair<Album, List<Audio>>, _>(Error.NOT_FOUND)
166+
Result.Failure(Error.NOT_FOUND)
176167
)
177168

178169
override fun artist(artistUri: Uri) = flowOf(
179-
Result.Failure<Pair<Artist, ArtistWorks>, _>(Error.NOT_FOUND)
170+
Result.Failure(Error.NOT_FOUND)
180171
)
181172

182173
override fun genre(genreUri: Uri) = flowOf(
183-
Result.Failure<Pair<Genre, GenreContent>, _>(Error.NOT_FOUND)
174+
Result.Failure(Error.NOT_FOUND)
184175
)
185176

186177
override fun playlist(playlistUri: Uri) = flowOf(
187-
Result.Failure<Pair<Playlist, List<Audio>>, _>(Error.NOT_FOUND)
178+
Result.Failure(Error.NOT_FOUND)
188179
)
189180

190181
override fun audioPlaylistsStatus(audioUri: Uri) = flowOf(
191-
Result.Failure<List<Pair<Playlist, Boolean>>, _>(Error.NOT_FOUND)
182+
Result.Failure(Error.NOT_FOUND)
192183
)
193184

194-
override fun lyrics(audioUri: Uri) = flowOf(Result.Failure<Lyrics, _>(Error.NOT_FOUND))
185+
override fun lyrics(audioUri: Uri) = flowOf(Result.Failure(Error.NOT_FOUND))
195186

196187
override suspend fun createPlaylist(
197188
providerIdentifier: ProviderIdentifier,
198189
name: String,
199-
) = Result.Failure<Uri, _>(Error.NOT_IMPLEMENTED)
190+
) = Result.Failure(Error.NOT_IMPLEMENTED)
200191

201192
override suspend fun renamePlaylist(
202193
playlistUri: Uri,
203194
name: String,
204-
) = Result.Failure<Unit, _>(Error.NOT_FOUND)
195+
) = Result.Failure(Error.NOT_FOUND)
205196

206197
override suspend fun deletePlaylist(
207198
playlistUri: Uri,
208-
) = Result.Failure<Unit, _>(Error.NOT_FOUND)
199+
) = Result.Failure(Error.NOT_FOUND)
209200

210201
override suspend fun addAudioToPlaylist(
211202
playlistUri: Uri,
212203
audioUri: Uri,
213-
) = Result.Failure<Unit, _>(Error.NOT_FOUND)
204+
) = Result.Failure(Error.NOT_FOUND)
214205

215206
override suspend fun removeAudioFromPlaylist(
216207
playlistUri: Uri,
217208
audioUri: Uri,
218-
) = Result.Failure<Unit, _>(Error.NOT_FOUND)
209+
) = Result.Failure(Error.NOT_FOUND)
219210

220211
override suspend fun onAudioPlayed(
221212
audioUri: Uri,
222213
positionMs: Long,
223-
): MediaRequestStatus<Unit> = Result.Success<_, Error>(Unit)
214+
): MediaRequestStatus<Unit> = Result.Success(Unit)
224215

225216
override suspend fun setFavorite(
226217
audioUri: Uri,
227218
isFavorite: Boolean,
228-
) = Result.Failure<Unit, _>(Error.NOT_IMPLEMENTED)
219+
) = Result.Failure(Error.NOT_IMPLEMENTED)
229220

230221
private suspend fun getMimeType(uri: Uri) = withContext(Dispatchers.IO) {
231222
when (uri.scheme) {

app/src/main/java/org/lineageos/twelve/datasources/JellyfinDataSource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ class JellyfinDataSource(
585585
) = providersManager.doWithInstanceOf(playlistUri) {
586586
when {
587587
playlistUri == favoritesUri -> Result.Failure(Error.IO)
588-
else -> Result.Failure<Unit, _>(Error.NOT_IMPLEMENTED)
588+
else -> Result.Failure(Error.NOT_IMPLEMENTED)
589589
}
590590
}
591591

app/src/main/java/org/lineageos/twelve/datasources/MediaStoreDataSource.kt

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import org.lineageos.twelve.models.Error
3636
import org.lineageos.twelve.models.Genre
3737
import org.lineageos.twelve.models.GenreContent
3838
import org.lineageos.twelve.models.LocalizedString
39-
import org.lineageos.twelve.models.Lyrics
4039
import org.lineageos.twelve.models.MediaType
4140
import org.lineageos.twelve.models.Playlist
4241
import org.lineageos.twelve.models.ProviderArgument
@@ -135,7 +134,7 @@ class MediaStoreDataSource(
135134
).mapEachRowToAlbum()
136135
}
137136
.mapLatest {
138-
Result.Success<List<Album>, Error>(it)
137+
Result.Success(it)
139138
}
140139

141140
fun Flow<Cursor?>.mapEachRowToAlbum() = mapEachRowToAlbum(volumeName)
@@ -155,7 +154,7 @@ class MediaStoreDataSource(
155154
}
156155

157156
override fun status(providerIdentifier: ProviderIdentifier) = flowOf(
158-
Result.Success<_, Error>(listOf<DataSourceInformation>())
157+
Result.Success(listOf<DataSourceInformation>())
159158
)
160159

161160
override suspend fun mediaTypeOf(
@@ -364,7 +363,7 @@ class MediaStoreDataSource(
364363
sortingRule: SortingRule,
365364
) = database.getPlaylistDao().getAll()
366365
.mapLatest { playlists ->
367-
Result.Success<_, Error>(
366+
Result.Success(
368367
buildList {
369368
add(favoritesPlaylist)
370369

@@ -753,7 +752,7 @@ class MediaStoreDataSource(
753752
val playlist = playlistWithItems.playlist.toModel()
754753

755754
audios(playlistWithItems.items).mapLatest { items ->
756-
Result.Success<_, Error>(playlist to items.filterNotNull())
755+
Result.Success(playlist to items.filterNotNull())
757756
}
758757
} ?: flowOf(Result.Failure(Error.NOT_FOUND))
759758
}
@@ -763,7 +762,7 @@ class MediaStoreDataSource(
763762
database.getFavoriteDao().containsFlow(audioUri),
764763
database.getPlaylistWithItemsDao().getPlaylistsWithItemStatus(audioUri),
765764
) { isFavorite, playlistsWithItemStatus ->
766-
Result.Success<_, Error>(
765+
Result.Success(
767766
buildList {
768767
add(favoritesPlaylist to isFavorite)
769768

@@ -775,7 +774,7 @@ class MediaStoreDataSource(
775774
}
776775

777776
override fun lyrics(audioUri: Uri) = flowOf(
778-
Result.Failure<Lyrics, _>(Error.NOT_IMPLEMENTED)
777+
Result.Failure(Error.NOT_IMPLEMENTED)
779778
)
780779

781780
override suspend fun createPlaylist(
@@ -784,20 +783,20 @@ class MediaStoreDataSource(
784783
) = database.getPlaylistDao().create(
785784
name
786785
).let {
787-
Result.Success<_, Error>(ContentUris.withAppendedId(playlistsBaseUri, it))
786+
Result.Success(ContentUris.withAppendedId(playlistsBaseUri, it))
788787
}
789788

790789
override suspend fun renamePlaylist(playlistUri: Uri, name: String) = when {
791790
playlistUri == favoritesUri -> Result.Failure(Error.IO)
792791
else -> database.getPlaylistDao().rename(ContentUris.parseId(playlistUri), name).let {
793-
Result.Success<_, Error>(Unit)
792+
Result.Success(Unit)
794793
}
795794
}
796795

797796
override suspend fun deletePlaylist(playlistUri: Uri) = when {
798797
playlistUri == favoritesUri -> Result.Failure(Error.IO)
799798
else -> database.getPlaylistDao().delete(ContentUris.parseId(playlistUri)).let {
800-
Result.Success<_, Error>(Unit)
799+
Result.Success(Unit)
801800
}
802801
}
803802

@@ -842,7 +841,7 @@ class MediaStoreDataSource(
842841
true -> database.getFavoriteDao().add(audioUri)
843842
false -> database.getFavoriteDao().remove(audioUri)
844843
}.let {
845-
Result.Success<_, Error>(Unit)
844+
Result.Success(Unit)
846845
}
847846

848847
fun audios() = contentResolver.queryFlow(

app/src/main/java/org/lineageos/twelve/datasources/ProvidersManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class ProvidersManager<T : ProvidersManager.Instance>(
115115
providersToInstance.firstNotNullOfOrNull { (provider, instance) ->
116116
provider.takeIf { instance.isMediaItemCompatible(mediaItemUri) }
117117
}?.let {
118-
Result.Success<_, Error>(it as ProviderIdentifier)
118+
Result.Success(it as ProviderIdentifier)
119119
} ?: Result.Failure(Error.NOT_FOUND)
120120
}
121121

app/src/main/java/org/lineageos/twelve/models/FlowResult.kt

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,26 @@ import kotlin.experimental.ExperimentalTypeInference
1717
/**
1818
* A data holder used for flows.
1919
*/
20-
sealed interface FlowResult<T, E> {
21-
class Loading<T, E> : FlowResult<T, E>
22-
class Success<T, E>(val data: T) : FlowResult<T, E>
23-
class Failure<T, E>(val error: E, val throwable: Throwable? = null) : FlowResult<T, E>
20+
sealed interface FlowResult<out T, out E> {
21+
/**
22+
* The result is loading.
23+
*/
24+
data object Loading : FlowResult<Nothing, Nothing>
25+
26+
/**
27+
* The result is ready.
28+
*
29+
* @param data The obtained data
30+
*/
31+
data class Success<T>(val data: T) : FlowResult<T, Nothing>
32+
33+
/**
34+
* The request failed.
35+
*
36+
* @param error The error
37+
* @param throwable An optional [Throwable] object
38+
*/
39+
data class Failure<E>(val error: E, val throwable: Throwable? = null) : FlowResult<Nothing, E>
2440

2541
companion object {
2642
/**
@@ -36,9 +52,9 @@ sealed interface FlowResult<T, E> {
3652
* Convert a flow of [Result] to a flow of [FlowResult].
3753
*/
3854
@OptIn(ExperimentalCoroutinesApi::class)
39-
fun <T, E> Flow<Result<out T, out E>>.asFlowResult() = mapLatest {
55+
fun <T, E> Flow<Result<T, E>>.asFlowResult() = mapLatest {
4056
when (it) {
41-
is Result.Success -> Success<T, E>(it.data)
57+
is Result.Success -> Success(it.data)
4258
is Result.Failure -> Failure(it.error, it.throwable)
4359
}
4460
}
@@ -52,12 +68,12 @@ sealed interface FlowResult<T, E> {
5268
*/
5369
@OptIn(ExperimentalCoroutinesApi::class, ExperimentalTypeInference::class)
5470
fun <T, E, R> Flow<FlowResult<T, E>>.mapLatestFlowResult(
55-
@BuilderInference transform: suspend (value: T) -> FlowResult<R, E>
71+
transform: suspend (value: T) -> FlowResult<R, E>
5672
) = mapLatest {
5773
when (it) {
58-
is Loading -> Loading()
74+
is Loading -> it
5975
is Success -> transform(it.data)
60-
is Failure -> Failure(it.error, it.throwable)
76+
is Failure -> it
6177
}
6278
}
6379

@@ -70,7 +86,7 @@ sealed interface FlowResult<T, E> {
7086
*/
7187
@OptIn(ExperimentalTypeInference::class)
7288
fun <T, E, R> Flow<FlowResult<T, E>>.mapLatestData(
73-
@BuilderInference transform: suspend (value: T) -> R
89+
transform: suspend (value: T) -> R
7490
) = mapLatestFlowResult { Success(transform(it)) }
7591

7692
/**
@@ -79,17 +95,17 @@ sealed interface FlowResult<T, E> {
7995
*/
8096
@OptIn(ExperimentalTypeInference::class)
8197
fun <T, E, R> Flow<FlowResult<T, E>>.foldLatest(
82-
@BuilderInference onSuccess: suspend (value: T) -> R,
83-
@BuilderInference onError: suspend (error: E, throwable: Throwable?) -> R,
98+
onSuccess: suspend (value: T) -> R,
99+
onError: suspend (error: E, throwable: Throwable?) -> R,
84100
) = channelFlow {
85101
this@foldLatest.collectLatest {
86102
when (it) {
87103
is Loading -> {
88104
// Do nothing
89105
}
90106

91-
is Success -> trySend(onSuccess(it.data))
92-
is Failure -> trySend(onError(it.error, it.throwable))
107+
is Success -> send(onSuccess(it.data))
108+
is Failure -> send(onError(it.error, it.throwable))
93109
}
94110
}
95111
}

0 commit comments

Comments
 (0)