书籍匹配URL #282、#203

This commit is contained in:
HapeLee
2025-11-11 00:56:25 +08:00
parent 59cf9b6389
commit 76feb9202a
7 changed files with 99 additions and 45 deletions
@@ -11,6 +11,7 @@ import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ActivityExploreShowBinding
import io.legado.app.databinding.ViewLoadMoreBinding
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.book.search.BookShelfState
import io.legado.app.ui.widget.recycler.LoadMoreView
import io.legado.app.utils.applyNavigationBarPadding
import io.legado.app.utils.startActivity
@@ -81,8 +82,8 @@ class ExploreShowActivity : VMBaseActivity<ActivityExploreShowBinding, ExploreSh
}
}
override fun isInBookshelf(name: String, author: String): Boolean {
return viewModel.isInBookShelf(name, author)
override fun getBookShelfState(name: String, author: String, url: String?): BookShelfState {
return viewModel.isInBookShelf(name, author, url)
}
override fun showBookInfo(book: Book) {
@@ -12,6 +12,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemSearchBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.ui.book.search.BookShelfState
import io.legado.app.ui.widget.text.AccentBgTextView
import io.legado.app.utils.dpToPx
import io.legado.app.utils.gone
@@ -46,7 +47,19 @@ class ExploreShowAdapter(context: Context, val callBack: CallBack) :
binding.run {
tvName.text = item.name
tvAuthor.text = context.getString(R.string.author_show, item.author)
ivInBookshelf.isVisible = callBack.isInBookshelf(item.name, item.author)
when (callBack.getBookShelfState(item.name, item.author, item.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
if (item.latestChapterTitle.isNullOrEmpty()) {
tvLasted.gone()
} else {
@@ -90,8 +103,21 @@ class ExploreShowAdapter(context: Context, val callBack: CallBack) :
binding.run {
bundle.keySet().forEach {
when (it) {
"isInBookshelf" -> ivInBookshelf.isVisible =
callBack.isInBookshelf(item.name, item.author)
"isInBookshelf" -> {
when (callBack.getBookShelfState(item.name, item.author, item.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
}
}
}
}
@@ -109,7 +135,7 @@ class ExploreShowAdapter(context: Context, val callBack: CallBack) :
/**
* 是否已经加入书架
*/
fun isInBookshelf(name: String, author: String): Boolean
fun getBookShelfState(name: String, author: String, url: String?): BookShelfState
fun showBookInfo(book: Book)
}
@@ -12,6 +12,8 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook
import io.legado.app.help.book.isNotShelf
import io.legado.app.model.webBook.WebBook
import io.legado.app.ui.book.search.BookKey
import io.legado.app.ui.book.search.BookShelfState
import io.legado.app.utils.printOnDebug
import io.legado.app.utils.stackTraceStr
import kotlinx.coroutines.Dispatchers.IO
@@ -23,7 +25,7 @@ import java.util.concurrent.ConcurrentHashMap
@OptIn(ExperimentalCoroutinesApi::class)
class ExploreShowViewModel(application: Application) : BaseViewModel(application) {
val bookshelf: MutableSet<String> = ConcurrentHashMap.newKeySet()
val bookshelf: MutableSet<BookKey> = ConcurrentHashMap.newKeySet()
val upAdapterLiveData = MutableLiveData<String>()
val booksData = MutableLiveData<List<SearchBook>>()
val errorLiveData = MutableLiveData<String>()
@@ -35,18 +37,12 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
init {
execute {
appDb.bookDao.flowAll().mapLatest { books ->
val keys = arrayListOf<String>()
books.filterNot { it.isNotShelf }
.forEach {
keys.add("${it.name}-${it.author}")
keys.add(it.name)
}
keys
}.catch {
AppLog.put("发现列表界面获取书籍数据失败\n${it.localizedMessage}", it)
}.collect {
AppLog.put("搜索界面获取书籍列表失败\n${it.localizedMessage}", it)
}.collect { books ->
bookshelf.clear()
bookshelf.addAll(it)
bookshelf.addAll(books.map { BookKey(it.name, it.author, it.bookUrl) })
upAdapterLiveData.postValue("isInBookshelf")
}
}.onError {
@@ -82,12 +78,14 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
}
}
fun isInBookShelf(name: String, author: String): Boolean {
return if (author.isNotBlank()) {
bookshelf.contains("$name-$author")
} else {
bookshelf.contains(name)
}
fun isInBookShelf(name: String, author: String, url: String?): BookShelfState {
val exactMatch = bookshelf.any { it.name == name && it.author == author && it.url == url }
if (exactMatch) return BookShelfState.IN_SHELF
val sameNameAuthor = bookshelf.any { it.name == name && it.author == author && it.url != url }
if (sameNameAuthor) return BookShelfState.SAME_NAME_AUTHOR
return BookShelfState.NOT_IN_SHELF
}
}
@@ -576,8 +576,8 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
/**
* 是否已经加入书架
*/
override fun isInBookshelf(name: String, author: String): Boolean {
return viewModel.isInBookShelf(name, author)
override fun getBookShelfState(name: String, author: String, url: String?): BookShelfState {
return viewModel.isInBookShelf(name, author, url)
}
/**
@@ -86,8 +86,19 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
binding.run {
tvName.text = searchBook.name
tvAuthor.text = context.getString(R.string.author_show, searchBook.author)
ivInBookshelf.isVisible =
callBack.isInBookshelf(searchBook.name, searchBook.author)
when (callBack.getBookShelfState(searchBook.name, searchBook.author, searchBook.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
bvOriginCount.text = searchBook.origins.size.toString()
upLasted(binding, searchBook.latestChapterTitle)
tvIntroduce.text = searchBook.trimIntro(context)
@@ -110,8 +121,21 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
"last" -> upLasted(binding, searchBook.latestChapterTitle)
"intro" -> tvIntroduce.text = searchBook.trimIntro(context)
"kind" -> upKind(binding, searchBook.getKindList())
"isInBookshelf" -> ivInBookshelf.isVisible =
callBack.isInBookshelf(searchBook.name, searchBook.author)
"isInBookshelf" -> {
when (callBack.getBookShelfState(searchBook.name, searchBook.author, searchBook.bookUrl)) {
BookShelfState.IN_SHELF -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.remove_from_bookshelf)
}
BookShelfState.SAME_NAME_AUTHOR -> {
ivInBookshelf.isVisible = true
tvBookshelf.text = context.getString(R.string.same_name_book)
}
BookShelfState.NOT_IN_SHELF -> {
ivInBookshelf.isVisible = false
}
}
}
"cover" -> ivCover.load(
searchBook.coverUrl,
searchBook.name,
@@ -165,7 +189,7 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
/**
* 是否已经加入书架
*/
fun isInBookshelf(name: String, author: String): Boolean
fun getBookShelfState(name: String, author: String, url: String?): BookShelfState
/**
* 显示书籍详情
@@ -20,10 +20,18 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.mapLatest
import java.util.concurrent.ConcurrentHashMap
enum class BookShelfState {
IN_SHELF,
SAME_NAME_AUTHOR,
NOT_IN_SHELF
}
data class BookKey(val name: String, val author: String, val url: String?)
@OptIn(ExperimentalCoroutinesApi::class)
class SearchViewModel(application: Application) : BaseViewModel(application) {
val handler = Handler(Looper.getMainLooper())
val bookshelf: MutableSet<String> = ConcurrentHashMap.newKeySet()
val bookshelf: MutableSet<BookKey> = ConcurrentHashMap.newKeySet()
val upAdapterLiveData = MutableLiveData<String>()
var searchBookLiveData = ConflateLiveData<List<SearchBook>>(1000)
val searchScope: SearchScope = SearchScope(AppConfig.searchScope)
@@ -70,18 +78,12 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
init {
execute {
appDb.bookDao.flowAll().mapLatest { books ->
val keys = arrayListOf<String>()
books.filterNot { it.isNotShelf }
.forEach {
keys.add("${it.name}-${it.author}")
keys.add(it.name)
}
keys
}.catch {
AppLog.put("搜索界面获取书籍列表失败\n${it.localizedMessage}", it)
}.collect {
}.collect { books ->
bookshelf.clear()
bookshelf.addAll(it)
bookshelf.addAll(books.map { BookKey(it.name, it.author, it.bookUrl) })
upAdapterLiveData.postValue("isInBookshelf")
}
}.onError {
@@ -89,12 +91,14 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
}
}
fun isInBookShelf(name: String, author: String): Boolean {
return if (author.isNotBlank()) {
bookshelf.contains("$name-$author")
} else {
bookshelf.contains(name)
}
fun isInBookShelf(name: String, author: String, url: String?): BookShelfState {
val exactMatch = bookshelf.any { it.name == name && it.author == author && it.url == url }
if (exactMatch) return BookShelfState.IN_SHELF
val sameNameAuthor = bookshelf.any { it.name == name && it.author == author && it.url != url }
if (sameNameAuthor) return BookShelfState.SAME_NAME_AUTHOR
return BookShelfState.NOT_IN_SHELF
}
/**