Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rx removal with coroutines #504

Merged
merged 11 commits into from
Jul 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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