pull/1303/head
parent
c4ddc2b642
commit
da907755c4
@ -1,118 +0,0 @@ |
||||
package io.legado.app.model.webBook |
||||
|
||||
import io.legado.app.data.appDb |
||||
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.utils.getPrefString |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import kotlinx.coroutines.ExecutorCoroutineDispatcher |
||||
import kotlinx.coroutines.asCoroutineDispatcher |
||||
import splitties.init.appCtx |
||||
import java.util.concurrent.Executors |
||||
import kotlin.math.min |
||||
|
||||
class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) { |
||||
val threadCount = AppConfig.threadCount |
||||
private var searchPool: ExecutorCoroutineDispatcher? = null |
||||
private var mSearchId = 0L |
||||
private var searchPage = 1 |
||||
private var searchKey: String = "" |
||||
private var tasks = CompositeCoroutine() |
||||
private var bookSourceList = arrayListOf<BookSource>() |
||||
|
||||
@Volatile |
||||
private var searchIndex = -1 |
||||
|
||||
private fun initSearchPool() { |
||||
searchPool = Executors.newFixedThreadPool(min(threadCount,8)).asCoroutineDispatcher() |
||||
} |
||||
|
||||
fun search(searchId: Long, key: String) { |
||||
callBack.onSearchStart() |
||||
if (searchId != mSearchId) { |
||||
if (key.isEmpty()) { |
||||
callBack.onSearchCancel() |
||||
return |
||||
} else { |
||||
this.searchKey = key |
||||
} |
||||
if (mSearchId != 0L) { |
||||
close() |
||||
} |
||||
initSearchPool() |
||||
mSearchId = searchId |
||||
searchPage = 1 |
||||
val searchGroup = appCtx.getPrefString("searchGroup") ?: "" |
||||
bookSourceList.clear() |
||||
if (searchGroup.isBlank()) { |
||||
bookSourceList.addAll(appDb.bookSourceDao.allEnabled) |
||||
} else { |
||||
bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup)) |
||||
} |
||||
} else { |
||||
searchPage++ |
||||
} |
||||
searchIndex = -1 |
||||
for (i in 0 until threadCount) { |
||||
search(searchId) |
||||
} |
||||
} |
||||
|
||||
private fun search(searchId: Long) { |
||||
synchronized(this) { |
||||
if (searchIndex >= bookSourceList.lastIndex) { |
||||
return |
||||
} |
||||
searchIndex++ |
||||
val source = bookSourceList[searchIndex] |
||||
val task = WebBook.searchBook( |
||||
scope, |
||||
source, |
||||
searchKey, |
||||
searchPage, |
||||
context = searchPool!! |
||||
).timeout(30000L) |
||||
.onSuccess(searchPool) { |
||||
if (searchId == mSearchId) { |
||||
callBack.onSearchSuccess(it) |
||||
} |
||||
} |
||||
.onFinally(searchPool) { |
||||
synchronized(this) { |
||||
if (searchIndex < bookSourceList.lastIndex) { |
||||
search(searchId) |
||||
} else { |
||||
searchIndex++ |
||||
} |
||||
if (searchIndex >= bookSourceList.lastIndex |
||||
+ min(bookSourceList.size, threadCount) |
||||
) { |
||||
callBack.onSearchFinish() |
||||
} |
||||
} |
||||
} |
||||
tasks.add(task) |
||||
} |
||||
} |
||||
|
||||
fun cancelSearch() { |
||||
close() |
||||
callBack.onSearchCancel() |
||||
} |
||||
|
||||
fun close() { |
||||
tasks.clear() |
||||
searchPool?.close() |
||||
mSearchId = 0L |
||||
} |
||||
|
||||
interface CallBack { |
||||
fun onSearchStart() |
||||
fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) |
||||
fun onSearchFinish() |
||||
fun onSearchCancel() |
||||
} |
||||
|
||||
} |
@ -0,0 +1,200 @@ |
||||
package io.legado.app.model.webBook |
||||
|
||||
import io.legado.app.constant.PreferKey |
||||
import io.legado.app.data.appDb |
||||
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.utils.getPrefBoolean |
||||
import io.legado.app.utils.getPrefString |
||||
import kotlinx.coroutines.CoroutineScope |
||||
import kotlinx.coroutines.ExecutorCoroutineDispatcher |
||||
import kotlinx.coroutines.asCoroutineDispatcher |
||||
import kotlinx.coroutines.isActive |
||||
import splitties.init.appCtx |
||||
import java.util.concurrent.Executors |
||||
import kotlin.math.min |
||||
|
||||
class SearchModel(private val scope: CoroutineScope, private val callBack: CallBack) { |
||||
val threadCount = AppConfig.threadCount |
||||
private var searchPool: ExecutorCoroutineDispatcher? = null |
||||
private var mSearchId = 0L |
||||
private var searchPage = 1 |
||||
private var searchKey: String = "" |
||||
private var tasks = CompositeCoroutine() |
||||
private var bookSourceList = arrayListOf<BookSource>() |
||||
private var searchBooks = arrayListOf<SearchBook>() |
||||
|
||||
@Volatile |
||||
private var searchIndex = -1 |
||||
|
||||
private fun initSearchPool() { |
||||
searchPool?.close() |
||||
searchPool = Executors.newFixedThreadPool(min(threadCount, 8)).asCoroutineDispatcher() |
||||
} |
||||
|
||||
fun search(searchId: Long, key: String) { |
||||
callBack.onSearchStart() |
||||
if (searchId != mSearchId) { |
||||
if (key.isEmpty()) { |
||||
callBack.onSearchCancel() |
||||
return |
||||
} else { |
||||
this.searchKey = key |
||||
} |
||||
if (mSearchId != 0L) { |
||||
close() |
||||
} |
||||
initSearchPool() |
||||
mSearchId = searchId |
||||
searchPage = 1 |
||||
val searchGroup = appCtx.getPrefString("searchGroup") ?: "" |
||||
bookSourceList.clear() |
||||
if (searchGroup.isBlank()) { |
||||
bookSourceList.addAll(appDb.bookSourceDao.allEnabled) |
||||
} else { |
||||
bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup)) |
||||
} |
||||
} else { |
||||
searchPage++ |
||||
} |
||||
searchIndex = -1 |
||||
for (i in 0 until threadCount) { |
||||
search(searchId) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
private fun search(searchId: Long) { |
||||
if (searchIndex >= bookSourceList.lastIndex) { |
||||
return |
||||
} |
||||
searchIndex++ |
||||
val source = bookSourceList[searchIndex] |
||||
val task = WebBook.searchBook( |
||||
scope, |
||||
source, |
||||
searchKey, |
||||
searchPage, |
||||
context = searchPool!! |
||||
).timeout(30000L) |
||||
.onSuccess(searchPool) { |
||||
onSuccess(searchId, it) |
||||
} |
||||
.onFinally(searchPool) { |
||||
onFinally(searchId) |
||||
} |
||||
tasks.add(task) |
||||
} |
||||
|
||||
@Synchronized |
||||
private fun onSuccess(searchId: Long, searchBooks: ArrayList<SearchBook>) { |
||||
if (searchId == mSearchId) { |
||||
val precision = appCtx.getPrefBoolean(PreferKey.precisionSearch) |
||||
appDb.searchBookDao.insert(*searchBooks.toTypedArray()) |
||||
mergeItems(scope, searchBooks, precision) |
||||
callBack.onSearchSuccess(searchBooks) |
||||
} |
||||
} |
||||
|
||||
@Synchronized |
||||
private fun onFinally(searchId: Long) { |
||||
if (searchIndex < bookSourceList.lastIndex) { |
||||
search(searchId) |
||||
} else { |
||||
searchIndex++ |
||||
} |
||||
if (searchIndex >= bookSourceList.lastIndex |
||||
+ min(bookSourceList.size, threadCount) |
||||
) { |
||||
callBack.onSearchFinish() |
||||
} |
||||
} |
||||
|
||||
private fun mergeItems(scope: CoroutineScope, newDataS: List<SearchBook>, precision: Boolean) { |
||||
if (newDataS.isNotEmpty()) { |
||||
val copyData = ArrayList(searchBooks) |
||||
val equalData = arrayListOf<SearchBook>() |
||||
val containsData = arrayListOf<SearchBook>() |
||||
val otherData = arrayListOf<SearchBook>() |
||||
copyData.forEach { |
||||
if (!scope.isActive) return |
||||
if (it.name == searchKey || it.author == searchKey) { |
||||
equalData.add(it) |
||||
} else if (it.name.contains(searchKey) || it.author.contains(searchKey)) { |
||||
containsData.add(it) |
||||
} else { |
||||
otherData.add(it) |
||||
} |
||||
} |
||||
newDataS.forEach { nBook -> |
||||
if (!scope.isActive) return |
||||
if (nBook.name == searchKey || nBook.author == searchKey) { |
||||
var hasSame = false |
||||
equalData.forEach { pBook -> |
||||
if (!scope.isActive) return |
||||
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||
pBook.addOrigin(nBook.origin) |
||||
hasSame = true |
||||
} |
||||
} |
||||
if (!hasSame) { |
||||
equalData.add(nBook) |
||||
} |
||||
} else if (nBook.name.contains(searchKey) || nBook.author.contains(searchKey)) { |
||||
var hasSame = false |
||||
containsData.forEach { pBook -> |
||||
if (!scope.isActive) return |
||||
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||
pBook.addOrigin(nBook.origin) |
||||
hasSame = true |
||||
} |
||||
} |
||||
if (!hasSame) { |
||||
containsData.add(nBook) |
||||
} |
||||
} else if (!precision) { |
||||
var hasSame = false |
||||
otherData.forEach { pBook -> |
||||
if (!scope.isActive) return |
||||
if (pBook.name == nBook.name && pBook.author == nBook.author) { |
||||
pBook.addOrigin(nBook.origin) |
||||
hasSame = true |
||||
} |
||||
} |
||||
if (!hasSame) { |
||||
otherData.add(nBook) |
||||
} |
||||
} |
||||
} |
||||
if (!scope.isActive) return |
||||
equalData.sortByDescending { it.origins.size } |
||||
equalData.addAll(containsData.sortedByDescending { it.origins.size }) |
||||
if (!precision) { |
||||
equalData.addAll(otherData) |
||||
} |
||||
searchBooks = equalData |
||||
} |
||||
} |
||||
|
||||
fun cancelSearch() { |
||||
close() |
||||
callBack.onSearchCancel() |
||||
} |
||||
|
||||
fun close() { |
||||
tasks.clear() |
||||
searchPool?.close() |
||||
searchPool = null |
||||
mSearchId = 0L |
||||
} |
||||
|
||||
interface CallBack { |
||||
fun onSearchStart() |
||||
fun onSearchSuccess(searchBooks: ArrayList<SearchBook>) |
||||
fun onSearchFinish() |
||||
fun onSearchCancel() |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue