mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
优化
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
package io.legado.app.ui.book.changesource
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.adapter.DiffRecyclerAdapter
|
||||
import io.legado.app.base.adapter.ItemViewHolder
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.databinding.ItemChangeSourceBinding
|
||||
import io.legado.app.utils.invisible
|
||||
import io.legado.app.utils.visible
|
||||
import splitties.views.onLongClick
|
||||
|
||||
|
||||
class ChangeChapterSourceAdapter(
|
||||
context: Context,
|
||||
val viewModel: ChangeBookSourceViewModel,
|
||||
val callBack: CallBack
|
||||
) : DiffRecyclerAdapter<SearchBook, ItemChangeSourceBinding>(context) {
|
||||
|
||||
override val diffItemCallback = object : DiffUtil.ItemCallback<SearchBook>() {
|
||||
override fun areItemsTheSame(oldItem: SearchBook, newItem: SearchBook): Boolean {
|
||||
return oldItem.bookUrl == newItem.bookUrl
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: SearchBook, newItem: SearchBook): Boolean {
|
||||
return oldItem.originName == newItem.originName
|
||||
&& oldItem.getDisplayLastChapterTitle() == newItem.getDisplayLastChapterTitle()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun getViewBinding(parent: ViewGroup): ItemChangeSourceBinding {
|
||||
return ItemChangeSourceBinding.inflate(inflater, parent, false)
|
||||
}
|
||||
|
||||
override fun convert(
|
||||
holder: ItemViewHolder,
|
||||
binding: ItemChangeSourceBinding,
|
||||
item: SearchBook,
|
||||
payloads: MutableList<Any>
|
||||
) {
|
||||
val bundle = payloads.getOrNull(0) as? Bundle
|
||||
binding.apply {
|
||||
if (bundle == null) {
|
||||
tvOrigin.text = item.originName
|
||||
tvAuthor.text = item.author
|
||||
tvLast.text = item.getDisplayLastChapterTitle()
|
||||
if (callBack.bookUrl == item.bookUrl) {
|
||||
ivChecked.visible()
|
||||
} else {
|
||||
ivChecked.invisible()
|
||||
}
|
||||
} else {
|
||||
bundle.keySet().map {
|
||||
when (it) {
|
||||
"name" -> tvOrigin.text = item.originName
|
||||
"latest" -> tvLast.text = item.getDisplayLastChapterTitle()
|
||||
"upCurSource" -> if (callBack.bookUrl == item.bookUrl) {
|
||||
ivChecked.visible()
|
||||
} else {
|
||||
ivChecked.invisible()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun registerListener(holder: ItemViewHolder, binding: ItemChangeSourceBinding) {
|
||||
holder.itemView.setOnClickListener {
|
||||
getItem(holder.layoutPosition)?.let {
|
||||
callBack.changeTo(it)
|
||||
}
|
||||
}
|
||||
holder.itemView.onLongClick {
|
||||
showMenu(holder.itemView, getItem(holder.layoutPosition))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showMenu(view: View, searchBook: SearchBook?) {
|
||||
searchBook ?: return
|
||||
val popupMenu = PopupMenu(context, view)
|
||||
popupMenu.inflate(R.menu.change_source_item)
|
||||
popupMenu.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_top_source -> {
|
||||
callBack.topSource(searchBook)
|
||||
}
|
||||
R.id.menu_bottom_source -> {
|
||||
callBack.bottomSource(searchBook)
|
||||
}
|
||||
R.id.menu_edit_source -> {
|
||||
callBack.editSource(searchBook)
|
||||
}
|
||||
R.id.menu_disable_source -> {
|
||||
callBack.disableSource(searchBook)
|
||||
}
|
||||
R.id.menu_delete_source -> {
|
||||
callBack.deleteSource(searchBook)
|
||||
updateItems(0, itemCount, listOf<Int>())
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
val bookUrl: String?
|
||||
fun changeTo(searchBook: SearchBook)
|
||||
fun topSource(searchBook: SearchBook)
|
||||
fun bottomSource(searchBook: SearchBook)
|
||||
fun editSource(searchBook: SearchBook)
|
||||
fun disableSource(searchBook: SearchBook)
|
||||
fun deleteSource(searchBook: SearchBook)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
package io.legado.app.ui.book.changesource
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.legado.app.R
|
||||
import io.legado.app.base.BaseDialogFragment
|
||||
import io.legado.app.constant.AppPattern
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.databinding.DialogChangeSourceBinding
|
||||
import io.legado.app.help.AppConfig
|
||||
import io.legado.app.lib.theme.primaryColor
|
||||
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
||||
import io.legado.app.ui.book.source.manage.BookSourceActivity
|
||||
import io.legado.app.ui.widget.recycler.VerticalDivider
|
||||
import io.legado.app.utils.*
|
||||
import io.legado.app.utils.viewbindingdelegate.viewBinding
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_change_source),
|
||||
Toolbar.OnMenuItemClickListener,
|
||||
ChangeBookSourceAdapter.CallBack {
|
||||
|
||||
constructor(name: String, author: String) : this() {
|
||||
arguments = Bundle().apply {
|
||||
putString("name", name)
|
||||
putString("author", author)
|
||||
}
|
||||
}
|
||||
|
||||
private val binding by viewBinding(DialogChangeSourceBinding::bind)
|
||||
private val groups = linkedSetOf<String>()
|
||||
private val callBack: CallBack? get() = activity as? CallBack
|
||||
private val viewModel: ChangeBookSourceViewModel by viewModels()
|
||||
private val adapter by lazy { ChangeBookSourceAdapter(requireContext(), viewModel, this) }
|
||||
private val editSourceResult =
|
||||
registerForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) {
|
||||
viewModel.startSearch()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setLayout(0.9f, 0.9f)
|
||||
}
|
||||
|
||||
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
|
||||
binding.toolBar.setBackgroundColor(primaryColor)
|
||||
viewModel.initData(arguments)
|
||||
showTitle()
|
||||
initMenu()
|
||||
initRecyclerView()
|
||||
initSearchView()
|
||||
initLiveData()
|
||||
}
|
||||
|
||||
private fun showTitle() {
|
||||
binding.toolBar.title = viewModel.name
|
||||
binding.toolBar.subtitle = viewModel.author
|
||||
}
|
||||
|
||||
private fun initMenu() {
|
||||
binding.toolBar.inflateMenu(R.menu.change_source)
|
||||
binding.toolBar.menu.applyTint(requireContext())
|
||||
binding.toolBar.setOnMenuItemClickListener(this)
|
||||
binding.toolBar.menu.findItem(R.id.menu_check_author)
|
||||
?.isChecked = AppConfig.changeSourceCheckAuthor
|
||||
binding.toolBar.menu.findItem(R.id.menu_load_info)
|
||||
?.isChecked = AppConfig.changeSourceLoadInfo
|
||||
binding.toolBar.menu.findItem(R.id.menu_load_toc)
|
||||
?.isChecked = AppConfig.changeSourceLoadToc
|
||||
}
|
||||
|
||||
private fun initRecyclerView() {
|
||||
binding.recyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.recyclerView.addItemDecoration(VerticalDivider(requireContext()))
|
||||
binding.recyclerView.adapter = adapter
|
||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == 0) {
|
||||
binding.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
|
||||
if (toPosition == 0) {
|
||||
binding.recyclerView.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initSearchView() {
|
||||
val searchView = binding.toolBar.menu.findItem(R.id.menu_screen).actionView as SearchView
|
||||
searchView.setOnCloseListener {
|
||||
showTitle()
|
||||
false
|
||||
}
|
||||
searchView.setOnSearchClickListener {
|
||||
binding.toolBar.title = ""
|
||||
binding.toolBar.subtitle = ""
|
||||
}
|
||||
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
viewModel.screen(newText)
|
||||
return false
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun initLiveData() {
|
||||
viewModel.searchStateData.observe(viewLifecycleOwner) {
|
||||
binding.refreshProgressBar.isAutoLoading = it
|
||||
if (it) {
|
||||
startStopMenuItem?.let { item ->
|
||||
item.setIcon(R.drawable.ic_stop_black_24dp)
|
||||
item.setTitle(R.string.stop)
|
||||
}
|
||||
} else {
|
||||
startStopMenuItem?.let { item ->
|
||||
item.setIcon(R.drawable.ic_refresh_black_24dp)
|
||||
item.setTitle(R.string.refresh)
|
||||
}
|
||||
}
|
||||
binding.toolBar.menu.applyTint(requireContext())
|
||||
}
|
||||
launch {
|
||||
viewModel.searchDataFlow
|
||||
.collect {
|
||||
adapter.setItems(it)
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
launch {
|
||||
appDb.bookSourceDao.flowGroupEnabled().collect {
|
||||
groups.clear()
|
||||
it.map { group ->
|
||||
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
|
||||
}
|
||||
upGroupMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val startStopMenuItem: MenuItem?
|
||||
get() = binding.toolBar.menu.findItem(R.id.menu_start_stop)
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
when (item?.itemId) {
|
||||
R.id.menu_check_author -> {
|
||||
AppConfig.changeSourceCheckAuthor = !item.isChecked
|
||||
item.isChecked = !item.isChecked
|
||||
viewModel.refresh()
|
||||
}
|
||||
R.id.menu_load_toc -> {
|
||||
putPrefBoolean(PreferKey.changeSourceLoadToc, !item.isChecked)
|
||||
item.isChecked = !item.isChecked
|
||||
}
|
||||
R.id.menu_load_info -> {
|
||||
putPrefBoolean(PreferKey.changeSourceLoadInfo, !item.isChecked)
|
||||
item.isChecked = !item.isChecked
|
||||
}
|
||||
R.id.menu_start_stop -> viewModel.startOrStopSearch()
|
||||
R.id.menu_source_manage -> startActivity<BookSourceActivity>()
|
||||
else -> if (item?.groupId == R.id.source_group) {
|
||||
if (!item.isChecked) {
|
||||
item.isChecked = true
|
||||
if (item.title.toString() == getString(R.string.all_source)) {
|
||||
putPrefString("searchGroup", "")
|
||||
} else {
|
||||
putPrefString("searchGroup", item.title.toString())
|
||||
}
|
||||
viewModel.startOrStopSearch()
|
||||
viewModel.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun changeTo(searchBook: SearchBook) {
|
||||
changeSource(searchBook)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
override val bookUrl: String?
|
||||
get() = callBack?.oldBook?.bookUrl
|
||||
|
||||
override fun topSource(searchBook: SearchBook) {
|
||||
viewModel.topSource(searchBook)
|
||||
}
|
||||
|
||||
override fun bottomSource(searchBook: SearchBook) {
|
||||
viewModel.bottomSource(searchBook)
|
||||
}
|
||||
|
||||
override fun editSource(searchBook: SearchBook) {
|
||||
editSourceResult.launch {
|
||||
putExtra("sourceUrl", searchBook.origin)
|
||||
}
|
||||
}
|
||||
|
||||
override fun disableSource(searchBook: SearchBook) {
|
||||
viewModel.disableSource(searchBook)
|
||||
}
|
||||
|
||||
override fun deleteSource(searchBook: SearchBook) {
|
||||
viewModel.del(searchBook)
|
||||
if (bookUrl == searchBook.bookUrl) {
|
||||
viewModel.firstSourceOrNull(searchBook)?.let {
|
||||
changeSource(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeSource(searchBook: SearchBook) {
|
||||
try {
|
||||
val book = searchBook.toBook()
|
||||
book.upInfoFromOld(callBack?.oldBook)
|
||||
val source = appDb.bookSourceDao.getBookSource(book.origin)
|
||||
callBack?.changeTo(source!!, book)
|
||||
searchBook.time = System.currentTimeMillis()
|
||||
viewModel.updateSource(searchBook)
|
||||
} catch (e: Exception) {
|
||||
toastOnUi("换源失败\n${e.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新分组菜单
|
||||
*/
|
||||
private fun upGroupMenu() {
|
||||
val menu: Menu = binding.toolBar.menu
|
||||
val selectedGroup = getPrefString("searchGroup")
|
||||
menu.removeGroup(R.id.source_group)
|
||||
val allItem = menu.add(R.id.source_group, Menu.NONE, Menu.NONE, R.string.all_source)
|
||||
var hasSelectedGroup = false
|
||||
groups.sortedWith { o1, o2 ->
|
||||
o1.cnCompare(o2)
|
||||
}.forEach { group ->
|
||||
menu.add(R.id.source_group, Menu.NONE, Menu.NONE, group)?.let {
|
||||
if (group == selectedGroup) {
|
||||
it.isChecked = true
|
||||
hasSelectedGroup = true
|
||||
}
|
||||
}
|
||||
}
|
||||
menu.setGroupCheckable(R.id.source_group, true, true)
|
||||
if (!hasSelectedGroup) {
|
||||
allItem.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun observeLiveBus() {
|
||||
observeEvent<String>(EventBus.SOURCE_CHANGED) {
|
||||
adapter.notifyItemRangeChanged(
|
||||
0,
|
||||
adapter.itemCount,
|
||||
bundleOf(Pair("upCurSource", bookUrl))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface CallBack {
|
||||
val oldBook: Book?
|
||||
fun changeTo(source: BookSource, book: Book)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
package io.legado.app.ui.book.changesource
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Bundle
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import cn.hutool.core.collection.ConcurrentHashSet
|
||||
import io.legado.app.base.BaseViewModel
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.constant.AppPattern
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.SearchBook
|
||||
import io.legado.app.help.AppConfig
|
||||
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.Dispatchers.IO
|
||||
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 kotlinx.coroutines.flow.map
|
||||
import splitties.init.appCtx
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.min
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class ChangeChapterSourceViewModel(application: Application) : BaseViewModel(application) {
|
||||
private val threadCount = AppConfig.threadCount
|
||||
private var searchPool: ExecutorCoroutineDispatcher? = null
|
||||
val searchStateData = MutableLiveData<Boolean>()
|
||||
var name: String = ""
|
||||
var author: String = ""
|
||||
private var tasks = CompositeCoroutine()
|
||||
private var screenKey: String = ""
|
||||
private var bookSourceList = arrayListOf<BookSource>()
|
||||
private val searchBooks = ConcurrentHashSet<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)
|
||||
when {
|
||||
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 {
|
||||
searchBooks.sortedBy { it.originOrder }
|
||||
}.flowOn(IO)
|
||||
|
||||
@Volatile
|
||||
private var searchIndex = -1
|
||||
|
||||
fun initData(arguments: Bundle?) {
|
||||
arguments?.let { bundle ->
|
||||
bundle.getString("name")?.let {
|
||||
name = it
|
||||
}
|
||||
bundle.getString("author")?.let {
|
||||
author = it.replace(AppPattern.authorRegex, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSearchPool() {
|
||||
searchPool = Executors
|
||||
.newFixedThreadPool(min(threadCount, AppConst.MAX_THREAD)).asCoroutineDispatcher()
|
||||
searchIndex = -1
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
getDbSearchBooks().let {
|
||||
searchBooks.clear()
|
||||
searchBooks.addAll(it)
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
fun startSearch() {
|
||||
execute {
|
||||
stopSearch()
|
||||
appDb.searchBookDao.clear(name, author)
|
||||
searchBooks.clear()
|
||||
bookSourceList.clear()
|
||||
if (searchGroup.isBlank()) {
|
||||
bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
|
||||
} else {
|
||||
val sources = appDb.bookSourceDao.getEnabledByGroup(searchGroup)
|
||||
if (sources.isEmpty()) {
|
||||
bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
|
||||
} else {
|
||||
bookSourceList.addAll(sources)
|
||||
}
|
||||
}
|
||||
searchStateData.postValue(true)
|
||||
initSearchPool()
|
||||
for (i in 0 until threadCount) {
|
||||
search()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun search() {
|
||||
synchronized(this) {
|
||||
if (searchIndex >= bookSourceList.lastIndex) {
|
||||
return
|
||||
}
|
||||
searchIndex++
|
||||
}
|
||||
val source = bookSourceList[searchIndex]
|
||||
val task = WebBook
|
||||
.searchBook(viewModelScope, source, name, context = searchPool!!)
|
||||
.timeout(60000L)
|
||||
.onSuccess(IO) {
|
||||
it.forEach { searchBook ->
|
||||
if (searchBook.name == name) {
|
||||
if ((AppConfig.changeSourceCheckAuthor && searchBook.author.contains(author))
|
||||
|| !AppConfig.changeSourceCheckAuthor
|
||||
) {
|
||||
if (searchBook.latestChapterTitle.isNullOrEmpty()) {
|
||||
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
|
||||
loadBookInfo(source, searchBook.toBook())
|
||||
} else {
|
||||
searchCallback?.searchSuccess(searchBook)
|
||||
}
|
||||
} else {
|
||||
searchCallback?.searchSuccess(searchBook)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onFinally(searchPool) {
|
||||
synchronized(this) {
|
||||
if (searchIndex < bookSourceList.lastIndex) {
|
||||
search()
|
||||
} else {
|
||||
searchIndex++
|
||||
}
|
||||
if (searchIndex >= bookSourceList.lastIndex + bookSourceList.size
|
||||
|| searchIndex >= bookSourceList.lastIndex + threadCount
|
||||
) {
|
||||
searchStateData.postValue(false)
|
||||
tasks.clear()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
tasks.add(task)
|
||||
|
||||
}
|
||||
|
||||
private fun loadBookInfo(source: BookSource, book: Book) {
|
||||
WebBook.getBookInfo(viewModelScope, source, book, context = searchPool!!)
|
||||
.onSuccess(IO) {
|
||||
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
|
||||
loadBookToc(source, book)
|
||||
} else {
|
||||
//从详情页里获取最新章节
|
||||
book.latestChapterTitle = it.latestChapterTitle
|
||||
val searchBook = book.toSearchBook()
|
||||
searchCallback?.searchSuccess(searchBook)
|
||||
}
|
||||
}.onError(IO) {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadBookToc(source: BookSource, book: Book) {
|
||||
WebBook.getChapterList(viewModelScope, source, book, context = searchPool!!)
|
||||
.onSuccess(IO) { chapters ->
|
||||
book.latestChapterTitle = chapters.last().title
|
||||
val searchBook: SearchBook = book.toSearchBook()
|
||||
searchCallback?.searchSuccess(searchBook)
|
||||
}.onError(IO) {
|
||||
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 {
|
||||
getDbSearchBooks().let {
|
||||
searchBooks.clear()
|
||||
searchBooks.addAll(it)
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun startOrStopSearch() {
|
||||
if (tasks.isEmpty) {
|
||||
startSearch()
|
||||
} else {
|
||||
stopSearch()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopSearch() {
|
||||
tasks.clear()
|
||||
searchPool?.close()
|
||||
searchStateData.postValue(false)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
searchPool?.close()
|
||||
}
|
||||
|
||||
fun disableSource(searchBook: SearchBook) {
|
||||
execute {
|
||||
appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
|
||||
source.enabled = false
|
||||
appDb.bookSourceDao.update(source)
|
||||
}
|
||||
searchBooks.remove(searchBook)
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
fun topSource(searchBook: SearchBook) {
|
||||
execute {
|
||||
appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
|
||||
val minOrder = appDb.bookSourceDao.minOrder - 1
|
||||
source.customOrder = minOrder
|
||||
searchBook.originOrder = source.customOrder
|
||||
appDb.bookSourceDao.update(source)
|
||||
updateSource(searchBook)
|
||||
}
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
fun bottomSource(searchBook: SearchBook) {
|
||||
execute {
|
||||
appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
|
||||
val maxOrder = appDb.bookSourceDao.maxOrder + 1
|
||||
source.customOrder = maxOrder
|
||||
searchBook.originOrder = source.customOrder
|
||||
appDb.bookSourceDao.update(source)
|
||||
updateSource(searchBook)
|
||||
}
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSource(searchBook: SearchBook) {
|
||||
appDb.searchBookDao.update(searchBook)
|
||||
}
|
||||
|
||||
fun del(searchBook: SearchBook) {
|
||||
execute {
|
||||
appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
|
||||
appDb.bookSourceDao.delete(source)
|
||||
appDb.searchBookDao.delete(searchBook)
|
||||
}
|
||||
}
|
||||
searchBooks.remove(searchBook)
|
||||
searchCallback?.upAdapter()
|
||||
}
|
||||
|
||||
fun firstSourceOrNull(searchBook: SearchBook): SearchBook? {
|
||||
return searchBooks.firstOrNull { it.bookUrl != searchBook.bookUrl }
|
||||
}
|
||||
|
||||
interface SourceCallback {
|
||||
|
||||
fun searchSuccess(searchBook: SearchBook)
|
||||
|
||||
fun upAdapter()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user