|
@@ -17,12 +17,18 @@ import io.legado.app.help.coroutine.CompositeCoroutine
|
|
|
import io.legado.app.model.webBook.WebBook
|
|
import io.legado.app.model.webBook.WebBook
|
|
|
import io.legado.app.utils.getPrefBoolean
|
|
import io.legado.app.utils.getPrefBoolean
|
|
|
import io.legado.app.utils.getPrefString
|
|
import io.legado.app.utils.getPrefString
|
|
|
-
|
|
|
|
|
-import kotlinx.coroutines.*
|
|
|
|
|
import kotlinx.coroutines.Dispatchers.IO
|
|
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 splitties.init.appCtx
|
|
|
import timber.log.Timber
|
|
import timber.log.Timber
|
|
|
-import java.util.concurrent.CopyOnWriteArraySet
|
|
|
|
|
|
|
+import java.util.concurrent.CopyOnWriteArrayList
|
|
|
import java.util.concurrent.Executors
|
|
import java.util.concurrent.Executors
|
|
|
import kotlin.math.min
|
|
import kotlin.math.min
|
|
|
|
|
|
|
@@ -30,17 +36,56 @@ import kotlin.math.min
|
|
|
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
|
|
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
|
|
|
private val threadCount = AppConfig.threadCount
|
|
private val threadCount = AppConfig.threadCount
|
|
|
private var searchPool: ExecutorCoroutineDispatcher? = null
|
|
private var searchPool: ExecutorCoroutineDispatcher? = null
|
|
|
- private var upAdapterJob: Job? = null
|
|
|
|
|
val searchStateData = MutableLiveData<Boolean>()
|
|
val searchStateData = MutableLiveData<Boolean>()
|
|
|
- val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
|
|
|
|
|
var name: String = ""
|
|
var name: String = ""
|
|
|
var author: String = ""
|
|
var author: String = ""
|
|
|
private var tasks = CompositeCoroutine()
|
|
private var tasks = CompositeCoroutine()
|
|
|
private var screenKey: String = ""
|
|
private var screenKey: String = ""
|
|
|
private var bookSourceList = arrayListOf<BookSource>()
|
|
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 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
|
|
@Volatile
|
|
|
private var searchIndex = -1
|
|
private var searchIndex = -1
|
|
@@ -62,56 +107,19 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
searchIndex = -1
|
|
searchIndex = -1
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- fun loadDbSearchBook() {
|
|
|
|
|
- execute {
|
|
|
|
|
|
|
+ fun refresh() {
|
|
|
|
|
+ getDbSearchBooks().let {
|
|
|
searchBooks.clear()
|
|
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() {
|
|
fun startSearch() {
|
|
|
execute {
|
|
execute {
|
|
|
stopSearch()
|
|
stopSearch()
|
|
|
appDb.searchBookDao.clear(name, author)
|
|
appDb.searchBookDao.clear(name, author)
|
|
|
searchBooks.clear()
|
|
searchBooks.clear()
|
|
|
- upAdapter()
|
|
|
|
|
bookSourceList.clear()
|
|
bookSourceList.clear()
|
|
|
if (searchGroup.isBlank()) {
|
|
if (searchGroup.isBlank()) {
|
|
|
bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
|
|
bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
|
|
@@ -152,10 +160,10 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
|
|
if (AppConfig.changeSourceLoadInfo || AppConfig.changeSourceLoadToc) {
|
|
|
loadBookInfo(source, searchBook.toBook())
|
|
loadBookInfo(source, searchBook.toBook())
|
|
|
} else {
|
|
} else {
|
|
|
- searchFinish(searchBook)
|
|
|
|
|
|
|
+ searchCallback?.searchSuccess(searchBook)
|
|
|
}
|
|
}
|
|
|
} else {
|
|
} else {
|
|
|
- searchFinish(searchBook)
|
|
|
|
|
|
|
+ searchCallback?.searchSuccess(searchBook)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -190,7 +198,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
//从详情页里获取最新章节
|
|
//从详情页里获取最新章节
|
|
|
book.latestChapterTitle = it.latestChapterTitle
|
|
book.latestChapterTitle = it.latestChapterTitle
|
|
|
val searchBook = book.toSearchBook()
|
|
val searchBook = book.toSearchBook()
|
|
|
- searchFinish(searchBook)
|
|
|
|
|
|
|
+ searchCallback?.searchSuccess(searchBook)
|
|
|
}
|
|
}
|
|
|
}.onError {
|
|
}.onError {
|
|
|
Timber.e(it)
|
|
Timber.e(it)
|
|
@@ -202,26 +210,38 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
.onSuccess(IO) { chapters ->
|
|
.onSuccess(IO) { chapters ->
|
|
|
book.latestChapterTitle = chapters.last().title
|
|
book.latestChapterTitle = chapters.last().title
|
|
|
val searchBook: SearchBook = book.toSearchBook()
|
|
val searchBook: SearchBook = book.toSearchBook()
|
|
|
- searchFinish(searchBook)
|
|
|
|
|
|
|
+ searchCallback?.searchSuccess(searchBook)
|
|
|
}.onError {
|
|
}.onError {
|
|
|
Timber.e(it)
|
|
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?) {
|
|
fun screen(key: String?) {
|
|
|
|
|
+ screenKey = key?.trim() ?: ""
|
|
|
execute {
|
|
execute {
|
|
|
- screenKey = key ?: ""
|
|
|
|
|
- if (key.isNullOrEmpty()) {
|
|
|
|
|
- loadDbSearchBook()
|
|
|
|
|
- } else {
|
|
|
|
|
- val items =
|
|
|
|
|
- appDb.searchBookDao.getChangeSourceSearch(name, author, screenKey, searchGroup)
|
|
|
|
|
|
|
+ getDbSearchBooks().let {
|
|
|
searchBooks.clear()
|
|
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)
|
|
appDb.bookSourceDao.update(source)
|
|
|
}
|
|
}
|
|
|
searchBooks.remove(searchBook)
|
|
searchBooks.remove(searchBook)
|
|
|
- upAdapter()
|
|
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -265,7 +285,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
appDb.bookSourceDao.update(source)
|
|
appDb.bookSourceDao.update(source)
|
|
|
updateSource(searchBook)
|
|
updateSource(searchBook)
|
|
|
}
|
|
}
|
|
|
- upAdapter()
|
|
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -278,7 +298,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
appDb.bookSourceDao.update(source)
|
|
appDb.bookSourceDao.update(source)
|
|
|
updateSource(searchBook)
|
|
updateSource(searchBook)
|
|
|
}
|
|
}
|
|
|
- upAdapter()
|
|
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -294,11 +314,19 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
searchBooks.remove(searchBook)
|
|
searchBooks.remove(searchBook)
|
|
|
- upAdapter()
|
|
|
|
|
|
|
+ searchCallback?.upAdapter()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fun firstSourceOrNull(searchBook: SearchBook): SearchBook? {
|
|
fun firstSourceOrNull(searchBook: SearchBook): SearchBook? {
|
|
|
return searchBooks.firstOrNull { it.bookUrl != searchBook.bookUrl }
|
|
return searchBooks.firstOrNull { it.bookUrl != searchBook.bookUrl }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ interface SourceCallback {
|
|
|
|
|
+
|
|
|
|
|
+ fun searchSuccess(searchBook: SearchBook)
|
|
|
|
|
+
|
|
|
|
|
+ fun upAdapter()
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|