pull/1649/head
parent
439fd5b5bf
commit
7a71ffe67e
@ -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() |
||||
|
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue