pull/1649/head
kunfei 3 years ago
parent 912bee7c05
commit a0c287550f
  1. 2
      app/src/main/java/io/legado/app/data/dao/SearchBookDao.kt
  2. 2
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt
  3. 4
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  4. 11
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt
  5. 156
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt

@ -30,7 +30,7 @@ interface SearchBookDao {
t1.wordCount, t2.customOrder as originOrder
from searchBooks as t1 inner join book_sources as t2
on t1.origin = t2.bookSourceUrl
where t1.name = :name and t1.author = :author
where t1.name = :name and t1.author like '%'||:author||'%'
and originName like '%'||:key||'%' and t2.enabled = 1
and t2.bookSourceGroup like '%'||:sourceGroup||'%'
order by t2.customOrder"""

@ -14,7 +14,6 @@ import io.legado.app.utils.applyTint
import io.legado.app.utils.setLayout
import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.launch
/**
@ -67,7 +66,6 @@ class ChangeCoverDialog() : BaseDialogFragment(R.layout.dialog_change_cover),
private fun initData() {
launch {
viewModel.dataFlow
.conflate()
.collect {
adapter.setItems(it)
delay(1000)

@ -18,6 +18,7 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import java.util.concurrent.Executors
import kotlin.math.min
@ -54,7 +55,8 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
searchBooks.clear()
searchSuccess = null
}
}.flowOn(IO)
}.conflate()
.flowOn(IO)
@Volatile
private var searchIndex = -1

@ -51,7 +51,6 @@ class ChangeSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
viewModel.startSearch()
}
override fun onStart() {
super.onStart()
setLayout(0.9f, 0.9f)
@ -65,7 +64,6 @@ class ChangeSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
initRecyclerView()
initSearchView()
initLiveData()
viewModel.loadDbSearchBook()
}
private fun showTitle() {
@ -143,10 +141,11 @@ class ChangeSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
}
binding.toolBar.menu.applyTint(requireContext())
}
viewModel.searchBooksLiveData.observe(viewLifecycleOwner) {
launch {
viewModel.searchDataFlow
.collect {
adapter.setItems(it)
}
launch {
appDb.bookSourceDao.flowGroupEnabled().collect {
groups.clear()
it.map { group ->
@ -165,7 +164,7 @@ class ChangeSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
R.id.menu_check_author -> {
AppConfig.changeSourceCheckAuthor = !item.isChecked
item.isChecked = !item.isChecked
viewModel.loadDbSearchBook()
viewModel.refresh()
}
R.id.menu_load_toc -> {
putPrefBoolean(PreferKey.changeSourceLoadToc, !item.isChecked)
@ -186,7 +185,7 @@ class ChangeSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
putPrefString("searchGroup", item.title.toString())
}
viewModel.startOrStopSearch()
viewModel.loadDbSearchBook()
viewModel.refresh()
}
}
}

@ -17,12 +17,18 @@ import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefString
import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import splitties.init.appCtx
import timber.log.Timber
import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.Executors
import kotlin.math.min
@ -30,17 +36,56 @@ import kotlin.math.min
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
private val threadCount = AppConfig.threadCount
private var searchPool: ExecutorCoroutineDispatcher? = null
private var upAdapterJob: Job? = null
val searchStateData = MutableLiveData<Boolean>()
val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
var name: String = ""
var author: String = ""
private var tasks = CompositeCoroutine()
private var screenKey: String = ""
private var bookSourceList = arrayListOf<BookSource>()
private val searchBooks = CopyOnWriteArraySet<SearchBook>()
private var postTime = 0L
private val searchBooks = CopyOnWriteArrayList<SearchBook>()
private val searchGroup get() = appCtx.getPrefString("searchGroup") ?: ""
private var searchCallback: SourceCallback? = null
val searchDataFlow = callbackFlow {
searchCallback = object : SourceCallback {
override fun searchSuccess(searchBook: SearchBook) {
appDb.searchBookDao.insert(searchBook)
val index = searchBooks.indexOf(searchBook)
when {
index >= 0 -> searchBooks[index] = searchBook
screenKey.isEmpty() -> searchBooks.add(searchBook)
searchBook.name.contains(screenKey) -> searchBooks.add(searchBook)
else -> return
}
trySend(searchBooks)
}
override fun upAdapter() {
trySend(searchBooks)
}
}
getDbSearchBooks().let {
searchBooks.clear()
searchBooks.addAll(it)
trySend(searchBooks)
}
if (searchBooks.size <= 1) {
startSearch()
}
awaitClose {
searchCallback = null
}
}.conflate()
.map {
val data = searchBooks.sortedBy { it.originOrder }
delay(1000)
data
}.flowOn(IO)
@Volatile
private var searchIndex = -1
@ -62,56 +107,19 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
searchIndex = -1
}
fun loadDbSearchBook() {
execute {
fun refresh() {
getDbSearchBooks().let {
searchBooks.clear()
upAdapter()
val sbs = if (AppConfig.changeSourceCheckAuthor) {
appDb.searchBookDao.getChangeSourceSearch(name, author, searchGroup)
} else {
appDb.searchBookDao.getChangeSourceSearch(name, "", searchGroup)
}
searchBooks.addAll(sbs)
searchBooksLiveData.postValue(searchBooks.toList())
if (sbs.size <= 1) {
startSearch()
}
searchBooks.addAll(it)
searchCallback?.upAdapter()
}
}
@Synchronized
private fun upAdapter() {
if (System.currentTimeMillis() >= postTime + 500) {
upAdapterJob?.cancel()
postTime = System.currentTimeMillis()
val books = searchBooks.toList()
searchBooksLiveData.postValue(books.sortedBy { it.originOrder })
} else {
upAdapterJob?.cancel()
upAdapterJob = viewModelScope.launch {
delay(500)
upAdapter()
}
}
}
private fun searchFinish(searchBook: SearchBook) {
if (searchBooks.contains(searchBook)) return
appDb.searchBookDao.insert(searchBook)
if (screenKey.isEmpty()) {
searchBooks.add(searchBook)
} else if (searchBook.name.contains(screenKey)) {
searchBooks.add(searchBook)
}
upAdapter()
}
fun startSearch() {
execute {
stopSearch()
appDb.searchBookDao.clear(name, author)
searchBooks.clear()
upAdapter()
bookSourceList.clear()
if (searchGroup.isBlank()) {
bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
@ -152,10 +160,10 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
loadBookInfo(source, searchBook.toBook())
} else {
searchFinish(searchBook)
searchCallback?.searchSuccess(searchBook)
}
} else {
searchFinish(searchBook)
searchCallback?.searchSuccess(searchBook)
}
}
}
@ -190,7 +198,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
//从详情页里获取最新章节
book.latestChapterTitle = it.latestChapterTitle
val searchBook = book.toSearchBook()
searchFinish(searchBook)
searchCallback?.searchSuccess(searchBook)
}
}.onError {
Timber.e(it)
@ -202,26 +210,38 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
.onSuccess(IO) { chapters ->
book.latestChapterTitle = chapters.last().title
val searchBook: SearchBook = book.toSearchBook()
searchFinish(searchBook)
searchCallback?.searchSuccess(searchBook)
}.onError {
Timber.e(it)
}
}
private fun getDbSearchBooks(): List<SearchBook> {
return if (screenKey.isEmpty()) {
if (AppConfig.changeSourceCheckAuthor) {
appDb.searchBookDao.getChangeSourceSearch(name, author, searchGroup)
} else {
appDb.searchBookDao.getChangeSourceSearch(name, "", searchGroup)
}
} else {
if (AppConfig.changeSourceCheckAuthor) {
appDb.searchBookDao.getChangeSourceSearch(name, author, screenKey, searchGroup)
} else {
appDb.searchBookDao.getChangeSourceSearch(name, "", screenKey, searchGroup)
}
}
}
/**
* 筛选
*/
fun screen(key: String?) {
screenKey = key?.trim() ?: ""
execute {
screenKey = key ?: ""
if (key.isNullOrEmpty()) {
loadDbSearchBook()
} else {
val items =
appDb.searchBookDao.getChangeSourceSearch(name, author, screenKey, searchGroup)
getDbSearchBooks().let {
searchBooks.clear()
searchBooks.addAll(items)
upAdapter()
searchBooks.addAll(it)
searchCallback?.upAdapter()
}
}
}
@ -252,7 +272,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
appDb.bookSourceDao.update(source)
}
searchBooks.remove(searchBook)
upAdapter()
searchCallback?.upAdapter()
}
}
@ -265,7 +285,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
appDb.bookSourceDao.update(source)
updateSource(searchBook)
}
upAdapter()
searchCallback?.upAdapter()
}
}
@ -278,7 +298,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
appDb.bookSourceDao.update(source)
updateSource(searchBook)
}
upAdapter()
searchCallback?.upAdapter()
}
}
@ -294,11 +314,19 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
}
searchBooks.remove(searchBook)
upAdapter()
searchCallback?.upAdapter()
}
fun firstSourceOrNull(searchBook: SearchBook): SearchBook? {
return searchBooks.firstOrNull { it.bookUrl != searchBook.bookUrl }
}
interface SourceCallback {
fun searchSuccess(searchBook: SearchBook)
fun upAdapter()
}
}
Loading…
Cancel
Save