书籍匹配URL #282、#203
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user