Skip to content

Commit

Permalink
Merge pull request #504 from jakepurple13/rx_removal_with_coroutines
Browse files Browse the repository at this point in the history
Rx removal with coroutines
  • Loading branch information
jakepurple13 authored Jul 8, 2022
2 parents 1fc7e43 + be9a845 commit 2900e99
Show file tree
Hide file tree
Showing 35 changed files with 2,371 additions and 365 deletions.
5 changes: 5 additions & 0 deletions Models/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,9 @@ dependencies {

//Rx
implementation rxkotlin

//Coroutines
implementation coroutinesCore
implementation coroutinesAndroid
implementation coroutinesRX
}
60 changes: 60 additions & 0 deletions Models/src/main/java/com/programmersbox/models/Models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.programmersbox.models

import io.reactivex.Single
import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import java.io.Serializable

data class ItemModel(
Expand All @@ -13,6 +15,7 @@ data class ItemModel(
) : Serializable {
val extras = mutableMapOf<String, Any>()
fun toInfoModel() = source.getItemInfo(this)
fun toInfoModelFlow() = source.getItemInfoFlow(this)
}

data class InfoModel(
Expand All @@ -37,6 +40,7 @@ data class ChapterModel(
) : Serializable {
var uploadedTime: Long? = null
fun getChapterInfo() = source.getChapterInfo(this)
fun getChapterInfoFlow() = source.getChapterInfoFlow(this)
val extras = mutableMapOf<String, Any>()
}

Expand All @@ -62,18 +66,74 @@ interface ApiService : Serializable {
val canPlay: Boolean get() = true
val canDownload: Boolean get() = true
fun getRecent(page: Int = 1): Single<List<ItemModel>>
fun getRecentFlow(page: Int = 1): Flow<List<ItemModel>> = flow { emit(recent(page)) }.dispatchIo()
suspend fun recent(page: Int): List<ItemModel> = emptyList()

fun getList(page: Int = 1): Single<List<ItemModel>>
fun getListFlow(page: Int = 1): Flow<List<ItemModel>> = flow { emit(allList(page)) }.dispatchIo()
suspend fun allList(page: Int): List<ItemModel> = emptyList()

fun getItemInfo(model: ItemModel): Single<InfoModel>
fun getItemInfoFlow(model: ItemModel): Flow<Result<InfoModel>> = flow {
emit(
try {
Result.success(itemInfo(model))
} catch (e: Exception) {
e.printStackTrace()
Result.failure(e)
}
)
}
.dispatchIo()

suspend fun itemInfo(model: ItemModel): InfoModel = error("Need to create an itemInfo")

suspend fun search(searchText: CharSequence, page: Int = 1, list: List<ItemModel>): List<ItemModel> =
list.filter { it.title.contains(searchText, true) }

fun searchList(searchText: CharSequence, page: Int = 1, list: List<ItemModel>): Single<List<ItemModel>> =
Single.create { e -> e.onSuccess(list.filter { it.title.contains(searchText, true) }) }

fun searchListFlow(searchText: CharSequence, page: Int = 1, list: List<ItemModel>): Flow<List<ItemModel>> =
flow { emit(search(searchText, page, list)) }

fun searchSourceList(searchText: CharSequence, page: Int = 1, list: List<ItemModel>): Flow<List<ItemModel>> = flow {
if (searchText.isBlank()) throw Exception("No search necessary")
emitAll(searchListFlow(searchText, page, list))
}
.dispatchIo()
.catch {
it.printStackTrace()
emitAll(flow { emit(list.filter { it.title.contains(searchText, true) }) })
}

fun getChapterInfo(chapterModel: ChapterModel): Single<List<Storage>>
fun getChapterInfoFlow(chapterModel: ChapterModel): Flow<List<Storage>> = flow { emit(chapterInfo(chapterModel)) }.dispatchIo()
suspend fun chapterInfo(chapterModel: ChapterModel): List<Storage> = emptyList()

fun getSourceByUrl(url: String): Single<ItemModel> = Single.create {
it.onSuccess(ItemModel("", "", url, "", this))
}

fun getSourceByUrlFlow(url: String): Flow<ItemModel> = flow { emit(sourceByUrl(url)) }
.dispatchIo()
.catch {
it.printStackTrace()
emit(ItemModel("", "", url, "", this@ApiService))
}

suspend fun sourceByUrl(url: String): ItemModel = error("Not setup")

val serviceName: String get() = this::class.java.name

fun <T> Flow<List<T>>.dispatchIoAndCatchList() = this
.dispatchIo()
.catch {
it.printStackTrace()
emit(emptyList())
}

fun <T> Flow<T>.dispatchIo() = this.flowOn(Dispatchers.IO)
}

val sourcePublish = BehaviorSubject.create<ApiService>()
60 changes: 28 additions & 32 deletions UIViews/src/main/java/com/programmersbox/uiviews/AllFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,15 @@ import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import androidx.compose.material3.MaterialTheme as M3MaterialTheme
import androidx.compose.material3.contentColorFor as m3ContentColorFor

class AllViewModel(dao: ItemDao, context: Context? = null) : ViewModel() {

val searchPublisher = BehaviorSubject.createDefault<List<ItemModel>>(emptyList())
var searchText by mutableStateOf("")
var searchList by mutableStateOf<List<ItemModel>>(emptyList())

var isSearching by mutableStateOf(false)

Expand Down Expand Up @@ -111,32 +112,28 @@ class AllViewModel(dao: ItemDao, context: Context? = null) : ViewModel() {
}

private fun sourceLoadCompose(context: Context?, sources: ApiService) {
sources
.getList(count)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnError { context?.showErrorToast() }
.onErrorReturnItem(emptyList())
.doOnSubscribe { isRefreshing = true }
.subscribeBy {
sourceList.addAll(it)
isRefreshing = false
}
.addTo(disposable)
viewModelScope.launch {
sources
.getListFlow(count)
.dispatchIoAndCatchList { context?.showErrorToast() }
.onStart { isRefreshing = true }
.onEach {
sourceList.addAll(it)
isRefreshing = false
}
.collect()
}
}

fun search(searchText: String) {
sourcePublish.value
?.searchList(searchText, 1, sourceList)
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.doOnSubscribe { isSearching = true }
?.onErrorReturnItem(sourceList)
?.subscribeBy {
searchPublisher.onNext(it)
isSearching = false
}
?.addTo(disposable)
fun search() {
viewModelScope.launch {
sourcePublish.value
?.searchSourceList(searchText, 1, sourceList)
?.onStart { isSearching = true }
?.onEach { searchList = it }
?.onCompletion { isSearching = false }
?.collect()
}
}

override fun onCleared() {
Expand Down Expand Up @@ -233,8 +230,7 @@ fun AllView(
sheetPeekHeight = ButtonDefaults.MinHeight + 4.dp,
sheetContent = {
val focusManager = LocalFocusManager.current
val searchList by allVm.searchPublisher.subscribeAsState(initial = emptyList())
var searchText by rememberSaveable { mutableStateOf("") }
val searchList = allVm.searchList
val searchTopAppBarScrollState = rememberTopAppBarScrollState()
val scrollBehavior = remember { TopAppBarDefaults.pinnedScrollBehavior(searchTopAppBarScrollState) }
Scaffold(
Expand All @@ -261,8 +257,8 @@ fun AllView(
) { Text(stringResource(R.string.search)) }

androidx.compose.material3.OutlinedTextField(
value = searchText,
onValueChange = { searchText = it },
value = allVm.searchText,
onValueChange = { allVm.searchText = it },
label = {
Text(
stringResource(
Expand All @@ -274,7 +270,7 @@ fun AllView(
trailingIcon = {
Row(verticalAlignment = Alignment.CenterVertically) {
Text(searchList.size.toString())
IconButton(onClick = { searchText = "" }) {
IconButton(onClick = { allVm.searchText = "" }) {
Icon(Icons.Default.Cancel, null)
}
}
Expand All @@ -286,7 +282,7 @@ fun AllView(
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(onSearch = {
focusManager.clearFocus()
allVm.search(searchText)
allVm.search()
})
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ import androidx.compose.material.icons.filled.BrokenImage
import androidx.compose.material.icons.filled.BrowseGallery
import androidx.compose.material.icons.filled.History
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.material3.*
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -114,7 +111,12 @@ abstract class BaseMainActivity : AppCompatActivity() {
OtakuMaterialTheme(navController, genericInfo) {
val showAllItem by showAll.collectAsState(false)

com.google.accompanist.navigation.material.ModalBottomSheetLayout(bottomSheetNavigator) {
com.google.accompanist.navigation.material.ModalBottomSheetLayout(
bottomSheetNavigator,
sheetBackgroundColor = MaterialTheme.colorScheme.surface,
sheetContentColor = MaterialTheme.colorScheme.onSurface,
scrimColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.32f)
) {
androidx.compose.material3.Scaffold(
bottomBar = {
Column {
Expand Down
36 changes: 21 additions & 15 deletions UIViews/src/main/java/com/programmersbox/uiviews/DetailsFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,7 @@ import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import me.onebone.toolbar.CollapsingToolbarScaffold
Expand Down Expand Up @@ -240,18 +238,27 @@ class DetailViewModel(

var description: String by mutableStateOf("")

private val itemSub = itemModel?.url?.let { url ->
Cached.cache[url]?.let { Single.create { emitter -> emitter.onSuccess(it) } } ?: itemModel.toInfoModel()
}
?.doOnError { context.showErrorToast() }
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribeBy {
info = it
description = it.description
setup(it)
Cached.cache[it.url] = it
init {
viewModelScope.launch {
itemModel?.url?.let { url ->
Cached.cache[url]?.let { flow<Result<InfoModel>> { emit(Result.success(it)) } } ?: itemModel.toInfoModelFlow()
}
?.dispatchIo()
?.catch {
it.printStackTrace()
context.showErrorToast()
}
?.onEach {
if(it.isSuccess) {
info = it.getOrThrow()
description = it.getOrThrow().description
setup(it.getOrThrow())
Cached.cache[it.getOrThrow().url] = it.getOrThrow()
}
}
?.collect()
}
}

private val englishTranslator = TranslateItems()

Expand Down Expand Up @@ -322,7 +329,6 @@ class DetailViewModel(

override fun onCleared() {
super.onCleared()
itemSub?.dispose()
disposable.dispose()
itemListener.unregister()
chapterListener.unregister()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMap
import androidx.lifecycle.ViewModel
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage
import coil.request.ImageRequest
Expand All @@ -66,6 +67,7 @@ import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.onebone.toolbar.CollapsingToolbarScaffold
import me.onebone.toolbar.ScrollStrategy
Expand Down Expand Up @@ -93,24 +95,21 @@ class GlobalSearchViewModel(
}

fun searchForItems() {
Observable.combineLatest(
info.searchList()
.fastMap { a ->
a
.searchList(searchText, list = emptyList())
.timeout(5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorReturnItem(emptyList())
.map { SearchModel(a.serviceName, it) }
.toObservable()
}
) { it.filterIsInstance<SearchModel>().filter { s -> s.data.isNotEmpty() } }
.doOnSubscribe { isRefreshing = true }
.doOnComplete { isRefreshing = false }
.onErrorReturnItem(emptyList())
.subscribe { searchListPublisher = it }
.addTo(disposable)
viewModelScope.launch {
combine(
info.searchList()
.fastMap { a ->
a
.searchSourceList(searchText, list = emptyList())
.dispatchIoAndCatchList()
.map { SearchModel(a.serviceName, it) }
}
) { it.filterIsInstance<SearchModel>().filter { s -> s.data.isNotEmpty() } }
.onCompletion { isRefreshing = false }
.onStart { isRefreshing = true }
.onEach { searchListPublisher = it }
.collect()
}
}

override fun onCleared() {
Expand Down
Loading

0 comments on commit 2900e99

Please sign in to comment.